This commit is contained in:
@@ -64,11 +64,11 @@ func isAptIndexPath(p string) bool {
|
|||||||
if isByHashPath(clean) {
|
if isByHashPath(clean) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(clean, "/dists/") {
|
|
||||||
if strings.HasSuffix(clean, "/release") || strings.HasSuffix(clean, "/inrelease") || strings.HasSuffix(clean, "/release.gpg") {
|
if strings.Contains(clean, "/dists/") {
|
||||||
return true
|
if strings.HasSuffix(clean, "/release") ||
|
||||||
}
|
strings.HasSuffix(clean, "/inrelease") ||
|
||||||
if strings.Contains(clean, "/packages") {
|
strings.HasSuffix(clean, "/release.gpg") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ func isAptImmutablePath(p string) bool {
|
|||||||
if isByHashPath(clean) {
|
if isByHashPath(clean) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(clean, "/pool/") {
|
if strings.Contains(clean, "/pool/") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -88,9 +88,10 @@ func isAptImmutablePath(p string) bool {
|
|||||||
|
|
||||||
func isByHashPath(p string) bool {
|
func isByHashPath(p string) bool {
|
||||||
clean := canonicalPath(p)
|
clean := canonicalPath(p)
|
||||||
if !strings.HasPrefix(clean, "/dists/") {
|
if strings.Contains(clean, "/dists/") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Contains(clean, "/by-hash/")
|
return strings.Contains(clean, "/by-hash/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ func TestCachePolicyIndexesRevalidate(t *testing.T) {
|
|||||||
if !current.AllowCache || !current.AllowStore || !current.RequireRevalidate {
|
if !current.AllowCache || !current.AllowStore || !current.RequireRevalidate {
|
||||||
t.Fatalf("expected packages index to revalidate")
|
t.Fatalf("expected packages index to revalidate")
|
||||||
}
|
}
|
||||||
|
current = cachePolicy(nil, "/dists/bookworm/main/Contents-amd64.gz", hooks.CachePolicy{})
|
||||||
|
if !current.AllowCache || !current.AllowStore || !current.RequireRevalidate {
|
||||||
|
t.Fatalf("expected contents index to revalidate")
|
||||||
|
}
|
||||||
|
current = cachePolicy(nil, "/debian-security/dists/trixie/Contents-amd64.gz", hooks.CachePolicy{})
|
||||||
|
if !current.AllowCache || !current.AllowStore || !current.RequireRevalidate {
|
||||||
|
t.Fatalf("expected prefixed contents index to revalidate")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCachePolicyImmutable(t *testing.T) {
|
func TestCachePolicyImmutable(t *testing.T) {
|
||||||
@@ -26,6 +34,7 @@ func TestCachePolicyImmutable(t *testing.T) {
|
|||||||
{name: "by-hash nested", path: "/dists/bookworm/main/binary-amd64/by-hash/SHA256/def"},
|
{name: "by-hash nested", path: "/dists/bookworm/main/binary-amd64/by-hash/SHA256/def"},
|
||||||
{name: "pool package", path: "/pool/main/h/hello.deb"},
|
{name: "pool package", path: "/pool/main/h/hello.deb"},
|
||||||
{name: "pool canonicalized", path: " /PoOl/main/../main/h/hello_1.0_amd64.DeB "},
|
{name: "pool canonicalized", path: " /PoOl/main/../main/h/hello_1.0_amd64.DeB "},
|
||||||
|
{name: "mirror prefix pool", path: "/debian/pool/main/h/hello.deb"},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ func TestAptUpdateCachesIndexes(t *testing.T) {
|
|||||||
|
|
||||||
releasePath := "/dists/bookworm/Release"
|
releasePath := "/dists/bookworm/Release"
|
||||||
packagesPath := "/dists/bookworm/main/binary-amd64/Packages.gz"
|
packagesPath := "/dists/bookworm/main/binary-amd64/Packages.gz"
|
||||||
|
contentsPath := "/dists/bookworm/main/Contents-amd64.gz"
|
||||||
|
|
||||||
resp := doRequest(releasePath)
|
resp := doRequest(releasePath)
|
||||||
if resp.StatusCode != fiber.StatusOK {
|
if resp.StatusCode != fiber.StatusOK {
|
||||||
@@ -114,6 +115,24 @@ func TestAptUpdateCachesIndexes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
pkgResp2.Body.Close()
|
pkgResp2.Body.Close()
|
||||||
|
|
||||||
|
contentsResp := doRequest(contentsPath)
|
||||||
|
if contentsResp.StatusCode != fiber.StatusOK {
|
||||||
|
t.Fatalf("expected 200 for contents, got %d", contentsResp.StatusCode)
|
||||||
|
}
|
||||||
|
if contentsResp.Header.Get("X-Any-Hub-Cache-Hit") != "false" {
|
||||||
|
t.Fatalf("expected cache miss for contents")
|
||||||
|
}
|
||||||
|
contentsResp.Body.Close()
|
||||||
|
|
||||||
|
contentsResp2 := doRequest(contentsPath)
|
||||||
|
if contentsResp2.StatusCode != fiber.StatusOK {
|
||||||
|
t.Fatalf("expected 200 for cached contents, got %d", contentsResp2.StatusCode)
|
||||||
|
}
|
||||||
|
if contentsResp2.Header.Get("X-Any-Hub-Cache-Hit") != "true" {
|
||||||
|
t.Fatalf("expected cache hit for contents")
|
||||||
|
}
|
||||||
|
contentsResp2.Body.Close()
|
||||||
|
|
||||||
if stub.ReleaseGets() != 1 {
|
if stub.ReleaseGets() != 1 {
|
||||||
t.Fatalf("expected single release GET, got %d", stub.ReleaseGets())
|
t.Fatalf("expected single release GET, got %d", stub.ReleaseGets())
|
||||||
}
|
}
|
||||||
@@ -126,6 +145,12 @@ func TestAptUpdateCachesIndexes(t *testing.T) {
|
|||||||
if stub.PackagesHeads() != 1 {
|
if stub.PackagesHeads() != 1 {
|
||||||
t.Fatalf("expected single packages HEAD revalidate, got %d", stub.PackagesHeads())
|
t.Fatalf("expected single packages HEAD revalidate, got %d", stub.PackagesHeads())
|
||||||
}
|
}
|
||||||
|
if stub.ContentsGets() != 1 {
|
||||||
|
t.Fatalf("expected single contents GET, got %d", stub.ContentsGets())
|
||||||
|
}
|
||||||
|
if stub.ContentsHeads() != 1 {
|
||||||
|
t.Fatalf("expected single contents HEAD revalidate, got %d", stub.ContentsHeads())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type aptStub struct {
|
type aptStub struct {
|
||||||
@@ -135,14 +160,19 @@ type aptStub struct {
|
|||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
releaseBody string
|
releaseBody string
|
||||||
packagesBody string
|
packagesBody string
|
||||||
|
contentsBody string
|
||||||
releaseETag string
|
releaseETag string
|
||||||
packagesETag string
|
packagesETag string
|
||||||
|
contentsETag string
|
||||||
releaseGets int
|
releaseGets int
|
||||||
releaseHeads int
|
releaseHeads int
|
||||||
packagesGets int
|
packagesGets int
|
||||||
packagesHeads int
|
packagesHeads int
|
||||||
|
contentsGets int
|
||||||
|
contentsHeads int
|
||||||
releasePath string
|
releasePath string
|
||||||
packagesPath string
|
packagesPath string
|
||||||
|
contentsPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAptStub(t *testing.T) *aptStub {
|
func newAptStub(t *testing.T) *aptStub {
|
||||||
@@ -150,15 +180,19 @@ func newAptStub(t *testing.T) *aptStub {
|
|||||||
stub := &aptStub{
|
stub := &aptStub{
|
||||||
releaseBody: "Release-body",
|
releaseBody: "Release-body",
|
||||||
packagesBody: "Packages-body",
|
packagesBody: "Packages-body",
|
||||||
|
contentsBody: "Contents-body",
|
||||||
releaseETag: "r1",
|
releaseETag: "r1",
|
||||||
packagesETag: "p1",
|
packagesETag: "p1",
|
||||||
|
contentsETag: "c1",
|
||||||
releasePath: "/dists/bookworm/Release",
|
releasePath: "/dists/bookworm/Release",
|
||||||
packagesPath: "/dists/bookworm/main/binary-amd64/Packages.gz",
|
packagesPath: "/dists/bookworm/main/binary-amd64/Packages.gz",
|
||||||
|
contentsPath: "/dists/bookworm/main/Contents-amd64.gz",
|
||||||
}
|
}
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc(stub.releasePath, stub.handleRelease)
|
mux.HandleFunc(stub.releasePath, stub.handleRelease)
|
||||||
mux.HandleFunc(stub.packagesPath, stub.handlePackages)
|
mux.HandleFunc(stub.packagesPath, stub.handlePackages)
|
||||||
|
mux.HandleFunc(stub.contentsPath, stub.handleContents)
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -212,6 +246,25 @@ func (s *aptStub) handlePackages(w http.ResponseWriter, r *http.Request) {
|
|||||||
_, _ = w.Write([]byte(s.packagesBody))
|
_, _ = w.Write([]byte(s.packagesBody))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *aptStub) handleContents(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
if r.Method == http.MethodHead {
|
||||||
|
s.contentsHeads++
|
||||||
|
if matchETag(r, s.contentsETag) {
|
||||||
|
w.WriteHeader(http.StatusNotModified)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeHeaders(w, s.contentsETag)
|
||||||
|
w.Header().Set("Content-Type", "application/gzip")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.contentsGets++
|
||||||
|
writeHeaders(w, s.contentsETag)
|
||||||
|
w.Header().Set("Content-Type", "application/gzip")
|
||||||
|
_, _ = w.Write([]byte(s.contentsBody))
|
||||||
|
}
|
||||||
|
|
||||||
func matchETag(r *http.Request, etag string) bool {
|
func matchETag(r *http.Request, etag string) bool {
|
||||||
for _, candidate := range r.Header.Values("If-None-Match") {
|
for _, candidate := range r.Header.Values("If-None-Match") {
|
||||||
c := strings.Trim(candidate, "\"")
|
c := strings.Trim(candidate, "\"")
|
||||||
@@ -251,6 +304,18 @@ func (s *aptStub) PackagesHeads() int {
|
|||||||
return s.packagesHeads
|
return s.packagesHeads
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *aptStub) ContentsGets() int {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.contentsGets
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *aptStub) ContentsHeads() int {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.contentsHeads
|
||||||
|
}
|
||||||
|
|
||||||
func (s *aptStub) Close() {
|
func (s *aptStub) Close() {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user