From e9af3c83c7a9927cf3eae3614f9041d403e7d7f2 Mon Sep 17 00:00:00 2001 From: montehurd Date: Sat, 3 Oct 2026 00:11:10 -0700 Subject: [PATCH] Copy inherited Composer fields shallowly when expanding expandMinifiedVersions deep-copied every inherited field into every version (b68184c) so that rewriting one version's dist URL in place could not change the versions that inherited it. For a package with a long history that means recursively copying require, autoload and the rest hundreds of times per document, on every metadata request. dist is the only field the proxy changes after expansion, so copy just that: rewriteDistURL now gives the version its own dist map before setting the URL, and expansion shares the other inherited values. TestComposerExpandMinifiedSharedDistReferences still guards the original bug, and fails if the copy in rewriteDistURL is removed. On the symfony/console metadata from Packagist the rewritten output is byte-identical, and the rewrite drops from about 20.7 ms to 13.8 ms, with allocations down from 13.6 MB to 9 MB. --- internal/handler/composer.go | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/internal/handler/composer.go b/internal/handler/composer.go index 47378f79..15102363 100644 --- a/internal/handler/composer.go +++ b/internal/handler/composer.go @@ -177,10 +177,12 @@ func expandMinifiedVersions(versionList []any) []any { } // Merge inherited fields into a new map, then overlay current fields. - // Deep copy values to avoid shared references between versions. + // Inherited values are shared between versions, not copied: the + // only one rewritten afterwards is dist, and rewriteDistURL copies + // it before changing it. merged := make(map[string]any, len(inherited)+len(vmap)) for k, val := range inherited { - merged[k] = deepCopyValue(val) + merged[k] = val } for k, val := range vmap { if val == composerUnset { @@ -199,26 +201,6 @@ func expandMinifiedVersions(versionList []any) []any { return expanded } -// deepCopyValue returns a deep copy of JSON-like values (maps, slices, scalars). -func deepCopyValue(v any) any { - switch val := v.(type) { - case map[string]any: - m := make(map[string]any, len(val)) - for k, v := range val { - m[k] = deepCopyValue(v) - } - return m - case []any: - s := make([]any, len(val)) - for i, v := range val { - s[i] = deepCopyValue(v) - } - return s - default: - return v - } -} - // filterAndRewriteVersions applies cooldown filtering and rewrites dist URLs // for a single package's version list. func (h *ComposerHandler) filterAndRewriteVersions(packageName string, versionList []any) []any { @@ -298,7 +280,14 @@ func (h *ComposerHandler) rewriteDistURL(vmap map[string]any, packageName, versi if len(parts) == vendorPackageParts { newURL := fmt.Sprintf("%s/composer/files/%s/%s/%s/%s", h.proxyURL, parts[0], parts[1], version, filename) - dist["url"] = newURL + // Expanded versions can share one inherited dist map, so give this + // version its own before changing its URL. + own := make(map[string]any, len(dist)) + for k, v := range dist { + own[k] = v + } + own["url"] = newURL + vmap["dist"] = own } }