]> git.feebdaed.xyz Git - 0xmirror/kubernetes.git/commitdiff
move httpcache to third_party/forked
authorDavanum Srinivas <davanum@gmail.com>
Tue, 2 Dec 2025 02:58:55 +0000 (21:58 -0500)
committerDavanum Srinivas <davanum@gmail.com>
Thu, 18 Dec 2025 13:18:57 +0000 (08:18 -0500)
Signed-off-by: Davanum Srinivas <davanum@gmail.com>
36 files changed:
LICENSES/vendor/github.com/gregjones/httpcache/LICENSE [deleted file]
go.mod
go.sum
hack/unwanted-dependencies.json
staging/src/k8s.io/apiextensions-apiserver/go.sum
staging/src/k8s.io/apiserver/go.sum
staging/src/k8s.io/cli-runtime/go.mod
staging/src/k8s.io/cli-runtime/go.sum
staging/src/k8s.io/client-go/discovery/cached/disk/round_tripper.go
staging/src/k8s.io/client-go/go.mod
staging/src/k8s.io/client-go/go.sum
staging/src/k8s.io/client-go/third_party/forked/httpcache/LICENSE [new file with mode: 0644]
staging/src/k8s.io/client-go/third_party/forked/httpcache/httpcache.go [new file with mode: 0644]
staging/src/k8s.io/cloud-provider/go.sum
staging/src/k8s.io/component-base/go.sum
staging/src/k8s.io/component-helpers/go.sum
staging/src/k8s.io/controller-manager/go.sum
staging/src/k8s.io/cri-client/go.sum
staging/src/k8s.io/dynamic-resource-allocation/go.sum
staging/src/k8s.io/endpointslice/go.sum
staging/src/k8s.io/kube-aggregator/go.sum
staging/src/k8s.io/kube-scheduler/go.sum
staging/src/k8s.io/kubectl/go.mod
staging/src/k8s.io/kubectl/go.sum
staging/src/k8s.io/kubelet/go.sum
staging/src/k8s.io/metrics/go.sum
staging/src/k8s.io/pod-security-admission/go.sum
staging/src/k8s.io/sample-apiserver/go.sum
staging/src/k8s.io/sample-cli-plugin/go.mod
staging/src/k8s.io/sample-cli-plugin/go.sum
staging/src/k8s.io/sample-controller/go.sum
vendor/github.com/gregjones/httpcache/.travis.yml [deleted file]
vendor/github.com/gregjones/httpcache/LICENSE.txt [deleted file]
vendor/github.com/gregjones/httpcache/README.md [deleted file]
vendor/github.com/gregjones/httpcache/httpcache.go [deleted file]
vendor/modules.txt

diff --git a/LICENSES/vendor/github.com/gregjones/httpcache/LICENSE b/LICENSES/vendor/github.com/gregjones/httpcache/LICENSE
deleted file mode 100644 (file)
index 36f2c40..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-= vendor/github.com/gregjones/httpcache licensed under: =
-
-Copyright © 2012 Greg Jones (greg.jones@gmail.com)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-= vendor/github.com/gregjones/httpcache/LICENSE.txt 3cfef421226b2dacde78a4871380ac24
diff --git a/go.mod b/go.mod
index 13706690065e9fe5e513b5ac43729a7bdc83a4bd..bfc89c52c7c65690a415456916924b9e44c623bc 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -163,7 +163,6 @@ require (
        github.com/google/btree v1.1.3 // indirect
        github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
        github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
-       github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
        github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
        github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 // indirect
        github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
diff --git a/go.sum b/go.sum
index f7889f0aba2f761f93693c2c0f4094c3938399ed..087fc69ce807b76c059b2c983f2b2d029975729e 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -179,8 +179,6 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
index 912cd9bbdd5a3048e5c255f693c184347a3fa795..2766ff31636f77edc357fb889efb42d1dd0078af 100644 (file)
       "github.com/google/gofuzz": [
         "github.com/json-iterator/go"
       ],
-      "github.com/gregjones/httpcache": [
-        "k8s.io/client-go"
-      ],
       "github.com/json-iterator/go": [
         "github.com/prometheus/client_golang",
         "go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful",
       "github.com/davecgh/go-spew",
       "github.com/gogo/protobuf",
       "github.com/golang/protobuf",
-      "github.com/gregjones/httpcache",
       "github.com/json-iterator/go",
       "github.com/mailru/easyjson",
       "github.com/modern-go/concurrent",
index 9dfccb40af68de77e8a6a44ae59c34ccc4cb40bd..9e58ed60057179b4751d37fc4faf7f5ea640df29 100644 (file)
@@ -95,7 +95,6 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
index 0868010b8a680c76736c3ebebd5de30364af85b8..505267b926549fb88d4389b58e3b1d43600ebe40 100644 (file)
@@ -96,7 +96,6 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
index 22a48618b2f723a24dd8ee70e77ae0f0e80b7642..4208e18014096e046be282e519f4c9b444bbdab7 100644 (file)
@@ -42,7 +42,6 @@ require (
        github.com/go-openapi/jsonreference v0.20.2 // indirect
        github.com/go-openapi/swag v0.23.0 // indirect
        github.com/google/btree v1.1.3 // indirect
-       github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
        github.com/inconshreveable/mousetrap v1.1.0 // indirect
        github.com/josharian/intern v1.0.0 // indirect
        github.com/json-iterator/go v1.1.12 // indirect
index de78293693858d7fcdb14edc2a00e773bcf9b3c8..c69e80d50e11adb2ff9ca43b82b9906e7c02a1cb 100644 (file)
@@ -45,8 +45,6 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
index f3a4b2947f3ea96b85bf2556f65307a5b9a1c648..b978a2bed909de2e4c11822979b4939ea2d22de9 100644 (file)
@@ -24,8 +24,8 @@ import (
        "os"
        "path/filepath"
 
-       "github.com/gregjones/httpcache"
        "github.com/peterbourgon/diskv"
+       "k8s.io/client-go/third_party/forked/httpcache"
        "k8s.io/klog/v2"
 )
 
@@ -66,7 +66,7 @@ func (rt *cacheRoundTripper) CancelRequest(req *http.Request) {
 
 func (rt *cacheRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt.Transport }
 
-// A sumDiskCache is a cache backend for github.com/gregjones/httpcache. It is
+// A sumDiskCache is a cache backend for httpcache. It is
 // similar to httpcache's diskcache package, but uses SHA256 sums to ensure
 // cache integrity at read time rather than fsyncing each cache entry to
 // increase the likelihood they will be persisted at write time. This avoids
index a6ff7e977985cfdf161f2ddabcace35fd5e184ed..613571ce2e6c13ca154591410162265847e999be 100644 (file)
@@ -12,7 +12,6 @@ require (
        github.com/google/go-cmp v0.7.0
        github.com/google/uuid v1.6.0
        github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
-       github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
        github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
        github.com/peterbourgon/diskv v2.0.1+incompatible
        github.com/spf13/pflag v1.0.9
index 11524f4f71234402c29ca6721af4467874c389b0..479d7eae1878255b4be20abb30887140fc1c61f9 100644 (file)
@@ -39,8 +39,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
diff --git a/staging/src/k8s.io/client-go/third_party/forked/httpcache/LICENSE b/staging/src/k8s.io/client-go/third_party/forked/httpcache/LICENSE
new file mode 100644 (file)
index 0000000..81316be
--- /dev/null
@@ -0,0 +1,7 @@
+Copyright © 2012 Greg Jones (greg.jones@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/staging/src/k8s.io/client-go/third_party/forked/httpcache/httpcache.go b/staging/src/k8s.io/client-go/third_party/forked/httpcache/httpcache.go
new file mode 100644 (file)
index 0000000..b92230e
--- /dev/null
@@ -0,0 +1,554 @@
+// This package is copied from github.com/gregjones/httpcache
+// with the following changes:
+// - ioutil.NopCloser replaced with io.NopCloser (deprecated)
+// - ioutil.ReadAll replaced with io.ReadAll (deprecated)
+
+// Package httpcache provides a http.RoundTripper implementation that works as a
+// mostly RFC-compliant cache for http responses.
+//
+// It is only suitable for use as a 'private' cache (i.e. for a web-browser or an API-client
+// and not for a shared proxy).
+package httpcache
+
+import (
+       "bufio"
+       "bytes"
+       "errors"
+       "io"
+       "net/http"
+       "net/http/httputil"
+       "strings"
+       "sync"
+       "time"
+)
+
+const (
+       stale = iota
+       fresh
+       transparent
+       // XFromCache is the header added to responses that are returned from the cache
+       XFromCache = "X-From-Cache"
+)
+
+// A Cache interface is used by the Transport to store and retrieve responses.
+type Cache interface {
+       // Get returns the []byte representation of a cached response and a bool
+       // set to true if the value isn't empty
+       Get(key string) (responseBytes []byte, ok bool)
+       // Set stores the []byte representation of a response against a key
+       Set(key string, responseBytes []byte)
+       // Delete removes the value associated with the key
+       Delete(key string)
+}
+
+// cacheKey returns the cache key for req.
+func cacheKey(req *http.Request) string {
+       if req.Method == http.MethodGet {
+               return req.URL.String()
+       } else {
+               return req.Method + " " + req.URL.String()
+       }
+}
+
+// CachedResponse returns the cached http.Response for req if present, and nil
+// otherwise.
+func CachedResponse(c Cache, req *http.Request) (resp *http.Response, err error) {
+       cachedVal, ok := c.Get(cacheKey(req))
+       if !ok {
+               return
+       }
+
+       b := bytes.NewBuffer(cachedVal)
+       return http.ReadResponse(bufio.NewReader(b), req)
+}
+
+// MemoryCache is an implemtation of Cache that stores responses in an in-memory map.
+type MemoryCache struct {
+       mu    sync.RWMutex
+       items map[string][]byte
+}
+
+// Get returns the []byte representation of the response and true if present, false if not
+func (c *MemoryCache) Get(key string) (resp []byte, ok bool) {
+       c.mu.RLock()
+       resp, ok = c.items[key]
+       c.mu.RUnlock()
+       return resp, ok
+}
+
+// Set saves response resp to the cache with key
+func (c *MemoryCache) Set(key string, resp []byte) {
+       c.mu.Lock()
+       c.items[key] = resp
+       c.mu.Unlock()
+}
+
+// Delete removes key from the cache
+func (c *MemoryCache) Delete(key string) {
+       c.mu.Lock()
+       delete(c.items, key)
+       c.mu.Unlock()
+}
+
+// NewMemoryCache returns a new Cache that will store items in an in-memory map
+func NewMemoryCache() *MemoryCache {
+       c := &MemoryCache{items: map[string][]byte{}}
+       return c
+}
+
+// Transport is an implementation of http.RoundTripper that will return values from a cache
+// where possible (avoiding a network request) and will additionally add validators (etag/if-modified-since)
+// to repeated requests allowing servers to return 304 / Not Modified
+type Transport struct {
+       // The RoundTripper interface actually used to make requests
+       // If nil, http.DefaultTransport is used
+       Transport http.RoundTripper
+       Cache     Cache
+       // If true, responses returned from the cache will be given an extra header, X-From-Cache
+       MarkCachedResponses bool
+}
+
+// NewTransport returns a new Transport with the
+// provided Cache implementation and MarkCachedResponses set to true
+func NewTransport(c Cache) *Transport {
+       return &Transport{Cache: c, MarkCachedResponses: true}
+}
+
+// Client returns an *http.Client that caches responses.
+func (t *Transport) Client() *http.Client {
+       return &http.Client{Transport: t}
+}
+
+// varyMatches will return false unless all of the cached values for the headers listed in Vary
+// match the new request
+func varyMatches(cachedResp *http.Response, req *http.Request) bool {
+       for _, header := range headerAllCommaSepValues(cachedResp.Header, "vary") {
+               header = http.CanonicalHeaderKey(header)
+               if header != "" && req.Header.Get(header) != cachedResp.Header.Get("X-Varied-"+header) {
+                       return false
+               }
+       }
+       return true
+}
+
+// RoundTrip takes a Request and returns a Response
+//
+// If there is a fresh Response already in cache, then it will be returned without connecting to
+// the server.
+//
+// If there is a stale Response, then any validators it contains will be set on the new request
+// to give the server a chance to respond with NotModified. If this happens, then the cached Response
+// will be returned.
+func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
+       cacheKey := cacheKey(req)
+       cacheable := (req.Method == "GET" || req.Method == "HEAD") && req.Header.Get("range") == ""
+       var cachedResp *http.Response
+       if cacheable {
+               cachedResp, err = CachedResponse(t.Cache, req)
+       } else {
+               // Need to invalidate an existing value
+               t.Cache.Delete(cacheKey)
+       }
+
+       transport := t.Transport
+       if transport == nil {
+               transport = http.DefaultTransport
+       }
+
+       if cacheable && cachedResp != nil && err == nil {
+               if t.MarkCachedResponses {
+                       cachedResp.Header.Set(XFromCache, "1")
+               }
+
+               if varyMatches(cachedResp, req) {
+                       // Can only use cached value if the new request doesn't Vary significantly
+                       freshness := getFreshness(cachedResp.Header, req.Header)
+                       if freshness == fresh {
+                               return cachedResp, nil
+                       }
+
+                       if freshness == stale {
+                               var req2 *http.Request
+                               // Add validators if caller hasn't already done so
+                               etag := cachedResp.Header.Get("etag")
+                               if etag != "" && req.Header.Get("etag") == "" {
+                                       req2 = cloneRequest(req)
+                                       req2.Header.Set("if-none-match", etag)
+                               }
+                               lastModified := cachedResp.Header.Get("last-modified")
+                               if lastModified != "" && req.Header.Get("last-modified") == "" {
+                                       if req2 == nil {
+                                               req2 = cloneRequest(req)
+                                       }
+                                       req2.Header.Set("if-modified-since", lastModified)
+                               }
+                               if req2 != nil {
+                                       req = req2
+                               }
+                       }
+               }
+
+               resp, err = transport.RoundTrip(req)
+               if err == nil && req.Method == "GET" && resp.StatusCode == http.StatusNotModified {
+                       // Replace the 304 response with the one from cache, but update with some new headers
+                       endToEndHeaders := getEndToEndHeaders(resp.Header)
+                       for _, header := range endToEndHeaders {
+                               cachedResp.Header[header] = resp.Header[header]
+                       }
+                       resp = cachedResp
+               } else if (err != nil || (cachedResp != nil && resp.StatusCode >= 500)) &&
+                       req.Method == "GET" && canStaleOnError(cachedResp.Header, req.Header) {
+                       // In case of transport failure and stale-if-error activated, returns cached content
+                       // when available
+                       return cachedResp, nil
+               } else {
+                       if err != nil || resp.StatusCode != http.StatusOK {
+                               t.Cache.Delete(cacheKey)
+                       }
+                       if err != nil {
+                               return nil, err
+                       }
+               }
+       } else {
+               reqCacheControl := parseCacheControl(req.Header)
+               if _, ok := reqCacheControl["only-if-cached"]; ok {
+                       resp = newGatewayTimeoutResponse(req)
+               } else {
+                       resp, err = transport.RoundTrip(req)
+                       if err != nil {
+                               return nil, err
+                       }
+               }
+       }
+
+       if cacheable && canStore(parseCacheControl(req.Header), parseCacheControl(resp.Header)) {
+               for _, varyKey := range headerAllCommaSepValues(resp.Header, "vary") {
+                       varyKey = http.CanonicalHeaderKey(varyKey)
+                       fakeHeader := "X-Varied-" + varyKey
+                       reqValue := req.Header.Get(varyKey)
+                       if reqValue != "" {
+                               resp.Header.Set(fakeHeader, reqValue)
+                       }
+               }
+               switch req.Method {
+               case "GET":
+                       // Delay caching until EOF is reached.
+                       resp.Body = &cachingReadCloser{
+                               R: resp.Body,
+                               OnEOF: func(r io.Reader) {
+                                       resp := *resp
+                                       resp.Body = io.NopCloser(r)
+                                       respBytes, err := httputil.DumpResponse(&resp, true)
+                                       if err == nil {
+                                               t.Cache.Set(cacheKey, respBytes)
+                                       }
+                               },
+                       }
+               default:
+                       respBytes, err := httputil.DumpResponse(resp, true)
+                       if err == nil {
+                               t.Cache.Set(cacheKey, respBytes)
+                       }
+               }
+       } else {
+               t.Cache.Delete(cacheKey)
+       }
+       return resp, nil
+}
+
+// ErrNoDateHeader indicates that the HTTP headers contained no Date header.
+var ErrNoDateHeader = errors.New("no Date header")
+
+// Date parses and returns the value of the Date header.
+func Date(respHeaders http.Header) (date time.Time, err error) {
+       dateHeader := respHeaders.Get("date")
+       if dateHeader == "" {
+               err = ErrNoDateHeader
+               return
+       }
+
+       return time.Parse(time.RFC1123, dateHeader)
+}
+
+type realClock struct{}
+
+func (c *realClock) since(d time.Time) time.Duration {
+       return time.Since(d)
+}
+
+type timer interface {
+       since(d time.Time) time.Duration
+}
+
+var clock timer = &realClock{}
+
+// getFreshness will return one of fresh/stale/transparent based on the cache-control
+// values of the request and the response
+//
+// fresh indicates the response can be returned
+// stale indicates that the response needs validating before it is returned
+// transparent indicates the response should not be used to fulfil the request
+//
+// Because this is only a private cache, 'public' and 'private' in cache-control aren't
+// signficant. Similarly, smax-age isn't used.
+func getFreshness(respHeaders, reqHeaders http.Header) (freshness int) {
+       respCacheControl := parseCacheControl(respHeaders)
+       reqCacheControl := parseCacheControl(reqHeaders)
+       if _, ok := reqCacheControl["no-cache"]; ok {
+               return transparent
+       }
+       if _, ok := respCacheControl["no-cache"]; ok {
+               return stale
+       }
+       if _, ok := reqCacheControl["only-if-cached"]; ok {
+               return fresh
+       }
+
+       date, err := Date(respHeaders)
+       if err != nil {
+               return stale
+       }
+       currentAge := clock.since(date)
+
+       var lifetime time.Duration
+       var zeroDuration time.Duration
+
+       // If a response includes both an Expires header and a max-age directive,
+       // the max-age directive overrides the Expires header, even if the Expires header is more restrictive.
+       if maxAge, ok := respCacheControl["max-age"]; ok {
+               lifetime, err = time.ParseDuration(maxAge + "s")
+               if err != nil {
+                       lifetime = zeroDuration
+               }
+       } else {
+               expiresHeader := respHeaders.Get("Expires")
+               if expiresHeader != "" {
+                       expires, err := time.Parse(time.RFC1123, expiresHeader)
+                       if err != nil {
+                               lifetime = zeroDuration
+                       } else {
+                               lifetime = expires.Sub(date)
+                       }
+               }
+       }
+
+       if maxAge, ok := reqCacheControl["max-age"]; ok {
+               // the client is willing to accept a response whose age is no greater than the specified time in seconds
+               lifetime, err = time.ParseDuration(maxAge + "s")
+               if err != nil {
+                       lifetime = zeroDuration
+               }
+       }
+       if minfresh, ok := reqCacheControl["min-fresh"]; ok {
+               //  the client wants a response that will still be fresh for at least the specified number of seconds.
+               minfreshDuration, err := time.ParseDuration(minfresh + "s")
+               if err == nil {
+                       currentAge = time.Duration(currentAge + minfreshDuration)
+               }
+       }
+
+       if maxstale, ok := reqCacheControl["max-stale"]; ok {
+               // Indicates that the client is willing to accept a response that has exceeded its expiration time.
+               // If max-stale is assigned a value, then the client is willing to accept a response that has exceeded
+               // its expiration time by no more than the specified number of seconds.
+               // If no value is assigned to max-stale, then the client is willing to accept a stale response of any age.
+               //
+               // Responses served only because of a max-stale value are supposed to have a Warning header added to them,
+               // but that seems like a  hassle, and is it actually useful? If so, then there needs to be a different
+               // return-value available here.
+               if maxstale == "" {
+                       return fresh
+               }
+               maxstaleDuration, err := time.ParseDuration(maxstale + "s")
+               if err == nil {
+                       currentAge = time.Duration(currentAge - maxstaleDuration)
+               }
+       }
+
+       if lifetime > currentAge {
+               return fresh
+       }
+
+       return stale
+}
+
+// Returns true if either the request or the response includes the stale-if-error
+// cache control extension: https://tools.ietf.org/html/rfc5861
+func canStaleOnError(respHeaders, reqHeaders http.Header) bool {
+       respCacheControl := parseCacheControl(respHeaders)
+       reqCacheControl := parseCacheControl(reqHeaders)
+
+       var err error
+       lifetime := time.Duration(-1)
+
+       if staleMaxAge, ok := respCacheControl["stale-if-error"]; ok {
+               if staleMaxAge != "" {
+                       lifetime, err = time.ParseDuration(staleMaxAge + "s")
+                       if err != nil {
+                               return false
+                       }
+               } else {
+                       return true
+               }
+       }
+       if staleMaxAge, ok := reqCacheControl["stale-if-error"]; ok {
+               if staleMaxAge != "" {
+                       lifetime, err = time.ParseDuration(staleMaxAge + "s")
+                       if err != nil {
+                               return false
+                       }
+               } else {
+                       return true
+               }
+       }
+
+       if lifetime >= 0 {
+               date, err := Date(respHeaders)
+               if err != nil {
+                       return false
+               }
+               currentAge := clock.since(date)
+               if lifetime > currentAge {
+                       return true
+               }
+       }
+
+       return false
+}
+
+func getEndToEndHeaders(respHeaders http.Header) []string {
+       // These headers are always hop-by-hop
+       hopByHopHeaders := map[string]struct{}{
+               "Connection":          {},
+               "Keep-Alive":          {},
+               "Proxy-Authenticate":  {},
+               "Proxy-Authorization": {},
+               "Te":                  {},
+               "Trailers":            {},
+               "Transfer-Encoding":   {},
+               "Upgrade":             {},
+       }
+
+       for _, extra := range strings.Split(respHeaders.Get("connection"), ",") {
+               // any header listed in connection, if present, is also considered hop-by-hop
+               if strings.Trim(extra, " ") != "" {
+                       hopByHopHeaders[http.CanonicalHeaderKey(extra)] = struct{}{}
+               }
+       }
+       endToEndHeaders := []string{}
+       for respHeader := range respHeaders {
+               if _, ok := hopByHopHeaders[respHeader]; !ok {
+                       endToEndHeaders = append(endToEndHeaders, respHeader)
+               }
+       }
+       return endToEndHeaders
+}
+
+func canStore(reqCacheControl, respCacheControl cacheControl) (canStore bool) {
+       if _, ok := respCacheControl["no-store"]; ok {
+               return false
+       }
+       if _, ok := reqCacheControl["no-store"]; ok {
+               return false
+       }
+       return true
+}
+
+func newGatewayTimeoutResponse(req *http.Request) *http.Response {
+       var braw bytes.Buffer
+       braw.WriteString("HTTP/1.1 504 Gateway Timeout\r\n\r\n")
+       resp, err := http.ReadResponse(bufio.NewReader(&braw), req)
+       if err != nil {
+               panic(err)
+       }
+       return resp
+}
+
+// cloneRequest returns a clone of the provided *http.Request.
+// The clone is a shallow copy of the struct and its Header map.
+// (This function copyright goauth2 authors: https://code.google.com/p/goauth2)
+func cloneRequest(r *http.Request) *http.Request {
+       // shallow copy of the struct
+       r2 := new(http.Request)
+       *r2 = *r
+       // deep copy of the Header
+       r2.Header = make(http.Header)
+       for k, s := range r.Header {
+               r2.Header[k] = s
+       }
+       return r2
+}
+
+type cacheControl map[string]string
+
+func parseCacheControl(headers http.Header) cacheControl {
+       cc := cacheControl{}
+       ccHeader := headers.Get("Cache-Control")
+       for _, part := range strings.Split(ccHeader, ",") {
+               part = strings.Trim(part, " ")
+               if part == "" {
+                       continue
+               }
+               if strings.ContainsRune(part, '=') {
+                       keyval := strings.Split(part, "=")
+                       cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",")
+               } else {
+                       cc[part] = ""
+               }
+       }
+       return cc
+}
+
+// headerAllCommaSepValues returns all comma-separated values (each
+// with whitespace trimmed) for header name in headers. According to
+// Section 4.2 of the HTTP/1.1 spec
+// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2),
+// values from multiple occurrences of a header should be concatenated, if
+// the header's value is a comma-separated list.
+func headerAllCommaSepValues(headers http.Header, name string) []string {
+       var vals []string
+       for _, val := range headers[http.CanonicalHeaderKey(name)] {
+               fields := strings.Split(val, ",")
+               for i, f := range fields {
+                       fields[i] = strings.TrimSpace(f)
+               }
+               vals = append(vals, fields...)
+       }
+       return vals
+}
+
+// cachingReadCloser is a wrapper around ReadCloser R that calls OnEOF
+// handler with a full copy of the content read from R when EOF is
+// reached.
+type cachingReadCloser struct {
+       // Underlying ReadCloser.
+       R io.ReadCloser
+       // OnEOF is called with a copy of the content of R when EOF is reached.
+       OnEOF func(io.Reader)
+
+       buf bytes.Buffer // buf stores a copy of the content of R.
+}
+
+// Read reads the next len(p) bytes from R or until R is drained. The
+// return value n is the number of bytes read. If R has no data to
+// return, err is io.EOF and OnEOF is called with a full copy of what
+// has been read so far.
+func (r *cachingReadCloser) Read(p []byte) (n int, err error) {
+       n, err = r.R.Read(p)
+       r.buf.Write(p[:n])
+       if err == io.EOF {
+               r.OnEOF(bytes.NewReader(r.buf.Bytes()))
+       }
+       return n, err
+}
+
+func (r *cachingReadCloser) Close() error {
+       return r.R.Close()
+}
+
+// NewMemoryCacheTransport returns a new Transport using the in-memory cache implementation
+func NewMemoryCacheTransport() *Transport {
+       c := NewMemoryCache()
+       t := NewTransport(c)
+       return t
+}
index 2809fdd014ea1beddd229005245b76e5fc68389c..9efbc188e854dd89a3a8c544948059a9440be170 100644 (file)
@@ -92,7 +92,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs=
index 77517074e21d229e4f7ce1414d9b935320af4939..a97da7947b33d324c4b83ec122965d61870cba33 100644 (file)
@@ -68,7 +68,6 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
index aa9915719f880fc4cb0e3fb71e6445c10da5234c..6808536eefa68f1c158ee43f13c27f8513912091 100644 (file)
@@ -35,7 +35,6 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
index 0c958052894b3db8e113e8318b7cc7e697e0e204..f2e9dcf088a5230b387a6d897e3cb9426f4d2e20 100644 (file)
@@ -89,7 +89,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs=
index 49388081278e9a41e266effcf02aca73663dbdc0..524d71c5ebc30bff98ccc073fc6e169b0d4ecebe 100644 (file)
@@ -64,7 +64,6 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
index 7d64c17d991975d31ca5e997f40a94cc10bae27b..69ad716f0e1d9b6a035ce5daa16cc64f5090e56b 100644 (file)
@@ -72,7 +72,6 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
index e538860399268a3574edc05a8345ce7edc177d53..31e873187dc369cf61069c9069e2df3207461892 100644 (file)
@@ -48,7 +48,6 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
index a5b07264f46478ca5bd923684841c1ac432b2163..285f906152d39a286df70027f001f5b950de4fd4 100644 (file)
@@ -90,7 +90,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs=
index c8a848fc602c5d91a3d91cc83751fd5ddc24f704..31b49f38b12d1649c9e24d619ffa8553c827a487 100644 (file)
@@ -62,7 +62,6 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
index 663d44b1189b834ad2a6796e946795802aaaca36..981d1edd1d2d09780289f714faec0d781ba5ac69 100644 (file)
@@ -64,7 +64,6 @@ require (
        github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
        github.com/google/uuid v1.6.0 // indirect
        github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
-       github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
        github.com/inconshreveable/mousetrap v1.1.0 // indirect
        github.com/josharian/intern v1.0.0 // indirect
        github.com/json-iterator/go v1.1.12 // indirect
index 77d9d5471c5ce00fbbb72c4e484c93a8a94ce45a..f6cabafd1c08ac76af115be45ed70d5825645f97 100644 (file)
@@ -72,8 +72,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
 github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
index 30ac7354713f5ed8388e79d3c451996818e12684..e07c5b8480faba3ba53c32632ece503c3086e61a 100644 (file)
@@ -65,7 +65,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
index 6da7eb1a2a06152cd34d286a9167473949e09760..f49dcc86a1fd782229aeabfb0967630c544e80ce 100644 (file)
@@ -36,7 +36,6 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
index 0c958052894b3db8e113e8318b7cc7e697e0e204..f2e9dcf088a5230b387a6d897e3cb9426f4d2e20 100644 (file)
@@ -89,7 +89,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs=
index dfad0efc921353127b9e50891c23e2e8c6c6c6b8..da9937139ded50b4fe1ae8071ace407f089bf3ec 100644 (file)
@@ -89,7 +89,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs=
index d886f048698b1f7726806d098660417da32fb7d2..2d6dafbc1945f28f2874de1c11d75ab8f118fd47 100644 (file)
@@ -27,7 +27,6 @@ require (
        github.com/google/btree v1.1.3 // indirect
        github.com/google/gnostic-models v0.7.0 // indirect
        github.com/google/uuid v1.6.0 // indirect
-       github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
        github.com/inconshreveable/mousetrap v1.1.0 // indirect
        github.com/josharian/intern v1.0.0 // indirect
        github.com/json-iterator/go v1.1.12 // indirect
index de78293693858d7fcdb14edc2a00e773bcf9b3c8..c69e80d50e11adb2ff9ca43b82b9906e7c02a1cb 100644 (file)
@@ -45,8 +45,6 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
index ffa375696615a873bcb59dc8b0258dccb57672cb..e943f9688c8a05270ac66592187f8815ea0daf97 100644 (file)
@@ -36,7 +36,6 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
diff --git a/vendor/github.com/gregjones/httpcache/.travis.yml b/vendor/github.com/gregjones/httpcache/.travis.yml
deleted file mode 100644 (file)
index 597bc99..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-sudo: false
-language: go
-matrix:
-  allow_failures:
-    - go: master
-  fast_finish: true
-  include:
-    - go: 1.10.x
-    - go: 1.11.x
-      env: GOFMT=1
-    - go: master
-install:
-  - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
-script:
-  - go get -t -v ./...
-  - if test -n "${GOFMT}"; then gofmt -w -s . && git diff --exit-code; fi
-  - go tool vet .
-  - go test -v -race ./...
diff --git a/vendor/github.com/gregjones/httpcache/LICENSE.txt b/vendor/github.com/gregjones/httpcache/LICENSE.txt
deleted file mode 100644 (file)
index 81316be..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-Copyright © 2012 Greg Jones (greg.jones@gmail.com)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/github.com/gregjones/httpcache/README.md b/vendor/github.com/gregjones/httpcache/README.md
deleted file mode 100644 (file)
index 51e7d23..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-httpcache
-=========
-
-[![Build Status](https://travis-ci.org/gregjones/httpcache.svg?branch=master)](https://travis-ci.org/gregjones/httpcache) [![GoDoc](https://godoc.org/github.com/gregjones/httpcache?status.svg)](https://godoc.org/github.com/gregjones/httpcache)
-
-Package httpcache provides a http.RoundTripper implementation that works as a mostly [RFC 7234](https://tools.ietf.org/html/rfc7234) compliant cache for http responses.
-
-It is only suitable for use as a 'private' cache (i.e. for a web-browser or an API-client and not for a shared proxy).
-
-This project isn't actively maintained; it works for what I, and seemingly others, want to do with it, and I consider it "done". That said, if you find any issues, please open a Pull Request and I will try to review it. Any changes now that change the public API won't be considered.
-
-Cache Backends
---------------
-
-- The built-in 'memory' cache stores responses in an in-memory map.
-- [`github.com/gregjones/httpcache/diskcache`](https://github.com/gregjones/httpcache/tree/master/diskcache) provides a filesystem-backed cache using the [diskv](https://github.com/peterbourgon/diskv) library.
-- [`github.com/gregjones/httpcache/memcache`](https://github.com/gregjones/httpcache/tree/master/memcache) provides memcache implementations, for both App Engine and 'normal' memcache servers.
-- [`sourcegraph.com/sourcegraph/s3cache`](https://sourcegraph.com/github.com/sourcegraph/s3cache) uses Amazon S3 for storage.
-- [`github.com/gregjones/httpcache/leveldbcache`](https://github.com/gregjones/httpcache/tree/master/leveldbcache) provides a filesystem-backed cache using [leveldb](https://github.com/syndtr/goleveldb/leveldb).
-- [`github.com/die-net/lrucache`](https://github.com/die-net/lrucache) provides an in-memory cache that will evict least-recently used entries.
-- [`github.com/die-net/lrucache/twotier`](https://github.com/die-net/lrucache/tree/master/twotier) allows caches to be combined, for example to use lrucache above with a persistent disk-cache.
-- [`github.com/birkelund/boltdbcache`](https://github.com/birkelund/boltdbcache) provides a BoltDB implementation (based on the [bbolt](https://github.com/coreos/bbolt) fork).
-
-If you implement any other backend and wish it to be linked here, please send a PR editing this file.
-
-License
--------
-
--      [MIT License](LICENSE.txt)
diff --git a/vendor/github.com/gregjones/httpcache/httpcache.go b/vendor/github.com/gregjones/httpcache/httpcache.go
deleted file mode 100644 (file)
index b41a63d..0000000
+++ /dev/null
@@ -1,551 +0,0 @@
-// Package httpcache provides a http.RoundTripper implementation that works as a
-// mostly RFC-compliant cache for http responses.
-//
-// It is only suitable for use as a 'private' cache (i.e. for a web-browser or an API-client
-// and not for a shared proxy).
-//
-package httpcache
-
-import (
-       "bufio"
-       "bytes"
-       "errors"
-       "io"
-       "io/ioutil"
-       "net/http"
-       "net/http/httputil"
-       "strings"
-       "sync"
-       "time"
-)
-
-const (
-       stale = iota
-       fresh
-       transparent
-       // XFromCache is the header added to responses that are returned from the cache
-       XFromCache = "X-From-Cache"
-)
-
-// A Cache interface is used by the Transport to store and retrieve responses.
-type Cache interface {
-       // Get returns the []byte representation of a cached response and a bool
-       // set to true if the value isn't empty
-       Get(key string) (responseBytes []byte, ok bool)
-       // Set stores the []byte representation of a response against a key
-       Set(key string, responseBytes []byte)
-       // Delete removes the value associated with the key
-       Delete(key string)
-}
-
-// cacheKey returns the cache key for req.
-func cacheKey(req *http.Request) string {
-       if req.Method == http.MethodGet {
-               return req.URL.String()
-       } else {
-               return req.Method + " " + req.URL.String()
-       }
-}
-
-// CachedResponse returns the cached http.Response for req if present, and nil
-// otherwise.
-func CachedResponse(c Cache, req *http.Request) (resp *http.Response, err error) {
-       cachedVal, ok := c.Get(cacheKey(req))
-       if !ok {
-               return
-       }
-
-       b := bytes.NewBuffer(cachedVal)
-       return http.ReadResponse(bufio.NewReader(b), req)
-}
-
-// MemoryCache is an implemtation of Cache that stores responses in an in-memory map.
-type MemoryCache struct {
-       mu    sync.RWMutex
-       items map[string][]byte
-}
-
-// Get returns the []byte representation of the response and true if present, false if not
-func (c *MemoryCache) Get(key string) (resp []byte, ok bool) {
-       c.mu.RLock()
-       resp, ok = c.items[key]
-       c.mu.RUnlock()
-       return resp, ok
-}
-
-// Set saves response resp to the cache with key
-func (c *MemoryCache) Set(key string, resp []byte) {
-       c.mu.Lock()
-       c.items[key] = resp
-       c.mu.Unlock()
-}
-
-// Delete removes key from the cache
-func (c *MemoryCache) Delete(key string) {
-       c.mu.Lock()
-       delete(c.items, key)
-       c.mu.Unlock()
-}
-
-// NewMemoryCache returns a new Cache that will store items in an in-memory map
-func NewMemoryCache() *MemoryCache {
-       c := &MemoryCache{items: map[string][]byte{}}
-       return c
-}
-
-// Transport is an implementation of http.RoundTripper that will return values from a cache
-// where possible (avoiding a network request) and will additionally add validators (etag/if-modified-since)
-// to repeated requests allowing servers to return 304 / Not Modified
-type Transport struct {
-       // The RoundTripper interface actually used to make requests
-       // If nil, http.DefaultTransport is used
-       Transport http.RoundTripper
-       Cache     Cache
-       // If true, responses returned from the cache will be given an extra header, X-From-Cache
-       MarkCachedResponses bool
-}
-
-// NewTransport returns a new Transport with the
-// provided Cache implementation and MarkCachedResponses set to true
-func NewTransport(c Cache) *Transport {
-       return &Transport{Cache: c, MarkCachedResponses: true}
-}
-
-// Client returns an *http.Client that caches responses.
-func (t *Transport) Client() *http.Client {
-       return &http.Client{Transport: t}
-}
-
-// varyMatches will return false unless all of the cached values for the headers listed in Vary
-// match the new request
-func varyMatches(cachedResp *http.Response, req *http.Request) bool {
-       for _, header := range headerAllCommaSepValues(cachedResp.Header, "vary") {
-               header = http.CanonicalHeaderKey(header)
-               if header != "" && req.Header.Get(header) != cachedResp.Header.Get("X-Varied-"+header) {
-                       return false
-               }
-       }
-       return true
-}
-
-// RoundTrip takes a Request and returns a Response
-//
-// If there is a fresh Response already in cache, then it will be returned without connecting to
-// the server.
-//
-// If there is a stale Response, then any validators it contains will be set on the new request
-// to give the server a chance to respond with NotModified. If this happens, then the cached Response
-// will be returned.
-func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
-       cacheKey := cacheKey(req)
-       cacheable := (req.Method == "GET" || req.Method == "HEAD") && req.Header.Get("range") == ""
-       var cachedResp *http.Response
-       if cacheable {
-               cachedResp, err = CachedResponse(t.Cache, req)
-       } else {
-               // Need to invalidate an existing value
-               t.Cache.Delete(cacheKey)
-       }
-
-       transport := t.Transport
-       if transport == nil {
-               transport = http.DefaultTransport
-       }
-
-       if cacheable && cachedResp != nil && err == nil {
-               if t.MarkCachedResponses {
-                       cachedResp.Header.Set(XFromCache, "1")
-               }
-
-               if varyMatches(cachedResp, req) {
-                       // Can only use cached value if the new request doesn't Vary significantly
-                       freshness := getFreshness(cachedResp.Header, req.Header)
-                       if freshness == fresh {
-                               return cachedResp, nil
-                       }
-
-                       if freshness == stale {
-                               var req2 *http.Request
-                               // Add validators if caller hasn't already done so
-                               etag := cachedResp.Header.Get("etag")
-                               if etag != "" && req.Header.Get("etag") == "" {
-                                       req2 = cloneRequest(req)
-                                       req2.Header.Set("if-none-match", etag)
-                               }
-                               lastModified := cachedResp.Header.Get("last-modified")
-                               if lastModified != "" && req.Header.Get("last-modified") == "" {
-                                       if req2 == nil {
-                                               req2 = cloneRequest(req)
-                                       }
-                                       req2.Header.Set("if-modified-since", lastModified)
-                               }
-                               if req2 != nil {
-                                       req = req2
-                               }
-                       }
-               }
-
-               resp, err = transport.RoundTrip(req)
-               if err == nil && req.Method == "GET" && resp.StatusCode == http.StatusNotModified {
-                       // Replace the 304 response with the one from cache, but update with some new headers
-                       endToEndHeaders := getEndToEndHeaders(resp.Header)
-                       for _, header := range endToEndHeaders {
-                               cachedResp.Header[header] = resp.Header[header]
-                       }
-                       resp = cachedResp
-               } else if (err != nil || (cachedResp != nil && resp.StatusCode >= 500)) &&
-                       req.Method == "GET" && canStaleOnError(cachedResp.Header, req.Header) {
-                       // In case of transport failure and stale-if-error activated, returns cached content
-                       // when available
-                       return cachedResp, nil
-               } else {
-                       if err != nil || resp.StatusCode != http.StatusOK {
-                               t.Cache.Delete(cacheKey)
-                       }
-                       if err != nil {
-                               return nil, err
-                       }
-               }
-       } else {
-               reqCacheControl := parseCacheControl(req.Header)
-               if _, ok := reqCacheControl["only-if-cached"]; ok {
-                       resp = newGatewayTimeoutResponse(req)
-               } else {
-                       resp, err = transport.RoundTrip(req)
-                       if err != nil {
-                               return nil, err
-                       }
-               }
-       }
-
-       if cacheable && canStore(parseCacheControl(req.Header), parseCacheControl(resp.Header)) {
-               for _, varyKey := range headerAllCommaSepValues(resp.Header, "vary") {
-                       varyKey = http.CanonicalHeaderKey(varyKey)
-                       fakeHeader := "X-Varied-" + varyKey
-                       reqValue := req.Header.Get(varyKey)
-                       if reqValue != "" {
-                               resp.Header.Set(fakeHeader, reqValue)
-                       }
-               }
-               switch req.Method {
-               case "GET":
-                       // Delay caching until EOF is reached.
-                       resp.Body = &cachingReadCloser{
-                               R: resp.Body,
-                               OnEOF: func(r io.Reader) {
-                                       resp := *resp
-                                       resp.Body = ioutil.NopCloser(r)
-                                       respBytes, err := httputil.DumpResponse(&resp, true)
-                                       if err == nil {
-                                               t.Cache.Set(cacheKey, respBytes)
-                                       }
-                               },
-                       }
-               default:
-                       respBytes, err := httputil.DumpResponse(resp, true)
-                       if err == nil {
-                               t.Cache.Set(cacheKey, respBytes)
-                       }
-               }
-       } else {
-               t.Cache.Delete(cacheKey)
-       }
-       return resp, nil
-}
-
-// ErrNoDateHeader indicates that the HTTP headers contained no Date header.
-var ErrNoDateHeader = errors.New("no Date header")
-
-// Date parses and returns the value of the Date header.
-func Date(respHeaders http.Header) (date time.Time, err error) {
-       dateHeader := respHeaders.Get("date")
-       if dateHeader == "" {
-               err = ErrNoDateHeader
-               return
-       }
-
-       return time.Parse(time.RFC1123, dateHeader)
-}
-
-type realClock struct{}
-
-func (c *realClock) since(d time.Time) time.Duration {
-       return time.Since(d)
-}
-
-type timer interface {
-       since(d time.Time) time.Duration
-}
-
-var clock timer = &realClock{}
-
-// getFreshness will return one of fresh/stale/transparent based on the cache-control
-// values of the request and the response
-//
-// fresh indicates the response can be returned
-// stale indicates that the response needs validating before it is returned
-// transparent indicates the response should not be used to fulfil the request
-//
-// Because this is only a private cache, 'public' and 'private' in cache-control aren't
-// signficant. Similarly, smax-age isn't used.
-func getFreshness(respHeaders, reqHeaders http.Header) (freshness int) {
-       respCacheControl := parseCacheControl(respHeaders)
-       reqCacheControl := parseCacheControl(reqHeaders)
-       if _, ok := reqCacheControl["no-cache"]; ok {
-               return transparent
-       }
-       if _, ok := respCacheControl["no-cache"]; ok {
-               return stale
-       }
-       if _, ok := reqCacheControl["only-if-cached"]; ok {
-               return fresh
-       }
-
-       date, err := Date(respHeaders)
-       if err != nil {
-               return stale
-       }
-       currentAge := clock.since(date)
-
-       var lifetime time.Duration
-       var zeroDuration time.Duration
-
-       // If a response includes both an Expires header and a max-age directive,
-       // the max-age directive overrides the Expires header, even if the Expires header is more restrictive.
-       if maxAge, ok := respCacheControl["max-age"]; ok {
-               lifetime, err = time.ParseDuration(maxAge + "s")
-               if err != nil {
-                       lifetime = zeroDuration
-               }
-       } else {
-               expiresHeader := respHeaders.Get("Expires")
-               if expiresHeader != "" {
-                       expires, err := time.Parse(time.RFC1123, expiresHeader)
-                       if err != nil {
-                               lifetime = zeroDuration
-                       } else {
-                               lifetime = expires.Sub(date)
-                       }
-               }
-       }
-
-       if maxAge, ok := reqCacheControl["max-age"]; ok {
-               // the client is willing to accept a response whose age is no greater than the specified time in seconds
-               lifetime, err = time.ParseDuration(maxAge + "s")
-               if err != nil {
-                       lifetime = zeroDuration
-               }
-       }
-       if minfresh, ok := reqCacheControl["min-fresh"]; ok {
-               //  the client wants a response that will still be fresh for at least the specified number of seconds.
-               minfreshDuration, err := time.ParseDuration(minfresh + "s")
-               if err == nil {
-                       currentAge = time.Duration(currentAge + minfreshDuration)
-               }
-       }
-
-       if maxstale, ok := reqCacheControl["max-stale"]; ok {
-               // Indicates that the client is willing to accept a response that has exceeded its expiration time.
-               // If max-stale is assigned a value, then the client is willing to accept a response that has exceeded
-               // its expiration time by no more than the specified number of seconds.
-               // If no value is assigned to max-stale, then the client is willing to accept a stale response of any age.
-               //
-               // Responses served only because of a max-stale value are supposed to have a Warning header added to them,
-               // but that seems like a  hassle, and is it actually useful? If so, then there needs to be a different
-               // return-value available here.
-               if maxstale == "" {
-                       return fresh
-               }
-               maxstaleDuration, err := time.ParseDuration(maxstale + "s")
-               if err == nil {
-                       currentAge = time.Duration(currentAge - maxstaleDuration)
-               }
-       }
-
-       if lifetime > currentAge {
-               return fresh
-       }
-
-       return stale
-}
-
-// Returns true if either the request or the response includes the stale-if-error
-// cache control extension: https://tools.ietf.org/html/rfc5861
-func canStaleOnError(respHeaders, reqHeaders http.Header) bool {
-       respCacheControl := parseCacheControl(respHeaders)
-       reqCacheControl := parseCacheControl(reqHeaders)
-
-       var err error
-       lifetime := time.Duration(-1)
-
-       if staleMaxAge, ok := respCacheControl["stale-if-error"]; ok {
-               if staleMaxAge != "" {
-                       lifetime, err = time.ParseDuration(staleMaxAge + "s")
-                       if err != nil {
-                               return false
-                       }
-               } else {
-                       return true
-               }
-       }
-       if staleMaxAge, ok := reqCacheControl["stale-if-error"]; ok {
-               if staleMaxAge != "" {
-                       lifetime, err = time.ParseDuration(staleMaxAge + "s")
-                       if err != nil {
-                               return false
-                       }
-               } else {
-                       return true
-               }
-       }
-
-       if lifetime >= 0 {
-               date, err := Date(respHeaders)
-               if err != nil {
-                       return false
-               }
-               currentAge := clock.since(date)
-               if lifetime > currentAge {
-                       return true
-               }
-       }
-
-       return false
-}
-
-func getEndToEndHeaders(respHeaders http.Header) []string {
-       // These headers are always hop-by-hop
-       hopByHopHeaders := map[string]struct{}{
-               "Connection":          {},
-               "Keep-Alive":          {},
-               "Proxy-Authenticate":  {},
-               "Proxy-Authorization": {},
-               "Te":                  {},
-               "Trailers":            {},
-               "Transfer-Encoding":   {},
-               "Upgrade":             {},
-       }
-
-       for _, extra := range strings.Split(respHeaders.Get("connection"), ",") {
-               // any header listed in connection, if present, is also considered hop-by-hop
-               if strings.Trim(extra, " ") != "" {
-                       hopByHopHeaders[http.CanonicalHeaderKey(extra)] = struct{}{}
-               }
-       }
-       endToEndHeaders := []string{}
-       for respHeader := range respHeaders {
-               if _, ok := hopByHopHeaders[respHeader]; !ok {
-                       endToEndHeaders = append(endToEndHeaders, respHeader)
-               }
-       }
-       return endToEndHeaders
-}
-
-func canStore(reqCacheControl, respCacheControl cacheControl) (canStore bool) {
-       if _, ok := respCacheControl["no-store"]; ok {
-               return false
-       }
-       if _, ok := reqCacheControl["no-store"]; ok {
-               return false
-       }
-       return true
-}
-
-func newGatewayTimeoutResponse(req *http.Request) *http.Response {
-       var braw bytes.Buffer
-       braw.WriteString("HTTP/1.1 504 Gateway Timeout\r\n\r\n")
-       resp, err := http.ReadResponse(bufio.NewReader(&braw), req)
-       if err != nil {
-               panic(err)
-       }
-       return resp
-}
-
-// cloneRequest returns a clone of the provided *http.Request.
-// The clone is a shallow copy of the struct and its Header map.
-// (This function copyright goauth2 authors: https://code.google.com/p/goauth2)
-func cloneRequest(r *http.Request) *http.Request {
-       // shallow copy of the struct
-       r2 := new(http.Request)
-       *r2 = *r
-       // deep copy of the Header
-       r2.Header = make(http.Header)
-       for k, s := range r.Header {
-               r2.Header[k] = s
-       }
-       return r2
-}
-
-type cacheControl map[string]string
-
-func parseCacheControl(headers http.Header) cacheControl {
-       cc := cacheControl{}
-       ccHeader := headers.Get("Cache-Control")
-       for _, part := range strings.Split(ccHeader, ",") {
-               part = strings.Trim(part, " ")
-               if part == "" {
-                       continue
-               }
-               if strings.ContainsRune(part, '=') {
-                       keyval := strings.Split(part, "=")
-                       cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",")
-               } else {
-                       cc[part] = ""
-               }
-       }
-       return cc
-}
-
-// headerAllCommaSepValues returns all comma-separated values (each
-// with whitespace trimmed) for header name in headers. According to
-// Section 4.2 of the HTTP/1.1 spec
-// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2),
-// values from multiple occurrences of a header should be concatenated, if
-// the header's value is a comma-separated list.
-func headerAllCommaSepValues(headers http.Header, name string) []string {
-       var vals []string
-       for _, val := range headers[http.CanonicalHeaderKey(name)] {
-               fields := strings.Split(val, ",")
-               for i, f := range fields {
-                       fields[i] = strings.TrimSpace(f)
-               }
-               vals = append(vals, fields...)
-       }
-       return vals
-}
-
-// cachingReadCloser is a wrapper around ReadCloser R that calls OnEOF
-// handler with a full copy of the content read from R when EOF is
-// reached.
-type cachingReadCloser struct {
-       // Underlying ReadCloser.
-       R io.ReadCloser
-       // OnEOF is called with a copy of the content of R when EOF is reached.
-       OnEOF func(io.Reader)
-
-       buf bytes.Buffer // buf stores a copy of the content of R.
-}
-
-// Read reads the next len(p) bytes from R or until R is drained. The
-// return value n is the number of bytes read. If R has no data to
-// return, err is io.EOF and OnEOF is called with a full copy of what
-// has been read so far.
-func (r *cachingReadCloser) Read(p []byte) (n int, err error) {
-       n, err = r.R.Read(p)
-       r.buf.Write(p[:n])
-       if err == io.EOF {
-               r.OnEOF(bytes.NewReader(r.buf.Bytes()))
-       }
-       return n, err
-}
-
-func (r *cachingReadCloser) Close() error {
-       return r.R.Close()
-}
-
-// NewMemoryCacheTransport returns a new Transport using the in-memory cache implementation
-func NewMemoryCacheTransport() *Transport {
-       c := NewMemoryCache()
-       t := NewTransport(c)
-       return t
-}
index 9466854734647f7c1ff9751d0635c7d0b52c8918..3cd0e51014cd5523841b35fed0ec3cb065d4870d 100644 (file)
@@ -330,9 +330,6 @@ github.com/google/uuid
 # github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
 ## explicit; go 1.20
 github.com/gorilla/websocket
-# github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
-## explicit
-github.com/gregjones/httpcache
 # github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1
 ## explicit; go 1.19
 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus