diff --git a/internal/handler/npm.go b/internal/handler/npm.go index ee9b59c..78aa5e4 100644 --- a/internal/handler/npm.go +++ b/internal/handler/npm.go @@ -13,7 +13,7 @@ import ( const ( npmUpstream = "https://registry.npmjs.org" - npmAbbreviatedCT = "application/vnd.npm.install-v1+json" + npmAcceptDefault = "application/vnd.npm.install-v1+json;q=1.0, application/json;q=0.8" scopedParts = 2 // scope + name in scoped packages ) @@ -71,9 +71,12 @@ func (h *NPMHandler) handlePackageMetadata(w http.ResponseWriter, r *http.Reques upstreamURL := fmt.Sprintf("%s/%s", h.upstreamURL, url.PathEscape(packageName)) - // Use abbreviated metadata when cooldown is disabled — it's much smaller - // (e.g. drizzle-orm: 4MB vs 92MB) but lacks the time map needed for cooldown. - accept := npmAbbreviatedCT + // Prefer the smaller abbreviated packument format but include application/json + // as a fallback so upstreams that reject the abbreviated type (e.g. JFrog + // Artifactory, which returns 406) can still respond with full metadata. + // When cooldown is enabled we must use full metadata exclusively because the + // abbreviated format omits the "time" map required for version age filtering. + accept := npmAcceptDefault if h.proxy.Cooldown != nil && h.proxy.Cooldown.Enabled() { accept = contentTypeJSON } diff --git a/internal/handler/npm_test.go b/internal/handler/npm_test.go index e0257dd..fd6f080 100644 --- a/internal/handler/npm_test.go +++ b/internal/handler/npm_test.go @@ -396,7 +396,7 @@ func TestNPMHandlerUsesAbbreviatedMetadata(t *testing.T) { })) defer upstream.Close() - t.Run("no cooldown uses abbreviated metadata", func(t *testing.T) { + t.Run("no cooldown uses combined accept header", func(t *testing.T) { h := &NPMHandler{ proxy: testProxy(), upstreamURL: upstream.URL, @@ -407,12 +407,12 @@ func TestNPMHandlerUsesAbbreviatedMetadata(t *testing.T) { w := httptest.NewRecorder() h.handlePackageMetadata(w, req) - if gotAccept != npmAbbreviatedCT { - t.Errorf("Accept = %q, want abbreviated metadata header", gotAccept) + if gotAccept != npmAcceptDefault { + t.Errorf("Accept = %q, want %q", gotAccept, npmAcceptDefault) } }) - t.Run("cooldown enabled uses full metadata", func(t *testing.T) { + t.Run("cooldown enabled uses full metadata only", func(t *testing.T) { proxy := testProxy() proxy.Cooldown = &cooldown.Config{Default: "3d"} @@ -426,8 +426,8 @@ func TestNPMHandlerUsesAbbreviatedMetadata(t *testing.T) { w := httptest.NewRecorder() h.handlePackageMetadata(w, req) - if gotAccept == npmAbbreviatedCT { - t.Error("cooldown enabled should use full metadata, not abbreviated") + if gotAccept != contentTypeJSON { + t.Errorf("Accept = %q, want %q (cooldown requires full metadata)", gotAccept, contentTypeJSON) } }) }