Skip to content

Commit

Permalink
Change parameter type and fix bug in HTTPURLs option (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
MicahParks authored Jan 14, 2025
1 parent 3583310 commit 437c67f
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 23 deletions.
17 changes: 4 additions & 13 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"log/slog"
"net/url"
"time"

"golang.org/x/time/rate"
Expand Down Expand Up @@ -51,13 +50,10 @@ func NewHTTPClient(options HTTPClientOptions) (Storage, error) {
}
for u, store := range options.HTTPURLs {
if store == nil {
parsed, err := url.ParseRequestURI(u)
var err error
options.HTTPURLs[u], err = NewStorageFromHTTP(u, HTTPClientStorageOptions{})
if err != nil {
return nil, fmt.Errorf("failed to parse given URL %q: %w", u, errors.Join(err, ErrNewClient))
}
options.HTTPURLs[u], err = NewStorageFromHTTP(nil, HTTPClientStorageOptions{})
if err != nil {
return nil, fmt.Errorf("failed to create HTTP client storage for %q: %w", parsed.String(), errors.Join(err, ErrNewClient))
return nil, fmt.Errorf("failed to create HTTP client storage for %q: %w", u, errors.Join(err, ErrNewClient))
}
}
}
Expand Down Expand Up @@ -94,11 +90,6 @@ func NewDefaultHTTPClientCtx(ctx context.Context, urls []string) (Storage, error
RefreshUnknownKID: rate.NewLimiter(rate.Every(5*time.Minute), 1),
}
for _, u := range urls {
parsed, err := url.ParseRequestURI(u)
if err != nil {
return nil, fmt.Errorf("failed to parse given URL %q: %w", u, errors.Join(err, ErrNewClient))
}
u = parsed.String()
refreshErrorHandler := func(ctx context.Context, err error) {
slog.Default().ErrorContext(ctx, "Failed to refresh HTTP JWK Set from remote HTTP resource.",
"error", err,
Expand All @@ -111,7 +102,7 @@ func NewDefaultHTTPClientCtx(ctx context.Context, urls []string) (Storage, error
RefreshErrorHandler: refreshErrorHandler,
RefreshInterval: time.Hour,
}
c, err := NewStorageFromHTTP(parsed, options)
c, err := NewStorageFromHTTP(u, options)
if err != nil {
return nil, fmt.Errorf("failed to create HTTP client storage for %q: %w", u, errors.Join(err, ErrNewClient))
}
Expand Down
67 changes: 60 additions & 7 deletions http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"context"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"sync"
"testing"
Expand Down Expand Up @@ -181,18 +180,14 @@ func TestClientCacheReplacement(t *testing.T) {
defer rawJWKSMux.RUnlock()
_, _ = w.Write(rawJWKS)
}))

u, err := url.ParseRequestURI(server.URL)
if err != nil {
t.Fatalf("Failed to parse the URL.\nError: %s", err)
}
defer server.Close()

refreshInterval := 50 * time.Millisecond
httpOptions := HTTPClientStorageOptions{
Ctx: ctx,
RefreshInterval: refreshInterval,
}
clientStore, err := NewStorageFromHTTP(u, httpOptions)
clientStore, err := NewStorageFromHTTP(server.URL, httpOptions)
if err != nil {
t.Fatalf("Failed to create a new HTTP client.\nError: %s", err)
}
Expand Down Expand Up @@ -258,6 +253,64 @@ func TestClientCacheReplacement(t *testing.T) {
}
}

func TestClientHTTPURLs(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

kid := "my-key-id"
secret := []byte("my-hmac-secret")
serverStore := NewMemoryStorage()
marshalOptions := JWKMarshalOptions{
Private: true,
}
metadata := JWKMetadataOptions{
KID: kid,
}
options := JWKOptions{
Marshal: marshalOptions,
Metadata: metadata,
}
jwk, err := NewJWKFromKey(secret, options)
if err != nil {
t.Fatalf("Failed to create a JWK from the given HMAC secret.\nError: %s", err)
}
err = serverStore.KeyWrite(ctx, jwk)
if err != nil {
t.Fatalf("Failed to write the given JWK to the store.\nError: %s", err)
}
rawJWKS, err := serverStore.JSON(ctx)
if err != nil {
t.Fatalf("Failed to get the JSON.\nError: %s", err)
}

rawJWKSMux := sync.RWMutex{}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rawJWKSMux.RLock()
defer rawJWKSMux.RUnlock()
_, _ = w.Write(rawJWKS)
}))
defer server.Close()

clientOptions := HTTPClientOptions{
HTTPURLs: map[string]Storage{server.URL: nil},
}
store, err := NewHTTPClient(clientOptions)
if err != nil {
t.Fatalf("Failed to create a new HTTP client.\nError: %s", err)
}

jwks, err := store.KeyReadAll(ctx)
if err != nil {
t.Fatalf("Failed to read the JWK.\nError: %s", err)
}
if len(jwks) != 1 {
t.Fatalf("Expected to read 1 JWK, but got %d.", len(jwks))
}
if !bytes.Equal(jwks[0].Key().([]byte), secret) {
t.Fatalf("The key read from the HTTP client did not match the original key.")
}
}

func TestClientError(t *testing.T) {
_, err := NewHTTPClient(HTTPClientOptions{})
if err == nil {
Expand Down
10 changes: 7 additions & 3 deletions storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ type httpStorage struct {
// the RefreshInterval option is not set, the remote HTTP resource will be requested and processed before returning. If
// the RefreshInterval option is set, a background goroutine will be launched to refresh the remote HTTP resource and
// not block the return of this function.
func NewStorageFromHTTP(u *url.URL, options HTTPClientStorageOptions) (Storage, error) {
func NewStorageFromHTTP(remoteJWKSetURL string, options HTTPClientStorageOptions) (Storage, error) {
if options.Client == nil {
options.Client = http.DefaultClient
}
Expand All @@ -227,9 +227,13 @@ func NewStorageFromHTTP(u *url.URL, options HTTPClientStorageOptions) (Storage,
options.HTTPMethod = http.MethodGet
}
store := NewMemoryStorage()
_, err := url.ParseRequestURI(remoteJWKSetURL)
if err != nil {
return nil, fmt.Errorf("failed to parse given URL %q: %w", remoteJWKSetURL, err)
}

refresh := func(ctx context.Context) error {
req, err := http.NewRequestWithContext(ctx, options.HTTPMethod, u.String(), nil)
req, err := http.NewRequestWithContext(ctx, options.HTTPMethod, remoteJWKSetURL, nil)
if err != nil {
return fmt.Errorf("failed to create HTTP request for JWK Set refresh: %w", err)
}
Expand Down Expand Up @@ -291,7 +295,7 @@ func NewStorageFromHTTP(u *url.URL, options HTTPClientStorageOptions) (Storage,

ctx, cancel := context.WithTimeout(options.Ctx, options.HTTPTimeout)
defer cancel()
err := refresh(ctx)
err = refresh(ctx)
cancel()
if err != nil {
if options.NoErrorReturnFirstHTTPReq {
Expand Down

0 comments on commit 437c67f

Please sign in to comment.