From 71ade836bb2469afe6ca83b1abeaa1486b46e362 Mon Sep 17 00:00:00 2001 From: darkweak <darkweak@protonmail.com> Date: Mon, 2 Sep 2024 16:09:39 +0200 Subject: [PATCH] fix(chore): surrogate use Content-Location instead of generated key --- .github/workflows/generate_release.sh | 2 +- .github/workflows/release_plugins.yml | 11 +++++ pkg/middleware/middleware.go | 6 +-- pkg/surrogate/providers/akamai.go | 4 +- pkg/surrogate/providers/cloudflare.go | 4 +- pkg/surrogate/providers/common.go | 9 ++-- pkg/surrogate/providers/common_test.go | 12 ++--- pkg/surrogate/providers/types.go | 2 +- plugins/caddy/httpcache_test.go | 48 +++++++++++++++++++ .../examples/internal/handler/routes.go | 2 + plugins/souin/storages/go.mod | 2 +- .../souin/pkg/surrogate/providers/akamai.go | 4 +- .../pkg/surrogate/providers/cloudflare.go | 4 +- .../souin/pkg/surrogate/providers/types.go | 2 +- 14 files changed, 88 insertions(+), 24 deletions(-) diff --git a/.github/workflows/generate_release.sh b/.github/workflows/generate_release.sh index 31c972a05..4e1df0880 100755 --- a/.github/workflows/generate_release.sh +++ b/.github/workflows/generate_release.sh @@ -1,6 +1,6 @@ #!/bin/bash -plugins=("beego" "caddy" "chi" "dotweb" "echo" "fiber" "gin" "go-zero" "goa" "goyave" "hertz" "kratos" "roadrunner" "skipper" "souin" "traefik" "tyk" "webgo") +plugins=("beego" "caddy" "chi" "dotweb" "echo" "fiber" "gin" "go-zero" "goa" "goyave" "hertz" "kratos" "roadrunner" "skipper" "souin" "souin/storages" "traefik" "tyk" "webgo") IFS= read -r -d '' tpl <<EOF name: Tag submodules on release diff --git a/.github/workflows/release_plugins.yml b/.github/workflows/release_plugins.yml index d8e61ca95..d8be37200 100644 --- a/.github/workflows/release_plugins.yml +++ b/.github/workflows/release_plugins.yml @@ -177,6 +177,17 @@ jobs: ref: 'refs/tags/plugins/souin/${{ github.ref_name }}', sha: context.sha }) + - + name: Create Souin/storages tag + uses: actions/github-script@v7 + with: + script: | + github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: 'refs/tags/plugins/souin/storages/${{ github.ref_name }}', + sha: context.sha + }) - name: Create Traefik tag uses: actions/github-script@v7 diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index ad84376cc..8e02dbe76 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -353,9 +353,9 @@ func (s *SouinBaseHandler) Store( wg.Wait() if len(fails) < s.storersLen { - go func(rs http.Response, key string, basekey string) { - _ = s.SurrogateKeyStorer.Store(&rs, key, uri, basekey) - }(res, variedKey, cachedKey) + go func(rs http.Response, key string) { + _ = s.SurrogateKeyStorer.Store(&rs, key, uri) + }(res, variedKey) status += "; stored" } diff --git a/pkg/surrogate/providers/akamai.go b/pkg/surrogate/providers/akamai.go index d33455b79..1efef7298 100644 --- a/pkg/surrogate/providers/akamai.go +++ b/pkg/surrogate/providers/akamai.go @@ -39,12 +39,12 @@ func (*AkamaiSurrogateStorage) getHeaderSeparator() string { } // Store stores the response tags located in the first non empty supported header -func (a *AkamaiSurrogateStorage) Store(response *http.Response, cacheKey, uri, basekey string) error { +func (a *AkamaiSurrogateStorage) Store(response *http.Response, cacheKey, uri string) error { defer func() { response.Header.Del(surrogateKey) response.Header.Del(surrogateControl) }() - e := a.baseStorage.Store(response, cacheKey, uri, basekey) + e := a.baseStorage.Store(response, cacheKey, uri) response.Header.Set(edgeCacheTag, response.Header.Get(surrogateKey)) return e diff --git a/pkg/surrogate/providers/cloudflare.go b/pkg/surrogate/providers/cloudflare.go index d733347b8..83a50f13f 100644 --- a/pkg/surrogate/providers/cloudflare.go +++ b/pkg/surrogate/providers/cloudflare.go @@ -38,12 +38,12 @@ func (*CloudflareSurrogateStorage) getHeaderSeparator() string { } // Store stores the response tags located in the first non empty supported header -func (c *CloudflareSurrogateStorage) Store(response *http.Response, cacheKey, uri, basekey string) error { +func (c *CloudflareSurrogateStorage) Store(response *http.Response, cacheKey, uri string) error { defer func() { response.Header.Del(surrogateKey) response.Header.Del(surrogateControl) }() - e := c.baseStorage.Store(response, cacheKey, uri, basekey) + e := c.baseStorage.Store(response, cacheKey, uri) response.Header.Set(cacheTag, strings.Join(c.ParseHeaders(response.Header.Get(surrogateKey)), c.getHeaderSeparator())) return e diff --git a/pkg/surrogate/providers/common.go b/pkg/surrogate/providers/common.go index 6b1e6b780..b5f2dcd5f 100644 --- a/pkg/surrogate/providers/common.go +++ b/pkg/surrogate/providers/common.go @@ -210,7 +210,7 @@ func (s *baseStorage) purgeTag(tag string) []string { } // Store will take the lead to store the cache key for each provided Surrogate-key -func (s *baseStorage) Store(response *http.Response, cacheKey, uri, basekey string) error { +func (s *baseStorage) Store(response *http.Response, cacheKey, uri string) error { h := response.Header cacheKey = url.QueryEscape(cacheKey) @@ -238,8 +238,11 @@ func (s *baseStorage) Store(response *http.Response, cacheKey, uri, basekey stri } } - urlRegexp = regexp.MustCompile("(^|" + regexp.QuoteMeta(souinStorageSeparator) + ")" + regexp.QuoteMeta(basekey) + "(" + regexp.QuoteMeta(souinStorageSeparator) + "|$)") - s.storeTag(uri, basekey, urlRegexp) + if h.Get("Content-Location") != "" { + location := h.Get("Content-Location") + urlRegexp = regexp.MustCompile("(^|" + regexp.QuoteMeta(souinStorageSeparator) + ")" + regexp.QuoteMeta(location) + "(" + regexp.QuoteMeta(souinStorageSeparator) + "|$)") + s.storeTag(uri, location, urlRegexp) + } s.storeTag(uri, cacheKey, urlRegexp) return nil diff --git a/pkg/surrogate/providers/common_test.go b/pkg/surrogate/providers/common_test.go index 06fb90d66..e1870234f 100644 --- a/pkg/surrogate/providers/common_test.go +++ b/pkg/surrogate/providers/common_test.go @@ -106,7 +106,7 @@ func TestBaseStorage_Store(t *testing.T) { bs := mockCommonProvider() - e := bs.Store(&res, "((((invalid_key_but_escaped", "", "") + e := bs.Store(&res, "((((invalid_key_but_escaped", "") if e != nil { t.Error("It shouldn't throw an error with a valid key.") } @@ -116,7 +116,7 @@ func TestBaseStorage_Store(t *testing.T) { _ = bs.Storage.Set("test5", []byte("first,second,fifth"), storageToInfiniteTTLMap[bs.Storage.Name()]) _ = bs.Storage.Set("testInvalid", []byte("invalid"), storageToInfiniteTTLMap[bs.Storage.Name()]) - if e = bs.Store(&res, "stored", "", ""); e != nil { + if e = bs.Store(&res, "stored", ""); e != nil { t.Error("It shouldn't throw an error with a valid key.") } @@ -133,10 +133,10 @@ func TestBaseStorage_Store(t *testing.T) { } res.Header.Set(surrogateKey, "something") - _ = bs.Store(&res, "/something", "", "") - _ = bs.Store(&res, "/something", "", "") + _ = bs.Store(&res, "/something", "") + _ = bs.Store(&res, "/something", "") res.Header.Set(surrogateKey, "something") - _ = bs.Store(&res, "/some", "", "") + _ = bs.Store(&res, "/some", "") _ = len(bs.Storage.MapKeys(surrogatePrefix)) // if storageSize != 6 { @@ -161,7 +161,7 @@ func TestBaseStorage_Store_Load(t *testing.T) { wg.Add(1) go func(r http.Response, iteration int, group *sync.WaitGroup) { defer wg.Done() - _ = bs.Store(&r, fmt.Sprintf("my_dynamic_cache_key_%d", iteration), "", "") + _ = bs.Store(&r, fmt.Sprintf("my_dynamic_cache_key_%d", iteration), "") }(res, i, &wg) } diff --git a/pkg/surrogate/providers/types.go b/pkg/surrogate/providers/types.go index 999fe90ff..9f012ddb2 100644 --- a/pkg/surrogate/providers/types.go +++ b/pkg/surrogate/providers/types.go @@ -16,7 +16,7 @@ type SurrogateInterface interface { Purge(http.Header) (cacheKeys []string, surrogateKeys []string) Invalidate(method string, h http.Header) purgeTag(string) []string - Store(*http.Response, string, string, string) error + Store(*http.Response, string, string) error storeTag(string, string, *regexp.Regexp) ParseHeaders(string) []string List() map[string]string diff --git a/plugins/caddy/httpcache_test.go b/plugins/caddy/httpcache_test.go index d7258c014..c5eed1f3e 100644 --- a/plugins/caddy/httpcache_test.go +++ b/plugins/caddy/httpcache_test.go @@ -1038,3 +1038,51 @@ func TestExpires(t *testing.T) { cacheChecker(caddyTester, "/expires-with-max-age", "Hello, expires-with-max-age!", 59) cacheChecker(caddyTester, "/expires-with-s-maxage", "Hello, expires-with-s-maxage!", 4) } + +func TestComplexQuery(t *testing.T) { + caddyTester := caddytest.NewTester(t) + caddyTester.InitServer(` + { + admin localhost:2999 + http_port 9080 + https_port 9443 + cache { + ttl 10s + } + } + localhost:9080 { + route /complex-query { + cache + respond "Hello, {query}!" + } + }`, "caddyfile") + + cacheChecker := func(tester *caddytest.Tester, query string, expectedDuration int) { + body := fmt.Sprintf("Hello, %s!", query) + resp1, _ := tester.AssertGetResponse("http://localhost:9080/complex-query?"+query, 200, body) + if resp1.Header.Get("Age") != "" { + t.Errorf("unexpected Age header %v", resp1.Header.Get("Age")) + } + + if resp1.Header.Get("Cache-Status") != "Souin; fwd=uri-miss; stored; key=GET-http-localhost:9080-/complex-query?"+query { + t.Errorf("unexpected first Cache-Status header %v", resp1.Header.Get("Cache-Status")) + } + + resp1, _ = tester.AssertGetResponse("http://localhost:9080/complex-query?"+query, 200, body) + + if resp1.Header.Get("Age") != "1" { + t.Errorf("unexpected Age header %v", resp1.Header.Get("Age")) + } + + if resp1.Header.Get("Cache-Status") != fmt.Sprintf("Souin; hit; ttl=%d; key=GET-http-localhost:9080-/complex-query?%s; detail=DEFAULT", expectedDuration, query) { + t.Errorf( + "unexpected second Cache-Status header %v, expected %s", + resp1.Header.Get("Cache-Status"), + fmt.Sprintf("Souin; hit; ttl=%d; key=GET-http-localhost:9080-/complex-query?%s; detail=DEFAULT", expectedDuration, query), + ) + } + } + + cacheChecker(caddyTester, "fields[]=id&pagination=true", 9) + cacheChecker(caddyTester, "fields[]=id&pagination=false", 9) +} diff --git a/plugins/go-zero/examples/internal/handler/routes.go b/plugins/go-zero/examples/internal/handler/routes.go index 8456f428e..bcdd43890 100644 --- a/plugins/go-zero/examples/internal/handler/routes.go +++ b/plugins/go-zero/examples/internal/handler/routes.go @@ -1,4 +1,6 @@ // Code generated by goctl. DO NOT EDIT. +// goctl 1.7.1 + package handler import ( diff --git a/plugins/souin/storages/go.mod b/plugins/souin/storages/go.mod index 50f32e638..d26ed4037 100644 --- a/plugins/souin/storages/go.mod +++ b/plugins/souin/storages/go.mod @@ -5,7 +5,7 @@ go 1.22.1 replace github.com/darkweak/souin => ../../.. require ( - github.com/darkweak/souin v1.6.49 + github.com/darkweak/souin v1.6.50 github.com/darkweak/storages/badger v0.0.8 github.com/darkweak/storages/core v0.0.8 github.com/darkweak/storages/etcd v0.0.8 diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/akamai.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/akamai.go index d33455b79..1efef7298 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/akamai.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/akamai.go @@ -39,12 +39,12 @@ func (*AkamaiSurrogateStorage) getHeaderSeparator() string { } // Store stores the response tags located in the first non empty supported header -func (a *AkamaiSurrogateStorage) Store(response *http.Response, cacheKey, uri, basekey string) error { +func (a *AkamaiSurrogateStorage) Store(response *http.Response, cacheKey, uri string) error { defer func() { response.Header.Del(surrogateKey) response.Header.Del(surrogateControl) }() - e := a.baseStorage.Store(response, cacheKey, uri, basekey) + e := a.baseStorage.Store(response, cacheKey, uri) response.Header.Set(edgeCacheTag, response.Header.Get(surrogateKey)) return e diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/cloudflare.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/cloudflare.go index d733347b8..83a50f13f 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/cloudflare.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/cloudflare.go @@ -38,12 +38,12 @@ func (*CloudflareSurrogateStorage) getHeaderSeparator() string { } // Store stores the response tags located in the first non empty supported header -func (c *CloudflareSurrogateStorage) Store(response *http.Response, cacheKey, uri, basekey string) error { +func (c *CloudflareSurrogateStorage) Store(response *http.Response, cacheKey, uri string) error { defer func() { response.Header.Del(surrogateKey) response.Header.Del(surrogateControl) }() - e := c.baseStorage.Store(response, cacheKey, uri, basekey) + e := c.baseStorage.Store(response, cacheKey, uri) response.Header.Set(cacheTag, strings.Join(c.ParseHeaders(response.Header.Get(surrogateKey)), c.getHeaderSeparator())) return e diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/types.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/types.go index 999fe90ff..9f012ddb2 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/types.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/types.go @@ -16,7 +16,7 @@ type SurrogateInterface interface { Purge(http.Header) (cacheKeys []string, surrogateKeys []string) Invalidate(method string, h http.Header) purgeTag(string) []string - Store(*http.Response, string, string, string) error + Store(*http.Response, string, string) error storeTag(string, string, *regexp.Regexp) ParseHeaders(string) []string List() map[string]string