Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-11-09 15:49:16 -05:00
pkg-proxy/internal/handler/cache_key_test.go
Simon Chrzanowski d2c6587996 Hash path-based metadata cache keys
Conan, CRAN, Conda, RPM, the main Debian archive and the default Maven
repository keyed cached metadata by the request path with '/' replaced
by '_'. Names and versions may contain '_', so foo/bar_1.0 and
foo_bar/1.0 shared an entry, and with cache_metadata one reference's
recipe files were served for the other. Conan also appended the query
with '_', so a path without a query could collide with a shorter path
plus one.

Hash the upstream URL, the path and, for Conan, the query instead, as
the APK, Arch, generic and Helm handlers already do. Each part is
length-prefixed so no two inputs share a key.
2026-10-24 12:55:00 +02:00

74 lines
2.6 KiB
Go

package handler
import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
// TestConanMetadataKeysKeepReferencesApart checks that Conan references whose
// paths only differ in where '/' and '_' fall, or in a query, are cached
// apart. Names and versions may contain '_'.
func TestConanMetadataKeysKeepReferencesApart(t *testing.T) {
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = io.WriteString(w, r.URL.Path+"?"+r.URL.RawQuery)
}))
defer upstream.Close()
proxy, _, _, _ := setupTestProxy(t)
proxy.CacheMetadata = true
proxy.MetadataTTL = time.Hour
proxy.HTTPClient = upstream.Client()
srv := httptest.NewServer(NewConanHandlerWithUpstream(proxy, testProxyURL, upstream.URL).Routes())
defer srv.Close()
for _, pair := range [][2]string{
{"/v2/conans/foo/bar_1.0/_/_/latest", "/v2/conans/foo_bar/1.0/_/_/latest"},
{"/v2/conans/foo/bar_1.0/_/_/revisions/r1/files/conanfile.py", "/v2/conans/foo_bar/1.0/_/_/revisions/r1/files/conanfile.py"},
{"/v2/conans/search?q=zlib", "/v2/conans/search_q=zlib"},
} {
for _, target := range pair {
resp, err := http.Get(srv.URL + target)
if err != nil {
t.Fatal(err)
}
body, _ := io.ReadAll(resp.Body)
_ = resp.Body.Close()
want := target
if !strings.Contains(want, "?") {
want += "?"
}
if string(body) != want {
t.Errorf("GET %s served %q, want %q", target, body, want)
}
}
}
}
// TestMetadataCacheKeysUnambiguous checks that the path-based metadata cache
// keys of the other ecosystems keep paths apart that differ only in where
// '/' and '_' fall.
func TestMetadataCacheKeysUnambiguous(t *testing.T) {
const a, b = "x/foo/bar_1.0/file", "x/foo_bar/1.0/file"
maven := mavenSource{upstreamURL: "https://repo.test"}
debian := NewDebianHandler(nil, "http://proxy.test", "https://deb.test", nil)
rpm := NewRPMHandlerWithUpstream(nil, "http://proxy.test", "https://rpm.test")
cran := NewCRANHandlerWithUpstream(nil, "http://proxy.test", "https://cran.test")
conda := NewCondaHandlerWithUpstream(nil, "http://proxy.test", "https://conda.test")
for name, key := range map[string]func(string) string{
"maven": maven.metadataCacheKey,
"debian": func(p string) string { return debian.metadataCacheKeyFor("", debian.upstreamURL, p) },
"rpm": func(p string) string { return rpm.metadataCacheKey(p) },
"cran": func(p string) string { return cran.metadataCacheKey("/" + p) },
"conda": func(p string) string { return conda.metadataCacheKey("/" + p) },
} {
if key(a) == key(b) {
t.Errorf("%s: %s and %s share metadata cache key %q", name, a, b, key(a))
}
}
}