From e2a683c7a6c96a79ddaef112a8eb357388573c93 Mon Sep 17 00:00:00 2001 From: Andrew Nesbitt Date: Fri, 13 Mar 2026 07:44:11 +0000 Subject: [PATCH] Route handler metadata requests through Proxy.HTTPClient instead of http.DefaultClient All handler metadata and proxy requests were using http.DefaultClient directly, bypassing any timeout or transport configuration. Added an HTTPClient field to the Proxy struct with a 30-second default timeout, and updated every handler to use it for upstream HTTP requests. --- internal/handler/cargo.go | 2 +- internal/handler/cargo_test.go | 3 ++- internal/handler/composer.go | 6 +++--- internal/handler/conan.go | 2 +- internal/handler/conda.go | 2 +- internal/handler/container.go | 10 +++++----- internal/handler/cran.go | 2 +- internal/handler/debian.go | 4 ++-- internal/handler/gem.go | 2 +- internal/handler/go.go | 2 +- internal/handler/handler.go | 16 ++++++++++------ internal/handler/hex.go | 2 +- internal/handler/maven.go | 2 +- internal/handler/npm.go | 2 +- internal/handler/npm_test.go | 3 ++- internal/handler/nuget.go | 4 ++-- internal/handler/pub.go | 2 +- internal/handler/pypi.go | 8 ++++---- internal/handler/rpm.go | 4 ++-- 19 files changed, 42 insertions(+), 36 deletions(-) diff --git a/internal/handler/cargo.go b/internal/handler/cargo.go index 98b3069..8aab5f0 100644 --- a/internal/handler/cargo.go +++ b/internal/handler/cargo.go @@ -90,7 +90,7 @@ func (h *CargoHandler) handleIndex(w http.ResponseWriter, r *http.Request) { return } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("failed to fetch upstream index", "error", err) http.Error(w, "failed to fetch from upstream", http.StatusBadGateway) diff --git a/internal/handler/cargo_test.go b/internal/handler/cargo_test.go index a2146f3..3fe0307 100644 --- a/internal/handler/cargo_test.go +++ b/internal/handler/cargo_test.go @@ -10,7 +10,8 @@ import ( func cargoTestProxy() *Proxy { return &Proxy{ - Logger: slog.Default(), + Logger: slog.Default(), + HTTPClient: http.DefaultClient, } } diff --git a/internal/handler/composer.go b/internal/handler/composer.go index d7bbc5d..2753e4a 100644 --- a/internal/handler/composer.go +++ b/internal/handler/composer.go @@ -94,7 +94,7 @@ func (h *ComposerHandler) handlePackageMetadata(w http.ResponseWriter, r *http.R return } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) @@ -221,7 +221,7 @@ func (h *ComposerHandler) handleDownload(w http.ResponseWriter, r *http.Request) return } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("failed to fetch metadata", "error", err) http.Error(w, "failed to fetch metadata", http.StatusBadGateway) @@ -302,7 +302,7 @@ func (h *ComposerHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) return } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) diff --git a/internal/handler/conan.go b/internal/handler/conan.go index 0a7dd8e..b91f7c3 100644 --- a/internal/handler/conan.go +++ b/internal/handler/conan.go @@ -167,7 +167,7 @@ func (h *ConanHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) { req.Header.Set("Authorization", auth) } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) diff --git a/internal/handler/conda.go b/internal/handler/conda.go index bf2c0e8..303ec0d 100644 --- a/internal/handler/conda.go +++ b/internal/handler/conda.go @@ -136,7 +136,7 @@ func (h *CondaHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) { req.Header.Set("Accept-Encoding", ae) } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) diff --git a/internal/handler/container.go b/internal/handler/container.go index 349d857..0669b12 100644 --- a/internal/handler/container.go +++ b/internal/handler/container.go @@ -172,7 +172,7 @@ func (h *ContainerHandler) handleManifest(w http.ResponseWriter, r *http.Request }, ", ")) } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("failed to fetch manifest", "error", err) h.containerError(w, http.StatusBadGateway, "INTERNAL_ERROR", "failed to fetch from upstream") @@ -224,7 +224,7 @@ func (h *ContainerHandler) handleTagsList(w http.ResponseWriter, r *http.Request req.Header.Set("Authorization", "Bearer "+token) - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.containerError(w, http.StatusBadGateway, "INTERNAL_ERROR", "failed to fetch from upstream") return @@ -248,7 +248,7 @@ func (h *ContainerHandler) getAuthToken(_ interface{ Done() <-chan struct{} }, r return "", err } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { return "", err } @@ -285,7 +285,7 @@ func (h *ContainerHandler) proxyBlobHead(w http.ResponseWriter, r *http.Request, req.Header.Set("Authorization", "Bearer "+token) - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.containerError(w, http.StatusBadGateway, "INTERNAL_ERROR", "failed to fetch from upstream") return @@ -313,7 +313,7 @@ func (h *ContainerHandler) proxyBlobWithAuth(w http.ResponseWriter, r *http.Requ req.Header.Set("Authorization", "Bearer "+token) - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.containerError(w, http.StatusBadGateway, "INTERNAL_ERROR", "failed to fetch from upstream") return diff --git a/internal/handler/cran.go b/internal/handler/cran.go index e10887f..4702473 100644 --- a/internal/handler/cran.go +++ b/internal/handler/cran.go @@ -167,7 +167,7 @@ func (h *CRANHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) { req.Header.Set("Accept-Encoding", ae) } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) diff --git a/internal/handler/debian.go b/internal/handler/debian.go index 1fc7c36..bada1af 100644 --- a/internal/handler/debian.go +++ b/internal/handler/debian.go @@ -110,7 +110,7 @@ func (h *DebianHandler) handleMetadata(w http.ResponseWriter, r *http.Request, p } } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("failed to fetch upstream metadata", "error", err) http.Error(w, "failed to fetch from upstream", http.StatusBadGateway) @@ -139,7 +139,7 @@ func (h *DebianHandler) proxyFile(w http.ResponseWriter, r *http.Request, path s return } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { http.Error(w, "failed to fetch from upstream", http.StatusBadGateway) return diff --git a/internal/handler/gem.go b/internal/handler/gem.go index bd47d6f..997e956 100644 --- a/internal/handler/gem.go +++ b/internal/handler/gem.go @@ -120,7 +120,7 @@ func (h *GemHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) { } } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) diff --git a/internal/handler/go.go b/internal/handler/go.go index 9b43e8f..d64ded3 100644 --- a/internal/handler/go.go +++ b/internal/handler/go.go @@ -118,7 +118,7 @@ func (h *GoHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) { return } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 3bd49f8..9205008 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -43,12 +43,13 @@ func ReadMetadata(r io.Reader) ([]byte, error) { // Proxy provides shared functionality for protocol handlers. type Proxy struct { - DB *database.DB - Storage storage.Storage - Fetcher fetch.FetcherInterface - Resolver *fetch.Resolver - Logger *slog.Logger - Cooldown *cooldown.Config + DB *database.DB + Storage storage.Storage + Fetcher fetch.FetcherInterface + Resolver *fetch.Resolver + Logger *slog.Logger + Cooldown *cooldown.Config + HTTPClient *http.Client } // NewProxy creates a new Proxy with the given dependencies. @@ -62,6 +63,9 @@ func NewProxy(db *database.DB, store storage.Storage, fetcher fetch.FetcherInter Fetcher: fetcher, Resolver: resolver, Logger: logger, + HTTPClient: &http.Client{ + Timeout: 30 * time.Second, + }, } } diff --git a/internal/handler/hex.go b/internal/handler/hex.go index 7a34795..2e972f0 100644 --- a/internal/handler/hex.go +++ b/internal/handler/hex.go @@ -104,7 +104,7 @@ func (h *HexHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) { req.Header.Set("Accept", accept) } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) diff --git a/internal/handler/maven.go b/internal/handler/maven.go index 5c8f949..2c7214f 100644 --- a/internal/handler/maven.go +++ b/internal/handler/maven.go @@ -146,7 +146,7 @@ func (h *MavenHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) { return } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) diff --git a/internal/handler/npm.go b/internal/handler/npm.go index fc23927..99a2bb9 100644 --- a/internal/handler/npm.go +++ b/internal/handler/npm.go @@ -74,7 +74,7 @@ func (h *NPMHandler) handlePackageMetadata(w http.ResponseWriter, r *http.Reques } req.Header.Set("Accept", "application/json") - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("failed to fetch upstream metadata", "error", err) JSONError(w, http.StatusBadGateway, "failed to fetch from upstream") diff --git a/internal/handler/npm_test.go b/internal/handler/npm_test.go index 5012f53..90616d7 100644 --- a/internal/handler/npm_test.go +++ b/internal/handler/npm_test.go @@ -13,7 +13,8 @@ import ( func testProxy() *Proxy { return &Proxy{ - Logger: slog.Default(), + Logger: slog.Default(), + HTTPClient: http.DefaultClient, } } diff --git a/internal/handler/nuget.go b/internal/handler/nuget.go index fafdebb..39d56cf 100644 --- a/internal/handler/nuget.go +++ b/internal/handler/nuget.go @@ -63,7 +63,7 @@ func (h *NuGetHandler) handleServiceIndex(w http.ResponseWriter, r *http.Request return } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) @@ -219,7 +219,7 @@ func (h *NuGetHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) { req.Header.Set("Accept-Encoding", ae) } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) diff --git a/internal/handler/pub.go b/internal/handler/pub.go index efb4211..53c3a4f 100644 --- a/internal/handler/pub.go +++ b/internal/handler/pub.go @@ -95,7 +95,7 @@ func (h *PubHandler) handlePackageMetadata(w http.ResponseWriter, r *http.Reques } req.Header.Set("Accept", "application/json") - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) diff --git a/internal/handler/pypi.go b/internal/handler/pypi.go index a4deb7d..4a4232c 100644 --- a/internal/handler/pypi.go +++ b/internal/handler/pypi.go @@ -78,7 +78,7 @@ func (h *PyPIHandler) handleSimplePackage(w http.ResponseWriter, r *http.Request } req.Header.Set("Accept", "text/html") - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) @@ -121,7 +121,7 @@ func (h *PyPIHandler) fetchFilteredVersions(r *http.Request, name string) map[st } req.Header.Set("Accept", "application/json") - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { return nil } @@ -245,7 +245,7 @@ func (h *PyPIHandler) proxyAndRewriteJSON(w http.ResponseWriter, r *http.Request } req.Header.Set("Accept", "application/json") - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) @@ -505,7 +505,7 @@ func (h *PyPIHandler) proxySimple(w http.ResponseWriter, r *http.Request, path s } req.Header.Set("Accept", "text/html") - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("upstream request failed", "error", err) http.Error(w, "upstream request failed", http.StatusBadGateway) diff --git a/internal/handler/rpm.go b/internal/handler/rpm.go index 12ce9dc..de4295a 100644 --- a/internal/handler/rpm.go +++ b/internal/handler/rpm.go @@ -112,7 +112,7 @@ func (h *RPMHandler) handleMetadata(w http.ResponseWriter, r *http.Request, path } } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { h.proxy.Logger.Error("failed to fetch upstream metadata", "error", err) http.Error(w, "failed to fetch from upstream", http.StatusBadGateway) @@ -141,7 +141,7 @@ func (h *RPMHandler) proxyFile(w http.ResponseWriter, r *http.Request, path stri return } - resp, err := http.DefaultClient.Do(req) + resp, err := h.proxy.HTTPClient.Do(req) if err != nil { http.Error(w, "failed to fetch from upstream", http.StatusBadGateway) return