Commit 588159a
Eric Bower
·
2026-02-26 21:36:17 -0500 EST
parent 7c27c34
refactor(pgs): custom rfc9111 compliant cache
12 files changed,
+1913,
-1321
M
Makefile
+5,
-1
| ... | ... | @@ -85,12 +85,16 @@ build-pico: | |
| 85 | 85 | go build -o "build/pico-ssh" "./cmd/pico/ssh" | |
| 86 | 86 | .PHONY: build-auth | |
| 87 | 87 | ||
| 88 | + | build-pgs-cdn: | |
| 89 | + | go build -o "build/pgs-cdn" "./cmd/pgs/cdn" | |
| 90 | + | .PHONY: build-cdn | |
| 91 | + | ||
| 88 | 92 | build-%: | |
| 89 | 93 | go build -o "build/$*-web" "./cmd/$*/web" | |
| 90 | 94 | go build -o "build/$*-ssh" "./cmd/$*/ssh" | |
| 91 | 95 | .PHONY: build-% | |
| 92 | 96 | ||
| 93 | - | build: build-prose build-pastes build-feeds build-pgs build-auth build-pico build-pipe | |
| 97 | + | build: build-prose build-pastes build-feeds build-pgs build-pgs-cdn build-auth build-pico build-pipe | |
| 94 | 98 | .PHONY: build | |
| 95 | 99 | ||
| 96 | 100 | scripts: |
+58,
-49
| ... | ... | @@ -3,22 +3,22 @@ package main | |
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | 5 | "fmt" | |
| 6 | + | "io" | |
| 6 | 7 | "log/slog" | |
| 7 | 8 | "net" | |
| 8 | 9 | "net/http" | |
| 9 | - | "net/http/httputil" | |
| 10 | 10 | "net/url" | |
| 11 | 11 | "strings" | |
| 12 | 12 | ||
| 13 | - | "github.com/darkweak/souin/pkg/middleware" | |
| 14 | - | "github.com/hashicorp/golang-lru/v2/expirable" | |
| 15 | 13 | "github.com/picosh/pico/pkg/apps/pgs" | |
| 14 | + | "github.com/picosh/pico/pkg/httpcache" | |
| 16 | 15 | "github.com/picosh/pico/pkg/shared" | |
| 17 | 16 | "github.com/prometheus/client_golang/prometheus/promhttp" | |
| 18 | 17 | ) | |
| 19 | 18 | ||
| 20 | 19 | func main() { | |
| 21 | - | withPipe := strings.ToLower(shared.GetEnv("PICO_PIPE_ENABLED", "true")) == "true" | |
| 20 | + | pipeEnabled := shared.GetEnv("PICO_PIPE_ENABLED", "true") | |
| 21 | + | withPipe := strings.ToLower(pipeEnabled) == "true" | |
| 22 | 22 | logger := shared.CreateLogger("pgs-cdn", withPipe) | |
| 23 | 23 | ctx := context.Background() | |
| 24 | 24 | drain := pgs.CreateSubCacheDrain(ctx, logger) |
| ... | ... | @@ -27,19 +27,14 @@ func main() { | |
| 27 | 27 | _ = pubsub.Close() | |
| 28 | 28 | }() | |
| 29 | 29 | cfg := pgs.NewPgsConfig(logger, nil, nil, drain) | |
| 30 | - | httpCache := pgs.SetupCache(cfg) | |
| 31 | - | router := &pgs.WebRouter{ | |
| 32 | - | Cfg: cfg, | |
| 33 | - | RedirectsCache: expirable.NewLRU[string, []*pgs.RedirectRule](2048, nil, shared.CacheTimeout), | |
| 34 | - | HeadersCache: expirable.NewLRU[string, []*pgs.HeaderRule](2048, nil, shared.CacheTimeout), | |
| 35 | - | } | |
| 30 | + | proxy := newProxyServe(cfg.Logger) | |
| 31 | + | httpCache := pgs.NewPgsHttpCache(cfg, proxy) | |
| 36 | 32 | cacher := &cachedHttp{ | |
| 37 | - | handler: httpCache, | |
| 38 | - | routes: router, | |
| 33 | + | Logger: cfg.Logger, | |
| 34 | + | Cache: httpCache, | |
| 39 | 35 | } | |
| 40 | 36 | ||
| 41 | - | go router.WatchCacheClear() | |
| 42 | - | go router.CacheMgmt(ctx, httpCache, cfg.CacheClearingQueue) | |
| 37 | + | go pgs.CacheMgmt(ctx, cfg.CacheClearingQueue, cfg, httpCache.Cache) | |
| 43 | 38 | ||
| 44 | 39 | portStr := fmt.Sprintf(":%s", cfg.WebPort) | |
| 45 | 40 | cfg.Logger.Info( |
| ... | ... | @@ -51,29 +46,54 @@ func main() { | |
| 51 | 46 | cfg.Logger.Error("listen and serve", "err", err) | |
| 52 | 47 | } | |
| 53 | 48 | ||
| 54 | - | type cachedHttp struct { | |
| 55 | - | handler *middleware.SouinBaseHandler | |
| 56 | - | routes *pgs.WebRouter | |
| 49 | + | type proxyServe struct { | |
| 50 | + | Logger *slog.Logger | |
| 51 | + | transport *http.Transport | |
| 57 | 52 | } | |
| 58 | 53 | ||
| 59 | - | type CustomTransport struct { | |
| 60 | - | *http.Transport | |
| 61 | - | Logger *slog.Logger | |
| 54 | + | func newProxyServe(logger *slog.Logger) *proxyServe { | |
| 55 | + | defaultTransport := http.DefaultTransport.(*http.Transport) | |
| 56 | + | oldDial := defaultTransport.DialContext | |
| 57 | + | transport := &http.Transport{ | |
| 58 | + | DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { | |
| 59 | + | return oldDial(ctx, "tcp", "ash.pgs.sh:443") | |
| 60 | + | }, | |
| 61 | + | } | |
| 62 | + | return &proxyServe{Logger: logger, transport: transport} | |
| 62 | 63 | } | |
| 63 | 64 | ||
| 64 | - | func (t *CustomTransport) RoundTrip(request *http.Request) (*http.Response, error) { | |
| 65 | - | // reqDump, _ := httputil.DumpRequestOut(request, false) | |
| 66 | - | // t.Logger.Info("request", "dump", string(reqDump)) | |
| 67 | - | response, err := http.DefaultTransport.RoundTrip(request) | |
| 65 | + | func (p *proxyServe) ServeHTTP(w http.ResponseWriter, req *http.Request) { | |
| 66 | + | target, _ := url.Parse(partialURL(req)) | |
| 67 | + | p.Logger.Info("proxying request to ash.pgs.sh", "url", target.String()) | |
| 68 | + | ||
| 69 | + | proxyReq := req.Clone(req.Context()) | |
| 70 | + | proxyReq.URL.Scheme = target.Scheme | |
| 71 | + | proxyReq.URL.Host = target.Host | |
| 72 | + | proxyReq.URL.Path = target.Path | |
| 73 | + | proxyReq.URL.RawQuery = target.RawQuery | |
| 74 | + | proxyReq.RequestURI = "" | |
| 75 | + | ||
| 76 | + | resp, err := p.transport.RoundTrip(proxyReq) | |
| 77 | + | if err != nil { | |
| 78 | + | http.Error(w, err.Error(), http.StatusBadGateway) | |
| 79 | + | return | |
| 80 | + | } | |
| 81 | + | defer func() { | |
| 82 | + | _ = resp.Body.Close() | |
| 83 | + | }() | |
| 68 | 84 | ||
| 69 | - | // body, err := httputil.DumpResponse(response, false) | |
| 70 | - | // if err != nil { | |
| 71 | - | // // copying the response body did not work | |
| 72 | - | // return nil, err | |
| 73 | - | // } | |
| 74 | - | // t.Logger.Info("response", "dump", string(body)) | |
| 85 | + | for k, vals := range resp.Header { | |
| 86 | + | for _, v := range vals { | |
| 87 | + | w.Header().Set(k, v) | |
| 88 | + | } | |
| 89 | + | } | |
| 90 | + | w.WriteHeader(resp.StatusCode) | |
| 91 | + | _, _ = io.Copy(w, resp.Body) | |
| 92 | + | } | |
| 75 | 93 | ||
| 76 | - | return response, err | |
| 94 | + | type cachedHttp struct { | |
| 95 | + | Logger *slog.Logger | |
| 96 | + | Cache *httpcache.HttpCache | |
| 77 | 97 | } | |
| 78 | 98 | ||
| 79 | 99 | func (c *cachedHttp) ServeHTTP(writer http.ResponseWriter, req *http.Request) { |
| ... | ... | @@ -83,7 +103,7 @@ func (c *cachedHttp) ServeHTTP(writer http.ResponseWriter, req *http.Request) { | |
| 83 | 103 | } | |
| 84 | 104 | ||
| 85 | 105 | if req.URL.Path == "/check" { | |
| 86 | - | c.routes.Cfg.Logger.Info("proxying `/check` request to ash.pgs.sh", "query", req.URL.RawQuery) | |
| 106 | + | c.Logger.Info("proxying `/check` request to ash.pgs.sh", "query", req.URL.RawQuery) | |
| 87 | 107 | req, _ := http.NewRequest("GET", "https://ash.pgs.sh/check?"+req.URL.RawQuery, nil) | |
| 88 | 108 | req.Host = "pgs.sh" | |
| 89 | 109 | // reqDump, _ := httputil.DumpRequestOut(req, true) |
| ... | ... | @@ -91,27 +111,16 @@ func (c *cachedHttp) ServeHTTP(writer http.ResponseWriter, req *http.Request) { | |
| 91 | 111 | ||
| 92 | 112 | resp, err := http.DefaultClient.Do(req) | |
| 93 | 113 | if err != nil { | |
| 94 | - | c.routes.Cfg.Logger.Error("check request", "err", err) | |
| 114 | + | c.Logger.Error("check request", "err", err) | |
| 95 | 115 | } | |
| 96 | 116 | writer.WriteHeader(resp.StatusCode) | |
| 97 | - | return | |
| 117 | + | defer func() { | |
| 118 | + | _ = resp.Body.Close() | |
| 119 | + | }() | |
| 120 | + | _, _ = io.Copy(writer, resp.Body) | |
| 98 | 121 | } | |
| 99 | 122 | ||
| 100 | - | _ = c.handler.ServeHTTP(writer, req, func(w http.ResponseWriter, r *http.Request) error { | |
| 101 | - | url, _ := url.Parse(partialURL(r)) | |
| 102 | - | ||
| 103 | - | c.routes.Cfg.Logger.Info("proxying request to ash.pgs.sh", "url", url.String()) | |
| 104 | - | defaultTransport := http.DefaultTransport.(*http.Transport) | |
| 105 | - | oldDialContext := defaultTransport.DialContext | |
| 106 | - | newTransport := CustomTransport{Transport: defaultTransport, Logger: c.routes.Cfg.Logger} | |
| 107 | - | newTransport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { | |
| 108 | - | return oldDialContext(ctx, "tcp", "ash.pgs.sh:443") | |
| 109 | - | } | |
| 110 | - | proxy := httputil.NewSingleHostReverseProxy(url) | |
| 111 | - | proxy.Transport = &newTransport | |
| 112 | - | proxy.ServeHTTP(w, r) | |
| 113 | - | return nil | |
| 114 | - | }) | |
| 123 | + | c.Cache.ServeHTTP(writer, req) | |
| 115 | 124 | } | |
| 116 | 125 | ||
| 117 | 126 | func partialURL(r *http.Request) string { |
M
go.mod
+3,
-150
| ... | ... | @@ -26,9 +26,6 @@ require ( | |
| 26 | 26 | github.com/antoniomika/syncmap v1.0.0 | |
| 27 | 27 | github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de | |
| 28 | 28 | github.com/containerd/console v1.0.5 | |
| 29 | - | github.com/darkweak/souin v1.7.8 | |
| 30 | - | github.com/darkweak/souin/plugins/souin/storages v1.7.8 | |
| 31 | - | github.com/darkweak/storages/core v0.0.16 | |
| 32 | 29 | github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 | |
| 33 | 30 | github.com/emersion/go-smtp v0.24.0 | |
| 34 | 31 | github.com/gkampitakis/go-snaps v0.5.15 |
| ... | ... | @@ -61,86 +58,37 @@ require ( | |
| 61 | 58 | go.abhg.dev/goldmark/hashtag v0.4.0 | |
| 62 | 59 | go.abhg.dev/goldmark/toc v0.12.0 | |
| 63 | 60 | golang.org/x/crypto v0.47.0 | |
| 64 | - | google.golang.org/protobuf v1.36.11 | |
| 65 | 61 | gopkg.in/yaml.v2 v2.4.0 | |
| 66 | 62 | modernc.org/sqlite v1.44.3 | |
| 67 | 63 | ) | |
| 68 | 64 | ||
| 69 | 65 | require ( | |
| 70 | - | cel.dev/expr v0.25.1 // indirect | |
| 71 | - | cloud.google.com/go/auth v0.18.1 // indirect | |
| 72 | - | cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect | |
| 73 | - | cloud.google.com/go/compute/metadata v0.9.0 // indirect | |
| 74 | 66 | codeberg.org/emersion/go-scfg v0.1.0 // indirect | |
| 75 | 67 | dario.cat/mergo v1.0.2 // indirect | |
| 76 | - | filippo.io/edwards25519 v1.1.0 // indirect | |
| 77 | - | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect | |
| 78 | 68 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect | |
| 79 | - | github.com/KimMachineGun/automemlimit v0.7.5 // indirect | |
| 80 | - | github.com/Masterminds/goutils v1.1.1 // indirect | |
| 81 | - | github.com/Masterminds/semver/v3 v3.4.0 // indirect | |
| 82 | - | github.com/Masterminds/sprig/v3 v3.3.0 // indirect | |
| 83 | 69 | github.com/Microsoft/go-winio v0.6.2 // indirect | |
| 84 | 70 | github.com/PuerkitoBio/goquery v1.11.0 // indirect | |
| 85 | - | github.com/RoaringBitmap/roaring v1.9.4 // indirect | |
| 86 | 71 | github.com/andybalholm/cascadia v1.3.3 // indirect | |
| 87 | - | github.com/antlabs/stl v0.0.2 // indirect | |
| 88 | - | github.com/antlabs/timer v0.1.4 // indirect | |
| 89 | - | github.com/antlr4-go/antlr/v4 v4.13.1 // indirect | |
| 90 | - | github.com/armon/go-metrics v0.4.1 // indirect | |
| 91 | - | github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect | |
| 92 | 72 | github.com/aymerick/douceur v0.2.0 // indirect | |
| 93 | 73 | github.com/beorn7/perks v1.0.1 // indirect | |
| 94 | - | github.com/bits-and-blooms/bitset v1.24.4 // indirect | |
| 95 | - | github.com/buraksezer/consistent v0.10.0 // indirect | |
| 96 | - | github.com/buraksezer/olric v0.5.7 // indirect | |
| 97 | - | github.com/bwmarrin/snowflake v0.3.0 // indirect | |
| 98 | - | github.com/caddyserver/caddy/v2 v2.10.2 // indirect | |
| 99 | - | github.com/caddyserver/certmagic v0.25.1 // indirect | |
| 100 | - | github.com/caddyserver/zerossl v0.1.4 // indirect | |
| 101 | - | github.com/ccoveille/go-safecast v1.6.1 // indirect | |
| 102 | 74 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect | |
| 103 | - | github.com/cespare/xxhash v1.1.0 // indirect | |
| 104 | 75 | github.com/cespare/xxhash/v2 v2.3.0 // indirect | |
| 105 | - | github.com/chzyer/readline v1.5.1 // indirect | |
| 106 | 76 | github.com/clipperhouse/stringish v0.1.1 // indirect | |
| 107 | 77 | github.com/clipperhouse/uax29/v2 v2.5.0 // indirect | |
| 108 | - | github.com/cloudflare/circl v1.6.3 // indirect | |
| 109 | 78 | github.com/containerd/errdefs v1.0.0 // indirect | |
| 110 | 79 | github.com/containerd/errdefs/pkg v0.3.0 // indirect | |
| 111 | 80 | github.com/containerd/log v0.1.0 // indirect | |
| 112 | 81 | github.com/containerd/platforms v0.2.1 // indirect | |
| 113 | - | github.com/coreos/go-oidc/v3 v3.17.0 // indirect | |
| 114 | - | github.com/coreos/go-semver v0.3.1 // indirect | |
| 115 | - | github.com/coreos/go-systemd/v22 v22.7.0 // indirect | |
| 116 | 82 | github.com/cpuguy83/dockercfg v0.3.2 // indirect | |
| 117 | - | github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect | |
| 118 | - | github.com/darkweak/go-esi v0.0.6 // indirect | |
| 119 | - | github.com/darkweak/storages/badger v0.0.16 // indirect | |
| 120 | - | github.com/darkweak/storages/etcd v0.0.16 // indirect | |
| 121 | - | github.com/darkweak/storages/nats v0.0.16 // indirect | |
| 122 | - | github.com/darkweak/storages/nuts v0.0.16 // indirect | |
| 123 | - | github.com/darkweak/storages/olric v0.0.16 // indirect | |
| 124 | - | github.com/darkweak/storages/otter v0.0.16 // indirect | |
| 125 | - | github.com/darkweak/storages/redis v0.0.16 // indirect | |
| 126 | - | github.com/darkweak/storages/simplefs v0.0.16 // indirect | |
| 127 | 83 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect | |
| 128 | 84 | github.com/delthas/go-libnp v0.2.0 // indirect | |
| 129 | 85 | github.com/delthas/go-localeinfo v0.2.0 // indirect | |
| 130 | - | github.com/dgraph-io/badger v1.6.2 // indirect | |
| 131 | - | github.com/dgraph-io/badger/v2 v2.2007.4 // indirect | |
| 132 | - | github.com/dgraph-io/badger/v4 v4.9.1 // indirect | |
| 133 | - | github.com/dgraph-io/ristretto v0.2.0 // indirect | |
| 134 | - | github.com/dgraph-io/ristretto/v2 v2.4.0 // indirect | |
| 135 | - | github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect | |
| 136 | - | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect | |
| 137 | 86 | github.com/disintegration/imaging v1.6.2 // indirect | |
| 138 | 87 | github.com/distribution/reference v0.6.0 // indirect | |
| 139 | 88 | github.com/dlclark/regexp2 v1.11.5 // indirect | |
| 140 | 89 | github.com/docker/docker v28.5.1+incompatible // indirect | |
| 141 | 90 | github.com/docker/go-connections v0.6.0 // indirect | |
| 142 | 91 | github.com/docker/go-units v0.5.0 // indirect | |
| 143 | - | github.com/dolthub/maphash v0.1.0 // indirect | |
| 144 | 92 | github.com/dsoprea/go-exif v0.0.0-20230826092837-6579e82b732d // indirect | |
| 145 | 93 | github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d // indirect | |
| 146 | 94 | github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 // indirect |
| ... | ... | @@ -150,76 +98,34 @@ require ( | |
| 150 | 98 | github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 // indirect | |
| 151 | 99 | github.com/dustin/go-humanize v1.0.1 // indirect | |
| 152 | 100 | github.com/ebitengine/purego v0.8.4 // indirect | |
| 153 | - | github.com/edsrzf/mmap-go v1.2.0 // indirect | |
| 154 | 101 | github.com/felixge/httpsnoop v1.0.4 // indirect | |
| 155 | 102 | github.com/forPelevin/gomoji v1.4.1 // indirect | |
| 156 | - | github.com/francoispqt/gojay v1.2.13 // indirect | |
| 157 | - | github.com/gammazero/deque v1.2.0 // indirect | |
| 158 | 103 | github.com/gkampitakis/ciinfo v0.3.2 // indirect | |
| 159 | 104 | github.com/gkampitakis/go-diff v1.3.2 // indirect | |
| 160 | 105 | github.com/go-errors/errors v1.5.1 // indirect | |
| 161 | - | github.com/go-jose/go-jose/v3 v3.0.4 // indirect | |
| 162 | - | github.com/go-jose/go-jose/v4 v4.1.3 // indirect | |
| 163 | 106 | github.com/go-logr/logr v1.4.3 // indirect | |
| 164 | 107 | github.com/go-logr/stdr v1.2.2 // indirect | |
| 165 | 108 | github.com/go-ole/go-ole v1.3.0 // indirect | |
| 166 | - | github.com/go-redis/redis/v8 v8.11.5 // indirect | |
| 167 | 109 | github.com/go-sql-driver/mysql v1.9.3 // indirect | |
| 168 | 110 | github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect | |
| 169 | 111 | github.com/goccy/go-yaml v1.18.0 // indirect | |
| 170 | 112 | github.com/godbus/dbus/v5 v5.2.2 // indirect | |
| 171 | - | github.com/gofrs/flock v0.13.0 // indirect | |
| 172 | - | github.com/gogo/protobuf v1.3.2 // indirect | |
| 173 | 113 | github.com/golang/geo v0.0.0-20260129164528-943061e2742c // indirect | |
| 174 | - | github.com/golang/protobuf v1.5.4 // indirect | |
| 175 | - | github.com/golang/snappy v1.0.0 // indirect | |
| 176 | - | github.com/google/btree v1.1.3 // indirect | |
| 177 | - | github.com/google/cel-go v0.27.0 // indirect | |
| 178 | - | github.com/google/flatbuffers v25.12.19+incompatible // indirect | |
| 179 | 114 | github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef // indirect | |
| 180 | - | github.com/google/s2a-go v0.1.9 // indirect | |
| 181 | - | github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect | |
| 182 | - | github.com/googleapis/gax-go/v2 v2.17.0 // indirect | |
| 183 | 115 | github.com/gorilla/css v1.0.1 // indirect | |
| 184 | 116 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect | |
| 185 | - | github.com/hashicorp/errwrap v1.1.0 // indirect | |
| 186 | - | github.com/hashicorp/go-immutable-radix v1.3.1 // indirect | |
| 187 | - | github.com/hashicorp/go-metrics v0.5.4 // indirect | |
| 188 | - | github.com/hashicorp/go-msgpack/v2 v2.1.5 // indirect | |
| 189 | - | github.com/hashicorp/go-multierror v1.1.1 // indirect | |
| 190 | - | github.com/hashicorp/go-sockaddr v1.0.7 // indirect | |
| 191 | - | github.com/hashicorp/golang-lru v1.0.2 // indirect | |
| 192 | - | github.com/hashicorp/logutils v1.0.0 // indirect | |
| 193 | - | github.com/hashicorp/memberlist v0.5.4 // indirect | |
| 194 | - | github.com/huandu/xstrings v1.5.0 // indirect | |
| 195 | - | github.com/inconshreveable/mousetrap v1.1.0 // indirect | |
| 196 | - | github.com/jackc/pgpassfile v1.0.0 // indirect | |
| 197 | - | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect | |
| 198 | 117 | github.com/jackc/pgx/v5 v5.8.0 // indirect | |
| 199 | - | github.com/jackc/puddle/v2 v2.2.2 // indirect | |
| 200 | - | github.com/jellydator/ttlcache/v3 v3.4.0 // indirect | |
| 201 | 118 | github.com/json-iterator/go v1.1.12 // indirect | |
| 202 | 119 | github.com/klauspost/compress v1.18.3 // indirect | |
| 203 | - | github.com/klauspost/cpuid/v2 v2.3.0 // indirect | |
| 204 | 120 | github.com/kr/fs v0.1.0 // indirect | |
| 205 | 121 | github.com/kr/pretty v0.3.1 // indirect | |
| 206 | 122 | github.com/kr/text v0.2.0 // indirect | |
| 207 | - | github.com/libdns/libdns v1.1.1 // indirect | |
| 208 | 123 | github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect | |
| 209 | 124 | github.com/magiconair/properties v1.8.10 // indirect | |
| 210 | - | github.com/manifoldco/promptui v0.9.0 // indirect | |
| 211 | 125 | github.com/maruel/natural v1.1.1 // indirect | |
| 212 | - | github.com/mattn/go-colorable v0.1.14 // indirect | |
| 213 | 126 | github.com/mattn/go-isatty v0.0.20 // indirect | |
| 214 | 127 | github.com/mattn/go-runewidth v0.0.19 // indirect | |
| 215 | 128 | github.com/mattn/go-sixel v0.0.8 // indirect | |
| 216 | - | github.com/maypok86/otter v1.2.4 // indirect | |
| 217 | - | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect | |
| 218 | - | github.com/mholt/acmez/v3 v3.1.4 // indirect | |
| 219 | - | github.com/miekg/dns v1.1.72 // indirect | |
| 220 | - | github.com/mitchellh/copystructure v1.2.0 // indirect | |
| 221 | - | github.com/mitchellh/go-ps v1.0.0 // indirect | |
| 222 | - | github.com/mitchellh/reflectwalk v1.0.2 // indirect | |
| 223 | 129 | github.com/mmcdole/goxpp v1.1.1 // indirect | |
| 224 | 130 | github.com/mmcloughlin/md4 v0.1.2 // indirect | |
| 225 | 131 | github.com/moby/docker-image-spec v1.3.1 // indirect |
| ... | ... | @@ -232,105 +138,52 @@ require ( | |
| 232 | 138 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | |
| 233 | 139 | github.com/modern-go/reflect2 v1.0.2 // indirect | |
| 234 | 140 | github.com/morikuni/aec v1.0.0 // indirect | |
| 235 | - | github.com/mschoch/smat v0.2.0 // indirect | |
| 236 | 141 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect | |
| 237 | - | github.com/nats-io/nats.go v1.48.0 // indirect | |
| 238 | - | github.com/nats-io/nkeys v0.4.15 // indirect | |
| 239 | - | github.com/nats-io/nuid v1.0.1 // indirect | |
| 240 | 142 | github.com/ncruces/go-strftime v1.0.0 // indirect | |
| 241 | 143 | github.com/neurosnap/go-jpeg-image-structure v0.0.0-20221010133817-70b1c1ff679e // indirect | |
| 242 | - | github.com/nutsdb/nutsdb v1.1.0 // indirect | |
| 243 | - | github.com/onsi/gomega v1.39.0 // indirect | |
| 244 | 144 | github.com/opencontainers/go-digest v1.0.0 // indirect | |
| 245 | 145 | github.com/opencontainers/image-spec v1.1.1 // indirect | |
| 246 | - | github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect | |
| 247 | - | github.com/pierrec/lz4/v4 v4.1.25 // indirect | |
| 248 | 146 | github.com/pkg/errors v0.9.1 // indirect | |
| 249 | 147 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect | |
| 250 | 148 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect | |
| 251 | - | github.com/pquerna/cachecontrol v0.2.0 // indirect | |
| 252 | 149 | github.com/prometheus/client_model v0.6.2 // indirect | |
| 253 | 150 | github.com/prometheus/common v0.67.5 // indirect | |
| 254 | 151 | github.com/prometheus/procfs v0.19.2 // indirect | |
| 255 | - | github.com/quic-go/qpack v0.5.1 // indirect | |
| 256 | - | github.com/quic-go/quic-go v0.54.0 // indirect | |
| 257 | - | github.com/redis/rueidis v1.0.71 // indirect | |
| 258 | 152 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect | |
| 259 | 153 | github.com/rivo/uniseg v0.4.7 // indirect | |
| 260 | 154 | github.com/rogpeppe/go-internal v1.14.1 // indirect | |
| 261 | - | github.com/rs/xid v1.6.0 // indirect | |
| 262 | - | github.com/russross/blackfriday/v2 v2.1.0 // indirect | |
| 263 | - | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect | |
| 264 | 155 | github.com/shirou/gopsutil/v4 v4.25.6 // indirect | |
| 265 | - | github.com/shopspring/decimal v1.4.0 // indirect | |
| 266 | - | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect | |
| 267 | 156 | github.com/sirupsen/logrus v1.9.4 // indirect | |
| 268 | - | github.com/slackhq/nebula v1.9.5 // indirect | |
| 269 | - | github.com/smallstep/certificates v0.28.4 // indirect | |
| 270 | - | github.com/smallstep/cli-utils v0.12.1 // indirect | |
| 271 | - | github.com/smallstep/linkedca v0.23.0 // indirect | |
| 272 | - | github.com/smallstep/nosql v0.7.0 // indirect | |
| 273 | - | github.com/smallstep/pkcs7 v0.2.1 // indirect | |
| 274 | - | github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492 // indirect | |
| 275 | - | github.com/smallstep/truststore v0.13.0 // indirect | |
| 276 | 157 | github.com/soniakeys/quant v1.0.0 // indirect | |
| 277 | - | github.com/spf13/cast v1.10.0 // indirect | |
| 278 | - | github.com/spf13/cobra v1.10.2 // indirect | |
| 279 | - | github.com/spf13/pflag v1.0.10 // indirect | |
| 280 | 158 | github.com/stretchr/testify v1.11.1 // indirect | |
| 281 | - | github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect | |
| 282 | - | github.com/tailscale/tscert v0.0.0-20251216020129-aea342f6d747 // indirect | |
| 283 | - | github.com/tidwall/btree v1.8.1 // indirect | |
| 284 | 159 | github.com/tidwall/gjson v1.18.0 // indirect | |
| 285 | 160 | github.com/tidwall/match v1.2.0 // indirect | |
| 286 | 161 | github.com/tidwall/pretty v1.2.1 // indirect | |
| 287 | - | github.com/tidwall/redcon v1.6.2 // indirect | |
| 288 | 162 | github.com/tidwall/sjson v1.2.5 // indirect | |
| 289 | 163 | github.com/tklauser/go-sysconf v0.3.15 // indirect | |
| 290 | 164 | github.com/tklauser/numcpus v0.10.0 // indirect | |
| 291 | - | github.com/urfave/cli v1.22.17 // indirect | |
| 292 | - | github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect | |
| 293 | - | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect | |
| 294 | - | github.com/xujiajun/utils v0.0.0-20220904132955-5f7c5b914235 // indirect | |
| 295 | 165 | github.com/yusufpapurcu/wmi v1.2.4 // indirect | |
| 296 | - | github.com/zeebo/blake3 v0.2.4 // indirect | |
| 297 | - | go.etcd.io/bbolt v1.4.3 // indirect | |
| 298 | - | go.etcd.io/etcd/api/v3 v3.6.7 // indirect | |
| 299 | - | go.etcd.io/etcd/client/pkg/v3 v3.6.7 // indirect | |
| 300 | - | go.etcd.io/etcd/client/v3 v3.6.7 // indirect | |
| 301 | 166 | go.opentelemetry.io/auto/sdk v1.2.1 // indirect | |
| 302 | 167 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect | |
| 303 | 168 | go.opentelemetry.io/otel v1.40.0 // indirect | |
| 169 | + | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect | |
| 304 | 170 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect | |
| 305 | 171 | go.opentelemetry.io/otel/metric v1.40.0 // indirect | |
| 306 | 172 | go.opentelemetry.io/otel/trace v1.40.0 // indirect | |
| 307 | - | go.step.sm/crypto v0.76.0 // indirect | |
| 308 | - | go.uber.org/automaxprocs v1.6.0 // indirect | |
| 309 | - | go.uber.org/mock v0.6.0 // indirect | |
| 310 | - | go.uber.org/multierr v1.11.0 // indirect | |
| 311 | - | go.uber.org/zap v1.27.1 // indirect | |
| 312 | - | go.uber.org/zap/exp v0.3.0 // indirect | |
| 173 | + | go.opentelemetry.io/proto/otlp v1.7.0 // indirect | |
| 313 | 174 | go.yaml.in/yaml/v2 v2.4.3 // indirect | |
| 314 | - | go.yaml.in/yaml/v3 v3.0.4 // indirect | |
| 315 | - | golang.org/x/crypto/x509roots/fallback v0.0.0-20260113154411-7d0074ccc6f1 // indirect | |
| 316 | 175 | golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect | |
| 317 | 176 | golang.org/x/image v0.35.0 // indirect | |
| 318 | - | golang.org/x/mod v0.32.0 // indirect | |
| 319 | 177 | golang.org/x/net v0.49.0 // indirect | |
| 320 | - | golang.org/x/oauth2 v0.34.0 // indirect | |
| 321 | 178 | golang.org/x/sync v0.19.0 // indirect | |
| 322 | 179 | golang.org/x/sys v0.40.0 // indirect | |
| 323 | - | golang.org/x/term v0.39.0 // indirect | |
| 324 | 180 | golang.org/x/text v0.33.0 // indirect | |
| 325 | 181 | golang.org/x/time v0.14.0 // indirect | |
| 326 | - | golang.org/x/tools v0.41.0 // indirect | |
| 327 | - | google.golang.org/api v0.265.0 // indirect | |
| 328 | 182 | google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect | |
| 329 | 183 | google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect | |
| 330 | 184 | google.golang.org/grpc v1.78.0 // indirect | |
| 331 | - | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1 // indirect | |
| 185 | + | google.golang.org/protobuf v1.36.11 // indirect | |
| 332 | 186 | gopkg.in/yaml.v3 v3.0.1 // indirect | |
| 333 | - | howett.net/plist v1.0.1 // indirect | |
| 334 | 187 | modernc.org/libc v1.67.7 // indirect | |
| 335 | 188 | modernc.org/mathutil v1.7.1 // indirect | |
| 336 | 189 | modernc.org/memory v1.11.0 // indirect |
M
go.sum
+0,
-765
| ... | ... | @@ -1,64 +1,21 @@ | |
| 1 | - | cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= | |
| 2 | - | cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= | |
| 3 | - | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | |
| 4 | - | cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | |
| 5 | - | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | |
| 6 | - | cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= | |
| 7 | - | cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= | |
| 8 | - | cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= | |
| 9 | - | cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs= | |
| 10 | - | cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA= | |
| 11 | - | cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= | |
| 12 | - | cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= | |
| 13 | - | cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= | |
| 14 | - | cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= | |
| 15 | - | cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= | |
| 16 | - | cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= | |
| 17 | - | cloud.google.com/go/kms v1.24.0 h1:SWltUuoPhTdv9q/P0YEAWQfoYT32O5HdfPgTiWMvrH8= | |
| 18 | - | cloud.google.com/go/kms v1.24.0/go.mod h1:QDH3z2SJ50lfNOE8EokKC1G40i7I0f8xTMCoiptcb5g= | |
| 19 | - | cloud.google.com/go/longrunning v0.7.0 h1:FV0+SYF1RIj59gyoWDRi45GiYUMM3K1qO51qoboQT1E= | |
| 20 | - | cloud.google.com/go/longrunning v0.7.0/go.mod h1:ySn2yXmjbK9Ba0zsQqunhDkYi0+9rlXIwnoAf+h+TPY= | |
| 21 | 1 | codeberg.org/emersion/go-scfg v0.1.0 h1:6dnGU0ZI4gX+O5rMjwhoaySItzHG710eXL5TIQKl+uM= | |
| 22 | 2 | codeberg.org/emersion/go-scfg v0.1.0/go.mod h1:0nooW1ufBB4SlJEdTtiVN9Or+bnNM1icOkQ6Tbrq6O0= | |
| 23 | 3 | dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= | |
| 24 | 4 | dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= | |
| 25 | - | dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= | |
| 26 | - | dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= | |
| 27 | - | dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= | |
| 28 | - | dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= | |
| 29 | 5 | filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= | |
| 30 | 6 | filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= | |
| 31 | - | git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= | |
| 32 | 7 | git.sr.ht/~delthas/senpai v0.4.1 h1:5CABpwJVzIBQaXtOngxG9TQDcxlaUqmGsXxlW+XcLuM= | |
| 33 | 8 | git.sr.ht/~delthas/senpai v0.4.1/go.mod h1:mzdu4o3wANA6cYzRnrz3w+uGPHA2z3j02JDrr/M3Myc= | |
| 34 | 9 | git.sr.ht/~rockorager/vaxis v0.15.1-0.20251218121515-cdf898cf10c7 h1:Hik9uYRclWV+mt5PnGc41aeGX6sh8MGzWcBnJt9EzpY= | |
| 35 | 10 | git.sr.ht/~rockorager/vaxis v0.15.1-0.20251218121515-cdf898cf10c7/go.mod h1:ptCAzb19xAhqhfAmCsN9Xnxdx01vlXkwZcuMYPnyqzE= | |
| 36 | 11 | github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= | |
| 37 | 12 | github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= | |
| 38 | - | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= | |
| 39 | - | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= | |
| 40 | 13 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= | |
| 41 | 14 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= | |
| 42 | - | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | |
| 43 | - | github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= | |
| 44 | - | github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= | |
| 45 | - | github.com/KimMachineGun/automemlimit v0.7.5 h1:RkbaC0MwhjL1ZuBKunGDjE/ggwAX43DwZrJqVwyveTk= | |
| 46 | - | github.com/KimMachineGun/automemlimit v0.7.5/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM= | |
| 47 | - | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= | |
| 48 | - | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= | |
| 49 | - | github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= | |
| 50 | - | github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= | |
| 51 | - | github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= | |
| 52 | - | github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= | |
| 53 | 15 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= | |
| 54 | 16 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= | |
| 55 | - | github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= | |
| 56 | - | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | |
| 57 | 17 | github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw= | |
| 58 | 18 | github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ= | |
| 59 | - | github.com/RoaringBitmap/roaring v1.2.1/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA= | |
| 60 | - | github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ= | |
| 61 | - | github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= | |
| 62 | 19 | github.com/adhocore/gronx v1.19.6 h1:5KNVcoR9ACgL9HhEqCm5QXsab/gI4QDIybTAWcXDKDc= | |
| 63 | 20 | github.com/adhocore/gronx v1.19.6/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= | |
| 64 | 21 | github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= |
| ... | ... | @@ -69,114 +26,24 @@ github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9c | |
| 69 | 26 | github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= | |
| 70 | 27 | github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= | |
| 71 | 28 | github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= | |
| 72 | - | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | |
| 73 | - | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | |
| 74 | - | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | |
| 75 | - | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | |
| 76 | - | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= | |
| 77 | 29 | github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= | |
| 78 | 30 | github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= | |
| 79 | - | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= | |
| 80 | - | github.com/antlabs/stl v0.0.2 h1:sna1AXR5yIkNE9lWhCcKbheFJSVfCa3vugnGyakI79s= | |
| 81 | - | github.com/antlabs/stl v0.0.2/go.mod h1:kKrO4xrn9cfS1mJVo+/BqePZjAYMXqD0amGF2Ouq7ac= | |
| 82 | - | github.com/antlabs/timer v0.1.4 h1:MHdE00MDnNfhJCmqSOdLXs35uGNwfkMwfbynxrGmQ1c= | |
| 83 | - | github.com/antlabs/timer v0.1.4/go.mod h1:mpw4zlD5KVjstEyUDp43DGLWsY076Mdo4bS78NTseRE= | |
| 84 | - | github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= | |
| 85 | - | github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= | |
| 86 | 31 | github.com/antoniomika/syncmap v1.0.0 h1:iFSfbQFQOvHZILFZF+hqWosO0no+W9+uF4y2VEyMKWU= | |
| 87 | 32 | github.com/antoniomika/syncmap v1.0.0/go.mod h1:fK2829foEYnO4riNfyUn0SHQZt4ue3DStYjGU+sJj38= | |
| 88 | 33 | github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= | |
| 89 | 34 | github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= | |
| 90 | - | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | |
| 91 | - | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= | |
| 92 | - | github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= | |
| 93 | - | github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= | |
| 94 | - | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= | |
| 95 | - | github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw= | |
| 96 | - | github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= | |
| 97 | - | github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU= | |
| 98 | - | github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= | |
| 99 | - | github.com/aws/aws-sdk-go-v2/config v1.32.7 h1:vxUyWGUwmkQ2g19n7JY/9YL8MfAIl7bTesIUykECXmY= | |
| 100 | - | github.com/aws/aws-sdk-go-v2/config v1.32.7/go.mod h1:2/Qm5vKUU/r7Y+zUk/Ptt2MDAEKAfUtKc1+3U1Mo3oY= | |
| 101 | - | github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8= | |
| 102 | - | github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw= | |
| 103 | - | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY= | |
| 104 | - | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA= | |
| 105 | - | github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U= | |
| 106 | - | github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ= | |
| 107 | - | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik= | |
| 108 | - | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM= | |
| 109 | - | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= | |
| 110 | - | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= | |
| 111 | - | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= | |
| 112 | - | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= | |
| 113 | - | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY= | |
| 114 | - | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU= | |
| 115 | - | github.com/aws/aws-sdk-go-v2/service/kms v1.49.5 h1:DKibav4XF66XSeaXcrn9GlWGHos6D/vJ4r7jsK7z5CE= | |
| 116 | - | github.com/aws/aws-sdk-go-v2/service/kms v1.49.5/go.mod h1:1SdcmEGUEQE1mrU2sIgeHtcMSxHuybhPvuEPANzIDfI= | |
| 117 | - | github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y= | |
| 118 | - | github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M= | |
| 119 | - | github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk= | |
| 120 | - | github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw= | |
| 121 | - | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 h1:gd84Omyu9JLriJVCbGApcLzVR3XtmC4ZDPcAI6Ftvds= | |
| 122 | - | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo= | |
| 123 | - | github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ= | |
| 124 | - | github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ= | |
| 125 | - | github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= | |
| 126 | - | github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= | |
| 127 | 35 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= | |
| 128 | 36 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= | |
| 129 | - | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | |
| 130 | - | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | |
| 131 | 37 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= | |
| 132 | 38 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= | |
| 133 | - | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= | |
| 134 | - | github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= | |
| 135 | - | github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= | |
| 136 | - | github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= | |
| 137 | - | github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= | |
| 138 | - | github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= | |
| 139 | - | github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= | |
| 140 | - | github.com/buraksezer/consistent v0.10.0 h1:hqBgz1PvNLC5rkWcEBVAL9dFMBWz6I0VgUCW25rrZlU= | |
| 141 | - | github.com/buraksezer/consistent v0.10.0/go.mod h1:6BrVajWq7wbKZlTOUPs/XVfR8c0maujuPowduSpZqmw= | |
| 142 | - | github.com/buraksezer/olric v0.5.7 h1:K8ypVViiPkXiqBz3UyDAY99cHvvofAR65fmH7ElPEWE= | |
| 143 | - | github.com/buraksezer/olric v0.5.7/go.mod h1:S1R+9Zt7P9TCbvQZvY/RYuRehLLRPDfbJNkukQsLJ4k= | |
| 144 | - | github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= | |
| 145 | - | github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= | |
| 146 | - | github.com/caddyserver/caddy/v2 v2.10.2 h1:g/gTYjGMD0dec+UgMw8SnfmJ3I9+M2TdvoRL/Ovu6U8= | |
| 147 | - | github.com/caddyserver/caddy/v2 v2.10.2/go.mod h1:TXLQHx+ev4HDpkO6PnVVHUbL6OXt6Dfe7VcIBdQnPL0= | |
| 148 | - | github.com/caddyserver/certmagic v0.25.1 h1:4sIKKbOt5pg6+sL7tEwymE1x2bj6CHr80da1CRRIPbY= | |
| 149 | - | github.com/caddyserver/certmagic v0.25.1/go.mod h1:VhyvndxtVton/Fo/wKhRoC46Rbw1fmjvQ3GjHYSQTEY= | |
| 150 | - | github.com/caddyserver/zerossl v0.1.4 h1:CVJOE3MZeFisCERZjkxIcsqIH4fnFdlYWnPYeFtBHRw= | |
| 151 | - | github.com/caddyserver/zerossl v0.1.4/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= | |
| 152 | - | github.com/ccoveille/go-safecast v1.6.1 h1:Nb9WMDR8PqhnKCVs2sCB+OqhohwO5qaXtCviZkIff5Q= | |
| 153 | - | github.com/ccoveille/go-safecast v1.6.1/go.mod h1:QqwNjxQ7DAqY0C721OIO9InMk9zCwcsO7tnRuHytad8= | |
| 154 | 39 | github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= | |
| 155 | 40 | github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= | |
| 156 | - | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= | |
| 157 | - | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= | |
| 158 | - | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | |
| 159 | - | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | |
| 160 | 41 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= | |
| 161 | 42 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | |
| 162 | - | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | |
| 163 | - | github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= | |
| 164 | - | github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= | |
| 165 | - | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | |
| 166 | - | github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= | |
| 167 | - | github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= | |
| 168 | - | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= | |
| 169 | - | github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= | |
| 170 | - | github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= | |
| 171 | - | github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= | |
| 172 | - | github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= | |
| 173 | - | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= | |
| 174 | 43 | github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= | |
| 175 | 44 | github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= | |
| 176 | 45 | github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U= | |
| 177 | 46 | github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= | |
| 178 | - | github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= | |
| 179 | - | github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= | |
| 180 | 47 | github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= | |
| 181 | 48 | github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= | |
| 182 | 49 | github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= |
| ... | ... | @@ -187,49 +54,11 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= | |
| 187 | 54 | github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= | |
| 188 | 55 | github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= | |
| 189 | 56 | github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= | |
| 190 | - | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= | |
| 191 | - | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= | |
| 192 | - | github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc= | |
| 193 | - | github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8= | |
| 194 | - | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= | |
| 195 | - | github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= | |
| 196 | - | github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= | |
| 197 | - | github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= | |
| 198 | - | github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= | |
| 199 | - | github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= | |
| 200 | 57 | github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= | |
| 201 | 58 | github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= | |
| 202 | - | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= | |
| 203 | - | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= | |
| 204 | - | github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= | |
| 205 | - | github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= | |
| 206 | 59 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | |
| 207 | 60 | github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= | |
| 208 | 61 | github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= | |
| 209 | - | github.com/darkweak/go-esi v0.0.6 h1:eVHCJfqrZwOHPfRK7JTlSYG9F8lfpX/d4lz/41RQkd8= | |
| 210 | - | github.com/darkweak/go-esi v0.0.6/go.mod h1:IJSayeQZDUh5R5ayyDC3wUEBykti12aUa0eUxZZeodk= | |
| 211 | - | github.com/darkweak/souin v1.7.8 h1:yENXy0oSzknu0Uzz/bO01rYLCcmAITlDgvEEtbT10PE= | |
| 212 | - | github.com/darkweak/souin v1.7.8/go.mod h1:kR97vndGl0jEWmHazJXNTdVkOzQis05kUdK9EGKUf+k= | |
| 213 | - | github.com/darkweak/souin/plugins/souin/storages v1.7.8 h1:7x+FU8CU7EKr96hnMTh6wuX0OHfyBBkSr03X2WY7MRs= | |
| 214 | - | github.com/darkweak/souin/plugins/souin/storages v1.7.8/go.mod h1:3OmumRhtRKzIalqlqIHDuvaaZMSJDoO5q1b1RRnCZRw= | |
| 215 | - | github.com/darkweak/storages/badger v0.0.16 h1:5NNHescfvT0YqqgeCvRB9cFtCeCvEetPliCYOI+aGqo= | |
| 216 | - | github.com/darkweak/storages/badger v0.0.16/go.mod h1:hpKJ7pGI27elfu1IJ1OnsBpsyYx627oeebtL+DbN0mI= | |
| 217 | - | github.com/darkweak/storages/core v0.0.16 h1:f7+XY8MJaKiukcOC5v48pTSpD0zpzEu4FnfgOAPbyF0= | |
| 218 | - | github.com/darkweak/storages/core v0.0.16/go.mod h1:3qJqrenCLpu+0bWPOAq36CmGpzL3SGWAz6KGZGnur1U= | |
| 219 | - | github.com/darkweak/storages/etcd v0.0.16 h1:l9p4+tinfc6AUITHld3g5lzBbLokfIudowEIPK2E1+U= | |
| 220 | - | github.com/darkweak/storages/etcd v0.0.16/go.mod h1:UnkdHq06kZS9QeQ9ItNIN9Bl7YZZVdVclX+kcvAd+k8= | |
| 221 | - | github.com/darkweak/storages/nats v0.0.16 h1:9KaHq9q+tK4Tm1JjAaj6YFGy8/9u5AJK9O9tFx+NvYI= | |
| 222 | - | github.com/darkweak/storages/nats v0.0.16/go.mod h1:yWkupn1y7R4wPzLgqgXc65c50X+7q94QQ9PCxwLqIWA= | |
| 223 | - | github.com/darkweak/storages/nuts v0.0.16 h1:EJ6b8YbXjK7PsdE6mMRKKs6qj3G+5dW4PfmzCNXrdsA= | |
| 224 | - | github.com/darkweak/storages/nuts v0.0.16/go.mod h1:MPNNFqFAuiFLDsPcsxXSG5h5dRea1Wd6cXAzHXnFD1Q= | |
| 225 | - | github.com/darkweak/storages/olric v0.0.16 h1:gv7S+eWAmjnYbhMVEmltEHG6Kzacil7nTXpKfXT47F4= | |
| 226 | - | github.com/darkweak/storages/olric v0.0.16/go.mod h1:PyZuWYNVN0y4lFNN1ZqEWxOW59nzQfXU6aiJnQegxcM= | |
| 227 | - | github.com/darkweak/storages/otter v0.0.16 h1:SFPlKIapzcrfSp16C3/K6fzIqRblqDWE/xyse9VhJFE= | |
| 228 | - | github.com/darkweak/storages/otter v0.0.16/go.mod h1:FSqYWBBSoojOZEH4hr19TmBnBxyH+ZQ4V8tGp4xVBGw= | |
| 229 | - | github.com/darkweak/storages/redis v0.0.16 h1:dlDUpco8V6DnOwqS5oYFIi4aLBtto9u0ZyJqbpyy22U= | |
| 230 | - | github.com/darkweak/storages/redis v0.0.16/go.mod h1:kBchmscY7C7URnruedPgb3jcijV1COzszfOe/KVTk5g= | |
| 231 | - | github.com/darkweak/storages/simplefs v0.0.16 h1:9duSjUTKjZWUv2Spk5+3MueENwxElEWlp0Tij2CihEY= | |
| 232 | - | github.com/darkweak/storages/simplefs v0.0.16/go.mod h1:TMjB3kGbYHZPZoRGTCW+Wph9taF2/ym0cUfP0jYU47s= | |
| 233 | 62 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |
| 234 | 63 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |
| 235 | 64 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= |
| ... | ... | @@ -238,23 +67,6 @@ github.com/delthas/go-libnp v0.2.0 h1:OVll7Z9CER0rYpgiglXN1QvuNtkyY5X9uzLqm+xDst | |
| 238 | 67 | github.com/delthas/go-libnp v0.2.0/go.mod h1:LHUapIgbv1vBcZJU4cJRPkyslKhKe7GNRF38yUbl9QA= | |
| 239 | 68 | github.com/delthas/go-localeinfo v0.2.0 h1:0K5F5GQ5p4ealZjuBs4eubHLOIL21/YWSxs557hHZ+M= | |
| 240 | 69 | github.com/delthas/go-localeinfo v0.2.0/go.mod h1:sG54BxlyQgIskYURLrg7mvhoGBe0Qq12DNtYRALwNa4= | |
| 241 | - | github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= | |
| 242 | - | github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= | |
| 243 | - | github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= | |
| 244 | - | github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= | |
| 245 | - | github.com/dgraph-io/badger/v4 v4.9.1 h1:DocZXZkg5JJHJPtUErA0ibyHxOVUDVoXLSCV6t8NC8w= | |
| 246 | - | github.com/dgraph-io/badger/v4 v4.9.1/go.mod h1:5/MEx97uzdPUHR4KtkNt8asfI2T4JiEiQlV7kWUo8c0= | |
| 247 | - | github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= | |
| 248 | - | github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= | |
| 249 | - | github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE= | |
| 250 | - | github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU= | |
| 251 | - | github.com/dgraph-io/ristretto/v2 v2.4.0 h1:I/w09yLjhdcVD2QV192UJcq8dPBaAJb9pOuMyNy0XlU= | |
| 252 | - | github.com/dgraph-io/ristretto/v2 v2.4.0/go.mod h1:0KsrXtXvnv0EqnzyowllbVJB8yBonswa2lTCK2gGo9E= | |
| 253 | - | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= | |
| 254 | - | github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= | |
| 255 | - | github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= | |
| 256 | - | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= | |
| 257 | - | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= | |
| 258 | 70 | github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= | |
| 259 | 71 | github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= | |
| 260 | 72 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= |
| ... | ... | @@ -269,8 +81,6 @@ github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pM | |
| 269 | 81 | github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= | |
| 270 | 82 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= | |
| 271 | 83 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= | |
| 272 | - | github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ= | |
| 273 | - | github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4= | |
| 274 | 84 | github.com/dsoprea/go-exif v0.0.0-20230826092837-6579e82b732d h1:ygcRCGNKuEiA98k7X35hknEN8RIRUF1jrz7k1rZCvsk= | |
| 275 | 85 | github.com/dsoprea/go-exif v0.0.0-20230826092837-6579e82b732d/go.mod h1:lOaOt7+UEppOgyvRy749v3do836U/hw0YVJNjoyPaEs= | |
| 276 | 86 | github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E= |
| ... | ... | @@ -295,40 +105,24 @@ github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3 | |
| 295 | 105 | github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 h1:/py11NlxDaOxkT9OKN+gXgT+QOH5xj1ZRoyusfRIlo4= | |
| 296 | 106 | github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349/go.mod h1:KVK+/Hul09ujXAGq+42UBgCTnXkiJZRnLYdURGjQUwo= | |
| 297 | 107 | github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU= | |
| 298 | - | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= | |
| 299 | 108 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= | |
| 300 | 109 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= | |
| 301 | 110 | github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= | |
| 302 | 111 | github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= | |
| 303 | - | github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84= | |
| 304 | - | github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= | |
| 305 | 112 | github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk= | |
| 306 | 113 | github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= | |
| 307 | 114 | github.com/emersion/go-smtp v0.24.0 h1:g6AfoF140mvW0vLNPD/LuCBLEAdlxOjIXqbIkJIS6Wk= | |
| 308 | 115 | github.com/emersion/go-smtp v0.24.0/go.mod h1:ZtRRkbTyp2XTHCA+BmyTFTrj8xY4I+b4McvHxCU2gsQ= | |
| 309 | - | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= | |
| 310 | 116 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= | |
| 311 | 117 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= | |
| 312 | - | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= | |
| 313 | 118 | github.com/forPelevin/gomoji v1.4.1 h1:7U+Bl8o6RV/dOQz7coQFWj/jX6Ram6/cWFOuFDEPEUo= | |
| 314 | 119 | github.com/forPelevin/gomoji v1.4.1/go.mod h1:mM6GtmCgpoQP2usDArc6GjbXrti5+FffolyQfGgPboQ= | |
| 315 | - | github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= | |
| 316 | - | github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= | |
| 317 | - | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= | |
| 318 | - | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | |
| 319 | - | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | |
| 320 | - | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= | |
| 321 | - | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= | |
| 322 | - | github.com/gammazero/deque v1.2.0 h1:scEFO8Uidhw6KDU5qg1HA5fYwM0+us2qdeJqm43bitU= | |
| 323 | - | github.com/gammazero/deque v1.2.0/go.mod h1:JVrR+Bj1NMQbPnYclvDlvSX0nVGReLrQZ0aUMuWLctg= | |
| 324 | - | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= | |
| 325 | 120 | github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= | |
| 326 | 121 | github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= | |
| 327 | 122 | github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= | |
| 328 | 123 | github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= | |
| 329 | 124 | github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= | |
| 330 | 125 | github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= | |
| 331 | - | github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= | |
| 332 | 126 | github.com/go-andiamo/splitter v1.2.5 h1:P3NovWMY2V14TJJSolXBvlOmGSZo3Uz+LtTl2bsV/eY= | |
| 333 | 127 | github.com/go-andiamo/splitter v1.2.5/go.mod h1:8WHU24t9hcMKU5FXDQb1hysSEC/GPuivIp0uKY1J8gw= | |
| 334 | 128 | github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= |
| ... | ... | @@ -336,16 +130,6 @@ github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWE | |
| 336 | 130 | github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= | |
| 337 | 131 | github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= | |
| 338 | 132 | github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= | |
| 339 | - | github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= | |
| 340 | - | github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= | |
| 341 | - | github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= | |
| 342 | - | github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= | |
| 343 | - | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= | |
| 344 | - | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= | |
| 345 | - | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= | |
| 346 | - | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= | |
| 347 | - | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= | |
| 348 | - | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= | |
| 349 | 133 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= | |
| 350 | 134 | github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= | |
| 351 | 135 | github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= |
| ... | ... | @@ -354,13 +138,9 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre | |
| 354 | 138 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= | |
| 355 | 139 | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= | |
| 356 | 140 | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= | |
| 357 | - | github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= | |
| 358 | - | github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= | |
| 359 | 141 | github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= | |
| 360 | 142 | github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= | |
| 361 | 143 | github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= | |
| 362 | - | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= | |
| 363 | - | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= | |
| 364 | 144 | github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= | |
| 365 | 145 | github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY= | |
| 366 | 146 | github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= |
| ... | ... | @@ -368,135 +148,32 @@ github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= | |
| 368 | 148 | github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= | |
| 369 | 149 | github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= | |
| 370 | 150 | github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= | |
| 371 | - | github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= | |
| 372 | - | github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= | |
| 373 | - | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= | |
| 374 | - | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= | |
| 375 | - | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= | |
| 376 | 151 | github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= | |
| 377 | 152 | github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= | |
| 378 | 153 | github.com/golang/geo v0.0.0-20260129164528-943061e2742c h1:ysO2h2Odnl1AJM1I2Lm/fa6JvO0pECMSt2CwBaa+ITo= | |
| 379 | 154 | github.com/golang/geo v0.0.0-20260129164528-943061e2742c/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8= | |
| 380 | - | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | |
| 381 | - | github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= | |
| 382 | - | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | |
| 383 | - | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | |
| 384 | - | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | |
| 385 | - | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | |
| 386 | - | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | |
| 387 | - | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | |
| 388 | - | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | |
| 389 | - | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | |
| 390 | - | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | |
| 391 | - | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | |
| 392 | - | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | |
| 393 | - | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | |
| 394 | - | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | |
| 395 | - | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | |
| 396 | - | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= | |
| 397 | - | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= | |
| 398 | - | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | |
| 399 | - | github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= | |
| 400 | - | github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | |
| 401 | - | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | |
| 402 | - | github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= | |
| 403 | - | github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= | |
| 404 | - | github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= | |
| 405 | - | github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= | |
| 406 | - | github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo= | |
| 407 | - | github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k= | |
| 408 | - | github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs= | |
| 409 | - | github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= | |
| 410 | - | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | |
| 411 | - | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | |
| 412 | - | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | |
| 413 | - | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |
| 414 | - | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |
| 415 | - | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |
| 416 | - | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | |
| 417 | 155 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | |
| 418 | 156 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= | |
| 419 | 157 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= | |
| 420 | - | github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= | |
| 421 | - | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= | |
| 422 | - | github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= | |
| 423 | - | github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= | |
| 424 | - | github.com/google/go-tpm-tools v0.4.7 h1:J3ycC8umYxM9A4eF73EofRZu4BxY0jjQnUnkhIBbvws= | |
| 425 | - | github.com/google/go-tpm-tools v0.4.7/go.mod h1:gSyXTZHe3fgbzb6WEGd90QucmsnT1SRdlye82gH8QjQ= | |
| 426 | - | github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= | |
| 427 | - | github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= | |
| 428 | 158 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | |
| 429 | - | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | |
| 430 | - | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | |
| 431 | - | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | |
| 432 | 159 | github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef h1:xpF9fUHpoIrrjX24DURVKiwHcFpw19ndIs+FwTSMbno= | |
| 433 | 160 | github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= | |
| 434 | 161 | github.com/google/renameio/v2 v2.0.2 h1:qKZs+tfn+arruZZhQ7TKC/ergJunuJicWS6gLDt/dGw= | |
| 435 | 162 | github.com/google/renameio/v2 v2.0.2/go.mod h1:OX+G6WHHpHq3NVj7cAOleLOwJfcQ1s3uUJQCrr78SWo= | |
| 436 | - | github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= | |
| 437 | - | github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= | |
| 438 | 163 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | |
| 439 | 164 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | |
| 440 | - | github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao= | |
| 441 | - | github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8= | |
| 442 | - | github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= | |
| 443 | - | github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= | |
| 444 | - | github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc= | |
| 445 | - | github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY= | |
| 446 | - | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | |
| 447 | 165 | github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= | |
| 448 | 166 | github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= | |
| 449 | 167 | github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc= | |
| 450 | 168 | github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= | |
| 451 | 169 | github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= | |
| 452 | 170 | github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | |
| 453 | - | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= | |
| 454 | - | github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= | |
| 455 | 171 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= | |
| 456 | 172 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= | |
| 457 | - | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | |
| 458 | - | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= | |
| 459 | - | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | |
| 460 | - | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | |
| 461 | - | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= | |
| 462 | - | github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= | |
| 463 | - | github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= | |
| 464 | - | github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY= | |
| 465 | - | github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= | |
| 466 | - | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= | |
| 467 | - | github.com/hashicorp/go-msgpack/v2 v2.1.5 h1:Ue879bPnutj/hXfmUk6s/jtIK90XxgiUIcXRl656T44= | |
| 468 | - | github.com/hashicorp/go-msgpack/v2 v2.1.5/go.mod h1:bjCsRXpZ7NsJdk45PoCQnzRGDaK8TKm5ZnDI/9y3J4M= | |
| 469 | - | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= | |
| 470 | - | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= | |
| 471 | - | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= | |
| 472 | - | github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= | |
| 473 | - | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= | |
| 474 | - | github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= | |
| 475 | - | github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= | |
| 476 | - | github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= | |
| 477 | - | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= | |
| 478 | - | github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= | |
| 479 | - | github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= | |
| 480 | - | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | |
| 481 | - | github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= | |
| 482 | - | github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= | |
| 483 | 173 | github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= | |
| 484 | 174 | github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= | |
| 485 | - | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= | |
| 486 | - | github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= | |
| 487 | - | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= | |
| 488 | - | github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= | |
| 489 | - | github.com/hashicorp/memberlist v0.5.4 h1:40YY+3qq2tAUhZIMEK8kqusKZBBjdwJ3NUjvYkcxh74= | |
| 490 | - | github.com/hashicorp/memberlist v0.5.4/go.mod h1:OgN6xiIo6RlHUWk+ALjP9e32xWCoQrsOCmHrWCm2MWA= | |
| 491 | 175 | github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= | |
| 492 | 176 | github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= | |
| 493 | - | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | |
| 494 | - | github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= | |
| 495 | - | github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= | |
| 496 | - | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= | |
| 497 | - | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= | |
| 498 | - | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= | |
| 499 | - | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | |
| 500 | 177 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= | |
| 501 | 178 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= | |
| 502 | 179 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= |
| ... | ... | @@ -505,41 +182,17 @@ github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= | |
| 505 | 182 | github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= | |
| 506 | 183 | github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= | |
| 507 | 184 | github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= | |
| 508 | - | github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= | |
| 509 | - | github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY= | |
| 510 | - | github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4= | |
| 511 | 185 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= | |
| 512 | 186 | github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= | |
| 513 | 187 | github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= | |
| 514 | - | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= | |
| 515 | - | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= | |
| 516 | - | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= | |
| 517 | - | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= | |
| 518 | - | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= | |
| 519 | 188 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | |
| 520 | 189 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | |
| 521 | - | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= | |
| 522 | - | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= | |
| 523 | - | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= | |
| 524 | - | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= | |
| 525 | - | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | |
| 526 | - | github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= | |
| 527 | 190 | github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= | |
| 528 | 191 | github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= | |
| 529 | - | github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= | |
| 530 | - | github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= | |
| 531 | - | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | |
| 532 | - | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | |
| 533 | 192 | github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= | |
| 534 | 193 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= | |
| 535 | - | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= | |
| 536 | - | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | |
| 537 | - | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= | |
| 538 | 194 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | |
| 539 | 195 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= | |
| 540 | - | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | |
| 541 | - | github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | |
| 542 | - | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | |
| 543 | 196 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | |
| 544 | 197 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | |
| 545 | 198 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= |
| ... | ... | @@ -547,25 +200,14 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ | |
| 547 | 200 | github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= | |
| 548 | 201 | github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI= | |
| 549 | 202 | github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= | |
| 550 | - | github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U= | |
| 551 | - | github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= | |
| 552 | 203 | github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= | |
| 553 | 204 | github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= | |
| 554 | - | github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= | |
| 555 | - | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= | |
| 556 | 205 | github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= | |
| 557 | 206 | github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= | |
| 558 | - | github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= | |
| 559 | - | github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= | |
| 560 | - | github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= | |
| 561 | 207 | github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= | |
| 562 | 208 | github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= | |
| 563 | 209 | github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= | |
| 564 | 210 | github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= | |
| 565 | - | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= | |
| 566 | - | github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= | |
| 567 | - | github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= | |
| 568 | - | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= | |
| 569 | 211 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | |
| 570 | 212 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | |
| 571 | 213 | github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= |
| ... | ... | @@ -575,32 +217,10 @@ github.com/mattn/go-sixel v0.0.8 h1:H0bBGQVOJoSvzvtTgCInxvg1IZiNlTcIIIx8A6uvjpQ= | |
| 575 | 217 | github.com/mattn/go-sixel v0.0.8/go.mod h1:wbDSbrwpykVI1qEHyjZYsDgaJTwpVg9wSwmmh2slnBw= | |
| 576 | 218 | github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= | |
| 577 | 219 | github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= | |
| 578 | - | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= | |
| 579 | - | github.com/maypok86/otter v1.2.4 h1:HhW1Pq6VdJkmWwcZZq19BlEQkHtI8xgsQzBVXJU0nfc= | |
| 580 | - | github.com/maypok86/otter v1.2.4/go.mod h1:mKLfoI7v1HOmQMwFgX4QkRk23mX6ge3RDvjdHOWG4R4= | |
| 581 | 220 | github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= | |
| 582 | 221 | github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o= | |
| 583 | - | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= | |
| 584 | - | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= | |
| 585 | - | github.com/mholt/acmez/v3 v3.1.4 h1:DyzZe/RnAzT3rpZj/2Ii5xZpiEvvYk3cQEN/RmqxwFQ= | |
| 586 | - | github.com/mholt/acmez/v3 v3.1.4/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= | |
| 587 | - | github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= | |
| 588 | 222 | github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= | |
| 589 | 223 | github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= | |
| 590 | - | github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= | |
| 591 | - | github.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= | |
| 592 | - | github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= | |
| 593 | - | github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= | |
| 594 | - | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= | |
| 595 | - | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= | |
| 596 | - | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= | |
| 597 | - | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | |
| 598 | - | github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= | |
| 599 | - | github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= | |
| 600 | - | github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= | |
| 601 | - | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= | |
| 602 | - | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= | |
| 603 | - | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= | |
| 604 | 224 | github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4= | |
| 605 | 225 | github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE= | |
| 606 | 226 | github.com/mmcdole/goxpp v1.1.1 h1:RGIX+D6iQRIunGHrKqnA2+700XMCnNv0bAOOv5MUhx8= |
| ... | ... | @@ -626,71 +246,27 @@ github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3 | |
| 626 | 246 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | |
| 627 | 247 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | |
| 628 | 248 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | |
| 629 | - | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= | |
| 630 | - | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= | |
| 631 | 249 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= | |
| 632 | 250 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | |
| 633 | 251 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= | |
| 634 | 252 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= | |
| 635 | - | github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= | |
| 636 | - | github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= | |
| 637 | 253 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= | |
| 638 | 254 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= | |
| 639 | - | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= | |
| 640 | - | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= | |
| 641 | - | github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U= | |
| 642 | - | github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g= | |
| 643 | - | github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4= | |
| 644 | - | github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs= | |
| 645 | - | github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= | |
| 646 | - | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= | |
| 647 | 255 | github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= | |
| 648 | 256 | github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= | |
| 649 | - | github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= | |
| 650 | - | github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= | |
| 651 | 257 | github.com/neurosnap/go-exif-remove v0.0.0-20221010134343-50d1e3c35577 h1:hVmVNttSLNloGsbFKVXAUHonXTd8KKrv30U/8UkloKI= | |
| 652 | 258 | github.com/neurosnap/go-exif-remove v0.0.0-20221010134343-50d1e3c35577/go.mod h1:G3Cu1AW+dmRLDFpOi8eUAfc3cGoRHUjTkGjeRcndgl4= | |
| 653 | 259 | github.com/neurosnap/go-jpeg-image-structure v0.0.0-20221010133817-70b1c1ff679e h1:76Dng5ms0fR+26doKZAvNqhi2UPfnLxGfPIDEr+BBlM= | |
| 654 | 260 | github.com/neurosnap/go-jpeg-image-structure v0.0.0-20221010133817-70b1c1ff679e/go.mod h1:nZBDA7+RD63GDJwjZmxhxac65MJqiCIHUUUvdYOsFkk= | |
| 655 | - | github.com/nutsdb/nutsdb v1.1.0 h1:fNGFzBHGqF2mB5BF8Qk8W94c3/ZzwdCdKAH7azwx70Y= | |
| 656 | - | github.com/nutsdb/nutsdb v1.1.0/go.mod h1:aKCtgSprZf2Mp1dIQD00Iya3DttoTErSSOnRx5ZtpAs= | |
| 657 | - | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= | |
| 658 | - | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= | |
| 659 | - | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= | |
| 660 | - | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | |
| 661 | - | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= | |
| 662 | - | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= | |
| 663 | - | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= | |
| 664 | - | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= | |
| 665 | - | github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= | |
| 666 | - | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= | |
| 667 | - | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= | |
| 668 | - | github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= | |
| 669 | - | github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= | |
| 670 | - | github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q= | |
| 671 | - | github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= | |
| 672 | 261 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= | |
| 673 | 262 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= | |
| 674 | 263 | github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= | |
| 675 | 264 | github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= | |
| 676 | - | github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= | |
| 677 | - | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= | |
| 678 | - | github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= | |
| 679 | - | github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= | |
| 680 | - | github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= | |
| 681 | - | github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= | |
| 682 | - | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= | |
| 683 | - | github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU= | |
| 684 | - | github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o= | |
| 685 | 265 | github.com/picosh/go-rsync-receiver v0.0.0-20250304201040-fcc11dd22d79 h1:MyB9P43hlQ6A2FoP9LGeiTBL3WKToW4gcWd6lQPg/Zg= | |
| 686 | 266 | github.com/picosh/go-rsync-receiver v0.0.0-20250304201040-fcc11dd22d79/go.mod h1:4ZICsr6bESoHP8He9DqROlZiMw4hHHjcbDzhtTTDQzA= | |
| 687 | 267 | github.com/picosh/utils v0.0.0-20260125160622-5c3a9e231ec6 h1:9KfCtfcx7vrSyGU1K9whdE1crll9Aq+nAZ6c0FzuzvE= | |
| 688 | 268 | github.com/picosh/utils v0.0.0-20260125160622-5c3a9e231ec6/go.mod h1:HogYEyJ43IGXrOa3D/kjM1pkzNAyh+pejRyv8Eo//pk= | |
| 689 | - | github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0= | |
| 690 | - | github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= | |
| 691 | 269 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= | |
| 692 | - | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | |
| 693 | - | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | |
| 694 | 270 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | |
| 695 | 271 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | |
| 696 | 272 | github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU= |
| ... | ... | @@ -698,47 +274,16 @@ github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1Hbe | |
| 698 | 274 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | |
| 699 | 275 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= | |
| 700 | 276 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | |
| 701 | - | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= | |
| 702 | 277 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= | |
| 703 | 278 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= | |
| 704 | - | github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k= | |
| 705 | - | github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= | |
| 706 | - | github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= | |
| 707 | - | github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= | |
| 708 | - | github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= | |
| 709 | - | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= | |
| 710 | - | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= | |
| 711 | - | github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= | |
| 712 | - | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= | |
| 713 | - | github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= | |
| 714 | 279 | github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= | |
| 715 | 280 | github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= | |
| 716 | - | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | |
| 717 | - | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | |
| 718 | - | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | |
| 719 | 281 | github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= | |
| 720 | 282 | github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= | |
| 721 | - | github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= | |
| 722 | - | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= | |
| 723 | - | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= | |
| 724 | - | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= | |
| 725 | - | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= | |
| 726 | 283 | github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= | |
| 727 | 284 | github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= | |
| 728 | - | github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= | |
| 729 | - | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= | |
| 730 | - | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= | |
| 731 | - | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= | |
| 732 | - | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= | |
| 733 | - | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= | |
| 734 | 285 | github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= | |
| 735 | 286 | github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= | |
| 736 | - | github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= | |
| 737 | - | github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= | |
| 738 | - | github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= | |
| 739 | - | github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= | |
| 740 | - | github.com/redis/rueidis v1.0.71 h1:pODtnAR5GAB7j4ekhldZ29HKOxe4Hph0GTDGk1ayEQY= | |
| 741 | - | github.com/redis/rueidis v1.0.71/go.mod h1:lfdcZzJ1oKGKL37vh9fO3ymwt+0TdjkkUCJxbgpmcgQ= | |
| 742 | 287 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= | |
| 743 | 288 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= | |
| 744 | 289 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= |
| ... | ... | @@ -747,125 +292,29 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc | |
| 747 | 292 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= | |
| 748 | 293 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= | |
| 749 | 294 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= | |
| 750 | - | github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= | |
| 751 | - | github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= | |
| 752 | - | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= | |
| 753 | - | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= | |
| 754 | - | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | |
| 755 | - | github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= | |
| 756 | 295 | github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= | |
| 757 | 296 | github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= | |
| 758 | - | github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E= | |
| 759 | - | github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg= | |
| 760 | 297 | github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= | |
| 761 | - | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= | |
| 762 | - | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= | |
| 763 | - | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= | |
| 764 | 298 | github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs= | |
| 765 | 299 | github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= | |
| 766 | - | github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= | |
| 767 | - | github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= | |
| 768 | - | github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= | |
| 769 | - | github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= | |
| 770 | - | github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= | |
| 771 | - | github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= | |
| 772 | - | github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= | |
| 773 | - | github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= | |
| 774 | - | github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= | |
| 775 | - | github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= | |
| 776 | - | github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= | |
| 777 | - | github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= | |
| 778 | - | github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= | |
| 779 | - | github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= | |
| 780 | - | github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= | |
| 781 | - | github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= | |
| 782 | - | github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= | |
| 783 | - | github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= | |
| 784 | - | github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= | |
| 785 | - | github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= | |
| 786 | - | github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= | |
| 787 | - | github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | |
| 788 | - | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= | |
| 789 | - | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | |
| 790 | - | github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= | |
| 791 | - | github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= | |
| 792 | 300 | github.com/simplesurance/go-ip-anonymizer v0.0.0-20200429124537-35a880f8e87d h1:4FkGkGts6gLznca6fgclIvbupwbq543mb/fFkog4VIg= | |
| 793 | 301 | github.com/simplesurance/go-ip-anonymizer v0.0.0-20200429124537-35a880f8e87d/go.mod h1:fTTj1EOmRdtuwYw3jF/1X2dTa0N1BdbZhrpA21N/S4I= | |
| 794 | - | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= | |
| 795 | - | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= | |
| 796 | - | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= | |
| 797 | 302 | github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= | |
| 798 | 303 | github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= | |
| 799 | - | github.com/slackhq/nebula v1.9.5 h1:ZrxcvP/lxwFglaijmiwXLuCSkybZMJnqSYI1S8DtGnY= | |
| 800 | - | github.com/slackhq/nebula v1.9.5/go.mod h1:1+4q4wd3dDAjO8rKCttSb9JIVbklQhuJiBp5I0lbIsQ= | |
| 801 | - | github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= | |
| 802 | - | github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= | |
| 803 | - | github.com/smallstep/certificates v0.28.4 h1:JTU6/A5Xes6m+OsR6fw1RACSA362vJc9SOFVG7poBEw= | |
| 804 | - | github.com/smallstep/certificates v0.28.4/go.mod h1:LUqo+7mKZE7FZldlTb0zhU4A0bq4G4+akieFMcTaWvA= | |
| 805 | - | github.com/smallstep/cli-utils v0.12.1 h1:D9QvfbFqiKq3snGZ2xDcXEFrdFJ1mQfPHZMq/leerpE= | |
| 806 | - | github.com/smallstep/cli-utils v0.12.1/go.mod h1:skV2Neg8qjiKPu2fphM89H9bIxNpKiiRTnX9Q6Lc+20= | |
| 807 | - | github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca h1:VX8L0r8vybH0bPeaIxh4NQzafKQiqvlOn8pmOXbFLO4= | |
| 808 | - | github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4= | |
| 809 | - | github.com/smallstep/linkedca v0.23.0 h1:5W/7EudlK1HcCIdZM68dJlZ7orqCCCyv6bm2l/0JmLU= | |
| 810 | - | github.com/smallstep/linkedca v0.23.0/go.mod h1:7cyRM9soAYySg9ag65QwytcgGOM+4gOlkJ/YA58A9E8= | |
| 811 | - | github.com/smallstep/nosql v0.7.0 h1:YiWC9ZAHcrLCrayfaF+QJUv16I2bZ7KdLC3RpJcnAnE= | |
| 812 | - | github.com/smallstep/nosql v0.7.0/go.mod h1:H5VnKMCbeq9QA6SRY5iqPylfxLfYcLwvUff3onQ8+HU= | |
| 813 | - | github.com/smallstep/pkcs7 v0.2.1 h1:6Kfzr/QizdIuB6LSv8y1LJdZ3aPSfTNhTLqAx9CTLfA= | |
| 814 | - | github.com/smallstep/pkcs7 v0.2.1/go.mod h1:RcXHsMfL+BzH8tRhmrF1NkkpebKpq3JEM66cOFxanf0= | |
| 815 | - | github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492 h1:k23+s51sgYix4Zgbvpmy+1ZgXLjr4ZTkBTqXmpnImwA= | |
| 816 | - | github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492/go.mod h1:QQhwLqCS13nhv8L5ov7NgusowENUtXdEzdytjmJHdZQ= | |
| 817 | - | github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4= | |
| 818 | - | github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A= | |
| 819 | 304 | github.com/soniakeys/quant v1.0.0 h1:N1um9ktjbkZVcywBVAAYpZYSHxEfJGzshHCxx/DaI0Y= | |
| 820 | 305 | github.com/soniakeys/quant v1.0.0/go.mod h1:HI1k023QuVbD4H8i9YdfZP2munIHU4QpjsImz6Y6zds= | |
| 821 | - | github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= | |
| 822 | - | github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= | |
| 823 | - | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= | |
| 824 | - | github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= | |
| 825 | - | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= | |
| 826 | - | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= | |
| 827 | - | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= | |
| 828 | - | github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= | |
| 829 | - | github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= | |
| 830 | - | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= | |
| 831 | - | github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= | |
| 832 | - | github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= | |
| 833 | - | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= | |
| 834 | - | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= | |
| 835 | - | github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | |
| 836 | - | github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= | |
| 837 | - | github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | |
| 838 | - | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= | |
| 839 | 306 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | |
| 840 | - | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | |
| 841 | - | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | |
| 842 | - | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | |
| 843 | 307 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= | |
| 844 | 308 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= | |
| 845 | - | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | |
| 846 | 309 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | |
| 847 | - | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | |
| 848 | - | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | |
| 849 | 310 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |
| 850 | 311 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |
| 851 | - | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |
| 852 | - | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | |
| 853 | - | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | |
| 854 | - | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | |
| 855 | 312 | github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= | |
| 856 | 313 | github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= | |
| 857 | - | github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4= | |
| 858 | - | github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg= | |
| 859 | - | github.com/tailscale/tscert v0.0.0-20251216020129-aea342f6d747 h1:RnBbFMmodYzhC6adOjTbtUQXyzV8dcvKYbolzs6Qch0= | |
| 860 | - | github.com/tailscale/tscert v0.0.0-20251216020129-aea342f6d747/go.mod h1:ejPAJui3kVK4u5TgMtqtXlWf5HnKh9fLy5kvpaeuas0= | |
| 861 | - | github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= | |
| 862 | 314 | github.com/testcontainers/testcontainers-go v0.40.0 h1:pSdJYLOVgLE8YdUY2FHQ1Fxu+aMnb6JfVz1mxk7OeMU= | |
| 863 | 315 | github.com/testcontainers/testcontainers-go v0.40.0/go.mod h1:FSXV5KQtX2HAMlm7U3APNyLkkap35zNLxukw9oBi/MY= | |
| 864 | 316 | github.com/testcontainers/testcontainers-go/modules/postgres v0.40.0 h1:s2bIayFXlbDFexo96y+htn7FzuhpXLYJNnIuglNKqOk= | |
| 865 | 317 | github.com/testcontainers/testcontainers-go/modules/postgres v0.40.0/go.mod h1:h+u/2KoREGTnTl9UwrQ/g+XhasAT8E6dClclAADeXoQ= | |
| 866 | - | github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= | |
| 867 | - | github.com/tidwall/btree v1.8.1 h1:27ehoXvm5AG/g+1VxLS1SD3vRhp/H7LuEfwNvddEdmA= | |
| 868 | - | github.com/tidwall/btree v1.8.1/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= | |
| 869 | 318 | github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= | |
| 870 | 319 | github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= | |
| 871 | 320 | github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= |
| ... | ... | @@ -875,33 +324,14 @@ github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT | |
| 875 | 324 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= | |
| 876 | 325 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= | |
| 877 | 326 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= | |
| 878 | - | github.com/tidwall/redcon v1.6.2 h1:5qfvrrybgtO85jnhSravmkZyC0D+7WstbfCs3MmPhow= | |
| 879 | - | github.com/tidwall/redcon v1.6.2/go.mod h1:p5Wbsgeyi2VSTBWOcA5vRXrOb9arFTcU2+ZzFjqV75Y= | |
| 880 | 327 | github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= | |
| 881 | 328 | github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= | |
| 882 | 329 | github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= | |
| 883 | 330 | github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= | |
| 884 | 331 | github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= | |
| 885 | 332 | github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= | |
| 886 | - | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= | |
| 887 | - | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= | |
| 888 | - | github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ= | |
| 889 | - | github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo= | |
| 890 | - | github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= | |
| 891 | - | github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= | |
| 892 | - | github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= | |
| 893 | - | github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= | |
| 894 | - | github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= | |
| 895 | - | github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= | |
| 896 | - | github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= | |
| 897 | 333 | github.com/x-way/crawlerdetect v0.2.30 h1:U43R8+TZ7AZwBZehWRPdRdW53NmPoVZSOptevJKo1mE= | |
| 898 | 334 | github.com/x-way/crawlerdetect v0.2.30/go.mod h1:BPHLsB3FOuiwoWyhAvnqeiUSAEKd34O7BcsTCcxHRj4= | |
| 899 | - | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= | |
| 900 | - | github.com/xujiajun/utils v0.0.0-20220904132955-5f7c5b914235 h1:w0si+uee0iAaCJO9q86T6yrhdadgcsoNuh47LrUykzg= | |
| 901 | - | github.com/xujiajun/utils v0.0.0-20220904132955-5f7c5b914235/go.mod h1:MR4+0R6A9NS5IABnIM3384FfOq8QFVnm7WDrBOhIaMU= | |
| 902 | - | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | |
| 903 | - | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | |
| 904 | - | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | |
| 905 | 335 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | |
| 906 | 336 | github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | |
| 907 | 337 | github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE= |
| ... | ... | @@ -912,31 +342,14 @@ github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUei | |
| 912 | 342 | github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= | |
| 913 | 343 | github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= | |
| 914 | 344 | github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= | |
| 915 | - | github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= | |
| 916 | - | github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= | |
| 917 | - | github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= | |
| 918 | - | github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= | |
| 919 | - | github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= | |
| 920 | - | github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= | |
| 921 | 345 | go.abhg.dev/goldmark/anchor v0.2.0 h1:RQZTodRc6VHSUoQYKFlyH0pokbhk1klwUuGgDmjGp2E= | |
| 922 | 346 | go.abhg.dev/goldmark/anchor v0.2.0/go.mod h1:Ym74zBV+QBKxK9ITOty680N9FT8otgGYvtYXroJUWms= | |
| 923 | 347 | go.abhg.dev/goldmark/hashtag v0.4.0 h1:jkYcsIRUpog/rhWMnIOjj14qj8p5p4um+IzPbPUxFww= | |
| 924 | 348 | go.abhg.dev/goldmark/hashtag v0.4.0/go.mod h1:5DQwSo2dEHKtsfXOC1+Po7WgVwR5IoGjgeVIerddz9g= | |
| 925 | 349 | go.abhg.dev/goldmark/toc v0.12.0 h1:kiEBBIOB7jEzNpXmGdiL2L/zGSELKw/p3mosm2+RSuo= | |
| 926 | 350 | go.abhg.dev/goldmark/toc v0.12.0/go.mod h1:kskbM5l9y8wOFEFfyEe9wnwhWeykvmHB6xEPCVrZIvg= | |
| 927 | - | go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= | |
| 928 | - | go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= | |
| 929 | - | go.etcd.io/etcd/api/v3 v3.6.7 h1:7BNJ2gQmc3DNM+9cRkv7KkGQDayElg8x3X+tFDYS+E0= | |
| 930 | - | go.etcd.io/etcd/api/v3 v3.6.7/go.mod h1:xJ81TLj9hxrYYEDmXTeKURMeY3qEDN24hqe+q7KhbnI= | |
| 931 | - | go.etcd.io/etcd/client/pkg/v3 v3.6.7 h1:vvzgyozz46q+TyeGBuFzVuI53/yd133CHceNb/AhBVs= | |
| 932 | - | go.etcd.io/etcd/client/pkg/v3 v3.6.7/go.mod h1:2IVulJ3FZ/czIGl9T4lMF1uxzrhRahLqe+hSgy+Kh7Q= | |
| 933 | - | go.etcd.io/etcd/client/v3 v3.6.7 h1:9WqA5RpIBtdMxAy1ukXLAdtg2pAxNqW5NUoO2wQrE6U= | |
| 934 | - | go.etcd.io/etcd/client/v3 v3.6.7/go.mod h1:2XfROY56AXnUqGsvl+6k29wrwsSbEh1lAouQB1vHpeE= | |
| 935 | - | go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= | |
| 936 | 351 | go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= | |
| 937 | 352 | go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= | |
| 938 | - | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= | |
| 939 | - | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= | |
| 940 | 353 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= | |
| 941 | 354 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= | |
| 942 | 355 | go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= |
| ... | ... | @@ -955,57 +368,23 @@ go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZY | |
| 955 | 368 | go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= | |
| 956 | 369 | go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= | |
| 957 | 370 | go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= | |
| 958 | - | go.step.sm/crypto v0.76.0 h1:K23BSaeoiY7Y5dvvijTeYC9EduDBetNwQYMBwMhi1aA= | |
| 959 | - | go.step.sm/crypto v0.76.0/go.mod h1:PXYJdKkK8s+GHLwLguFaLxHNAFsFL3tL1vSBrYfey5k= | |
| 960 | - | go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= | |
| 961 | - | go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= | |
| 962 | 371 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= | |
| 963 | 372 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= | |
| 964 | - | go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= | |
| 965 | - | go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= | |
| 966 | - | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= | |
| 967 | - | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= | |
| 968 | - | go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= | |
| 969 | - | go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= | |
| 970 | - | go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= | |
| 971 | - | go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= | |
| 972 | 373 | go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= | |
| 973 | 374 | go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= | |
| 974 | - | go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= | |
| 975 | - | go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= | |
| 976 | - | go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= | |
| 977 | - | golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= | |
| 978 | - | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | |
| 979 | - | golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | |
| 980 | - | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | |
| 981 | 375 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | |
| 982 | - | golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | |
| 983 | - | golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= | |
| 984 | - | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |
| 985 | - | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | |
| 986 | 376 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | |
| 987 | 377 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= | |
| 988 | 378 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= | |
| 989 | 379 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= | |
| 990 | - | golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= | |
| 991 | 380 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= | |
| 992 | - | golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= | |
| 993 | 381 | golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= | |
| 994 | 382 | golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= | |
| 995 | - | golang.org/x/crypto/x509roots/fallback v0.0.0-20260113154411-7d0074ccc6f1 h1:EBHQuS9qI8xJ96+YRgVV2ahFLUYbWpt1rf3wPfXN2wQ= | |
| 996 | - | golang.org/x/crypto/x509roots/fallback v0.0.0-20260113154411-7d0074ccc6f1/go.mod h1:MEIPiCnxvQEjA4astfaKItNwEVZA5Ki+3+nyGbJ5N18= | |
| 997 | - | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | |
| 998 | 383 | golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= | |
| 999 | 384 | golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= | |
| 1000 | 385 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | |
| 1001 | 386 | golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I= | |
| 1002 | 387 | golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk= | |
| 1003 | - | golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | |
| 1004 | - | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | |
| 1005 | - | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= | |
| 1006 | - | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | |
| 1007 | - | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | |
| 1008 | - | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | |
| 1009 | 388 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | |
| 1010 | 389 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | |
| 1011 | 390 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= |
| ... | ... | @@ -1013,110 +392,40 @@ golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | |
| 1013 | 392 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | |
| 1014 | 393 | golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= | |
| 1015 | 394 | golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= | |
| 1016 | - | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |
| 1017 | - | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |
| 1018 | - | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |
| 1019 | - | golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |
| 1020 | - | golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |
| 1021 | - | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |
| 1022 | - | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |
| 1023 | - | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |
| 1024 | - | golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | |
| 1025 | - | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | |
| 1026 | - | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |
| 1027 | 395 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |
| 1028 | - | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |
| 1029 | 396 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |
| 1030 | - | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |
| 1031 | 397 | golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |
| 1032 | 398 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | |
| 1033 | 399 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | |
| 1034 | - | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | |
| 1035 | 400 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | |
| 1036 | - | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | |
| 1037 | - | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | |
| 1038 | 401 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | |
| 1039 | - | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= | |
| 1040 | - | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= | |
| 1041 | - | golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | |
| 1042 | 402 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | |
| 1043 | 403 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | |
| 1044 | 404 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= | |
| 1045 | 405 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= | |
| 1046 | 406 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= | |
| 1047 | 407 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= | |
| 1048 | - | golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= | |
| 1049 | 408 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= | |
| 1050 | 409 | golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= | |
| 1051 | 410 | golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= | |
| 1052 | - | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | |
| 1053 | - | golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | |
| 1054 | - | golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | |
| 1055 | - | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | |
| 1056 | - | golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= | |
| 1057 | - | golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= | |
| 1058 | - | golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= | |
| 1059 | - | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
| 1060 | - | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
| 1061 | - | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
| 1062 | - | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
| 1063 | 411 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
| 1064 | - | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
| 1065 | - | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
| 1066 | - | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
| 1067 | - | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
| 1068 | 412 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
| 1069 | 413 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
| 1070 | 414 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= | |
| 1071 | 415 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | |
| 1072 | 416 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | |
| 1073 | - | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | |
| 1074 | 417 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | |
| 1075 | - | golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | |
| 1076 | 418 | golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= | |
| 1077 | 419 | golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= | |
| 1078 | - | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 1079 | - | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 1080 | - | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 1081 | - | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 1082 | - | golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 1083 | - | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 1084 | - | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 1085 | - | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 1086 | 420 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 1087 | - | golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1088 | - | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1089 | - | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1090 | - | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1091 | - | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1092 | 421 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1093 | - | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1094 | - | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1095 | - | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1096 | - | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1097 | - | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1098 | - | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1099 | - | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1100 | 422 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1101 | - | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1102 | - | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1103 | - | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1104 | 423 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1105 | 424 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1106 | - | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1107 | - | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1108 | - | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1109 | - | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 1110 | - | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1111 | - | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1112 | 425 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1113 | 426 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1114 | - | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1115 | - | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1116 | - | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1117 | 427 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1118 | 428 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1119 | - | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1120 | 429 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1121 | 430 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1122 | 431 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
| ... | ... | @@ -1124,9 +433,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1124 | 433 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
| 1125 | 434 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | |
| 1126 | 435 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | |
| 1127 | - | golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | |
| 1128 | 436 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | |
| 1129 | - | golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | |
| 1130 | 437 | golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= | |
| 1131 | 438 | golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= | |
| 1132 | 439 | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= |
| ... | ... | @@ -1137,42 +444,24 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= | |
| 1137 | 444 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= | |
| 1138 | 445 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= | |
| 1139 | 446 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= | |
| 1140 | - | golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= | |
| 1141 | 447 | golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= | |
| 1142 | - | golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= | |
| 1143 | 448 | golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= | |
| 1144 | 449 | golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= | |
| 1145 | 450 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |
| 1146 | - | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |
| 1147 | - | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | |
| 1148 | 451 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | |
| 1149 | - | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | |
| 1150 | 452 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | |
| 1151 | 453 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | |
| 1152 | 454 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | |
| 1153 | 455 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= | |
| 1154 | 456 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= | |
| 1155 | 457 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= | |
| 1156 | - | golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= | |
| 1157 | 458 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= | |
| 1158 | - | golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= | |
| 1159 | 459 | golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= | |
| 1160 | 460 | golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= | |
| 1161 | - | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |
| 1162 | - | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |
| 1163 | 461 | golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= | |
| 1164 | 462 | golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= | |
| 1165 | - | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |
| 1166 | 463 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |
| 1167 | - | golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |
| 1168 | - | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |
| 1169 | - | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= | |
| 1170 | - | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |
| 1171 | 464 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |
| 1172 | - | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | |
| 1173 | - | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | |
| 1174 | - | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | |
| 1175 | - | golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | |
| 1176 | 465 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | |
| 1177 | 466 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= | |
| 1178 | 467 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= |
| ... | ... | @@ -1180,63 +469,17 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb | |
| 1180 | 469 | golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= | |
| 1181 | 470 | golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= | |
| 1182 | 471 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |
| 1183 | - | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |
| 1184 | - | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |
| 1185 | - | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |
| 1186 | - | gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= | |
| 1187 | - | gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= | |
| 1188 | - | google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= | |
| 1189 | - | google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= | |
| 1190 | - | google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= | |
| 1191 | - | google.golang.org/api v0.265.0 h1:FZvfUdI8nfmuNrE34aOWFPmLC+qRBEiNm3JdivTvAAU= | |
| 1192 | - | google.golang.org/api v0.265.0/go.mod h1:uAvfEl3SLUj/7n6k+lJutcswVojHPp2Sp08jWCu8hLY= | |
| 1193 | - | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | |
| 1194 | - | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | |
| 1195 | - | google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | |
| 1196 | - | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | |
| 1197 | - | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | |
| 1198 | - | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | |
| 1199 | - | google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | |
| 1200 | - | google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= | |
| 1201 | - | google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | |
| 1202 | - | google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM= | |
| 1203 | - | google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM= | |
| 1204 | 472 | google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= | |
| 1205 | 473 | google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= | |
| 1206 | 474 | google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= | |
| 1207 | 475 | google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= | |
| 1208 | - | google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= | |
| 1209 | - | google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= | |
| 1210 | - | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= | |
| 1211 | - | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | |
| 1212 | 476 | google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= | |
| 1213 | 477 | google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= | |
| 1214 | - | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1 h1:/WILD1UcXj/ujCxgoL/DvRgt2CP3txG8+FwkUbb9110= | |
| 1215 | - | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1/go.mod h1:YNKnb2OAApgYn2oYY47Rn7alMr1zWjb2U8Q0aoGWiNc= | |
| 1216 | - | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | |
| 1217 | - | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | |
| 1218 | - | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | |
| 1219 | - | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | |
| 1220 | - | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | |
| 1221 | - | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | |
| 1222 | - | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | |
| 1223 | - | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | |
| 1224 | 478 | google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= | |
| 1225 | 479 | google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= | |
| 1226 | - | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= | |
| 1227 | 480 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |
| 1228 | - | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |
| 1229 | 481 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= | |
| 1230 | 482 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= | |
| 1231 | - | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | |
| 1232 | - | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= | |
| 1233 | - | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= | |
| 1234 | - | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | |
| 1235 | - | gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= | |
| 1236 | - | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |
| 1237 | - | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |
| 1238 | - | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |
| 1239 | - | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |
| 1240 | 483 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |
| 1241 | 484 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |
| 1242 | 485 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= |
| ... | ... | @@ -1246,12 +489,6 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | |
| 1246 | 489 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |
| 1247 | 490 | gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= | |
| 1248 | 491 | gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= | |
| 1249 | - | grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= | |
| 1250 | - | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | |
| 1251 | - | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | |
| 1252 | - | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | |
| 1253 | - | howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= | |
| 1254 | - | howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= | |
| 1255 | 492 | modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= | |
| 1256 | 493 | modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= | |
| 1257 | 494 | modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc= |
| ... | ... | @@ -1284,5 +521,3 @@ mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= | |
| 1284 | 521 | mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= | |
| 1285 | 522 | pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= | |
| 1286 | 523 | pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= | |
| 1287 | - | sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= | |
| 1288 | - | sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= |
+0,
-5
| ... | ... | @@ -12,7 +12,6 @@ import ( | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | type PgsConfig struct { | |
| 15 | - | CacheControl string | |
| 16 | 15 | CacheTTL time.Duration | |
| 17 | 16 | Domain string | |
| 18 | 17 | MaxAssetSize int64 |
| ... | ... | @@ -75,15 +74,11 @@ func NewPgsConfig(logger *slog.Logger, dbpool pgsdb.PgsDB, st storage.StorageSer | |
| 75 | 74 | if err != nil { | |
| 76 | 75 | cacheTTL = 600 * time.Second | |
| 77 | 76 | } | |
| 78 | - | cacheControl := shared.GetEnv( | |
| 79 | - | "PGS_CACHE_CONTROL", | |
| 80 | - | fmt.Sprintf("max-age=%d", int(cacheTTL.Seconds()))) | |
| 81 | 77 | ||
| 82 | 78 | sshHost := shared.GetEnv("PGS_SSH_HOST", "0.0.0.0") | |
| 83 | 79 | sshPort := shared.GetEnv("PGS_SSH_PORT", "2222") | |
| 84 | 80 | ||
| 85 | 81 | cfg := PgsConfig{ | |
| 86 | - | CacheControl: cacheControl, | |
| 87 | 82 | CacheTTL: cacheTTL, | |
| 88 | 83 | Domain: domain, | |
| 89 | 84 | MaxAssetSize: maxAssetSize, |
+0,
-183
| ... | ... | @@ -1,183 +0,0 @@ | |
| 1 | - | package pgs | |
| 2 | - | ||
| 3 | - | import ( | |
| 4 | - | "bytes" | |
| 5 | - | "log/slog" | |
| 6 | - | "net/http" | |
| 7 | - | "net/http/httptest" | |
| 8 | - | "strings" | |
| 9 | - | "testing" | |
| 10 | - | ||
| 11 | - | "github.com/picosh/pico/pkg/shared" | |
| 12 | - | "github.com/picosh/pico/pkg/storage" | |
| 13 | - | ) | |
| 14 | - | ||
| 15 | - | // TestLargeFileNotTruncatedOnCacheHit reproduces the Souin truncation bug. | |
| 16 | - | // Large files (3MB) are cached as truncated (~4KB) on the second request. | |
| 17 | - | // | |
| 18 | - | // Bug behavior: | |
| 19 | - | // - First request (cache miss): Returns full 3MB file ✓ | |
| 20 | - | // - Second request (cache hit): Returns only ~4KB (truncated!) ✗ | |
| 21 | - | // | |
| 22 | - | // Root cause: Souin's Store() snapshots the buffer mid-stream while | |
| 23 | - | // io.Copy() is still writing data, resulting in partial cache entries. | |
| 24 | - | func TestLargeFileNotTruncatedOnCacheHit(t *testing.T) { | |
| 25 | - | logger := slog.Default() | |
| 26 | - | dbpool := NewPgsDb(logger) | |
| 27 | - | bucketName := shared.GetAssetBucketName(dbpool.Users[0].ID) | |
| 28 | - | ||
| 29 | - | // 3MB payload - reproduces the exact bug scenario | |
| 30 | - | largePayload := bytes.Repeat([]byte("x"), 3*1024*1024) | |
| 31 | - | expectedSize := len(largePayload) | |
| 32 | - | ||
| 33 | - | st, err := storage.NewStorageMemory(map[string]map[string]string{ | |
| 34 | - | bucketName: { | |
| 35 | - | "/test/large-file.bin": string(largePayload), | |
| 36 | - | }, | |
| 37 | - | }) | |
| 38 | - | if err != nil { | |
| 39 | - | t.Fatalf("storage setup failed: %v", err) | |
| 40 | - | } | |
| 41 | - | ||
| 42 | - | pubsub := NewPubsubChan() | |
| 43 | - | defer func() { | |
| 44 | - | _ = pubsub.Close() | |
| 45 | - | }() | |
| 46 | - | ||
| 47 | - | cfg := NewPgsConfig(logger, dbpool, st, pubsub) | |
| 48 | - | cfg.Domain = "pgs.test" | |
| 49 | - | // Increase max asset size for testing large files | |
| 50 | - | cfg.MaxAssetSize = 10 * 1024 * 1024 // 10MB | |
| 51 | - | ||
| 52 | - | // Set up the full stack WITH Souin HTTP caching middleware | |
| 53 | - | httpCache := SetupCache(cfg) | |
| 54 | - | routes := NewWebRouter(cfg) | |
| 55 | - | cacher := &CachedHttp{ | |
| 56 | - | handler: httpCache, | |
| 57 | - | routes: routes, | |
| 58 | - | } | |
| 59 | - | ||
| 60 | - | // First request (cache miss) | |
| 61 | - | req1 := httptest.NewRequest("GET", dbpool.mkpath("/large-file.bin"), strings.NewReader("")) | |
| 62 | - | rec1 := httptest.NewRecorder() | |
| 63 | - | cacher.ServeHTTP(rec1, req1) | |
| 64 | - | ||
| 65 | - | if rec1.Code != http.StatusOK { | |
| 66 | - | t.Fatalf("first request failed with status %d", rec1.Code) | |
| 67 | - | } | |
| 68 | - | ||
| 69 | - | body1 := rec1.Body.String() | |
| 70 | - | size1 := len(body1) | |
| 71 | - | ||
| 72 | - | if size1 != expectedSize { | |
| 73 | - | t.Errorf("first request: expected %d bytes, got %d bytes", expectedSize, size1) | |
| 74 | - | } | |
| 75 | - | ||
| 76 | - | t.Logf("Cache miss: received %d bytes (expected %d)", size1, expectedSize) | |
| 77 | - | ||
| 78 | - | // Second request (cache hit) - This is where the bug manifests | |
| 79 | - | req2 := httptest.NewRequest("GET", dbpool.mkpath("/large-file.bin"), strings.NewReader("")) | |
| 80 | - | rec2 := httptest.NewRecorder() | |
| 81 | - | cacher.ServeHTTP(rec2, req2) | |
| 82 | - | ||
| 83 | - | if rec2.Code != http.StatusOK { | |
| 84 | - | t.Fatalf("second request failed with status %d", rec2.Code) | |
| 85 | - | } | |
| 86 | - | ||
| 87 | - | body2 := rec2.Body.String() | |
| 88 | - | size2 := len(body2) | |
| 89 | - | ||
| 90 | - | t.Logf("Cache hit: received %d bytes (expected %d)", size2, expectedSize) | |
| 91 | - | ||
| 92 | - | // CRITICAL ASSERTION: Both requests must return the full file | |
| 93 | - | if size2 != expectedSize { | |
| 94 | - | t.Errorf("SOUIN_TRUNCATION_BUG: cache hit returned %d bytes instead of %d bytes", | |
| 95 | - | size2, expectedSize) | |
| 96 | - | ||
| 97 | - | // Show evidence of truncation | |
| 98 | - | if size2 < 10000 { | |
| 99 | - | t.Logf("Truncated response: only %d bytes (about %dKB)", size2, size2/1024) | |
| 100 | - | } | |
| 101 | - | } | |
| 102 | - | ||
| 103 | - | // Verify both responses are identical | |
| 104 | - | if body1 != body2 { | |
| 105 | - | t.Errorf("cache hit response differs from cache miss") | |
| 106 | - | t.Errorf("Cache miss: %d bytes, Cache hit: %d bytes", size1, size2) | |
| 107 | - | } | |
| 108 | - | } | |
| 109 | - | ||
| 110 | - | // TestMediumFileNotTruncatedOnCacheHit tests with 512KB files | |
| 111 | - | // which can trigger the race condition due to buffering behavior. | |
| 112 | - | func TestMediumFileNotTruncatedOnCacheHit(t *testing.T) { | |
| 113 | - | logger := slog.Default() | |
| 114 | - | dbpool := NewPgsDb(logger) | |
| 115 | - | bucketName := shared.GetAssetBucketName(dbpool.Users[0].ID) | |
| 116 | - | ||
| 117 | - | // 512KB payload with repeating pattern | |
| 118 | - | payload := bytes.Repeat([]byte("0123456789ABCDEF"), 512*1024/16) | |
| 119 | - | expectedSize := len(payload) | |
| 120 | - | ||
| 121 | - | st, err := storage.NewStorageMemory(map[string]map[string]string{ | |
| 122 | - | bucketName: { | |
| 123 | - | "/test/medium-file.bin": string(payload), | |
| 124 | - | }, | |
| 125 | - | }) | |
| 126 | - | if err != nil { | |
| 127 | - | t.Fatalf("storage setup failed: %v", err) | |
| 128 | - | } | |
| 129 | - | ||
| 130 | - | pubsub := NewPubsubChan() | |
| 131 | - | defer func() { | |
| 132 | - | _ = pubsub.Close() | |
| 133 | - | }() | |
| 134 | - | ||
| 135 | - | cfg := NewPgsConfig(logger, dbpool, st, pubsub) | |
| 136 | - | cfg.Domain = "pgs.test" | |
| 137 | - | // Increase max asset size for testing large files | |
| 138 | - | cfg.MaxAssetSize = 10 * 1024 * 1024 // 10MB | |
| 139 | - | ||
| 140 | - | httpCache := SetupCache(cfg) | |
| 141 | - | routes := NewWebRouter(cfg) | |
| 142 | - | cacher := &CachedHttp{ | |
| 143 | - | handler: httpCache, | |
| 144 | - | routes: routes, | |
| 145 | - | } | |
| 146 | - | ||
| 147 | - | // First request (cache miss) | |
| 148 | - | req1 := httptest.NewRequest("GET", dbpool.mkpath("/medium-file.bin"), strings.NewReader("")) | |
| 149 | - | rec1 := httptest.NewRecorder() | |
| 150 | - | cacher.ServeHTTP(rec1, req1) | |
| 151 | - | ||
| 152 | - | body1 := rec1.Body.String() | |
| 153 | - | size1 := len(body1) | |
| 154 | - | ||
| 155 | - | // Second request (cache hit) | |
| 156 | - | req2 := httptest.NewRequest("GET", dbpool.mkpath("/medium-file.bin"), strings.NewReader("")) | |
| 157 | - | rec2 := httptest.NewRecorder() | |
| 158 | - | cacher.ServeHTTP(rec2, req2) | |
| 159 | - | ||
| 160 | - | body2 := rec2.Body.String() | |
| 161 | - | size2 := len(body2) | |
| 162 | - | ||
| 163 | - | t.Logf("Cache miss: %d bytes, Cache hit: %d bytes (expected %d)", size1, size2, expectedSize) | |
| 164 | - | ||
| 165 | - | // Verify complete responses | |
| 166 | - | if size1 != expectedSize { | |
| 167 | - | t.Errorf("cache miss: expected %d bytes, got %d", expectedSize, size1) | |
| 168 | - | } | |
| 169 | - | ||
| 170 | - | if size2 != expectedSize { | |
| 171 | - | t.Errorf("SOUIN_TRUNCATION_BUG: cache hit returned %d bytes instead of %d bytes", | |
| 172 | - | size2, expectedSize) | |
| 173 | - | } | |
| 174 | - | ||
| 175 | - | // Verify content matches original | |
| 176 | - | if body1 != string(payload) { | |
| 177 | - | t.Errorf("cache miss response body doesn't match") | |
| 178 | - | } | |
| 179 | - | ||
| 180 | - | if body2 != string(payload) { | |
| 181 | - | t.Errorf("cache hit response body doesn't match") | |
| 182 | - | } | |
| 183 | - | } |
+126,
-168
| ... | ... | @@ -8,7 +8,6 @@ import ( | |
| 8 | 8 | "html/template" | |
| 9 | 9 | "log/slog" | |
| 10 | 10 | "net/http" | |
| 11 | - | "net/url" | |
| 12 | 11 | "os" | |
| 13 | 12 | "path/filepath" | |
| 14 | 13 | "regexp" |
| ... | ... | @@ -17,71 +16,116 @@ import ( | |
| 17 | 16 | ||
| 18 | 17 | _ "net/http/pprof" | |
| 19 | 18 | ||
| 20 | - | "github.com/darkweak/souin/configurationtypes" | |
| 21 | - | "github.com/darkweak/souin/pkg/middleware" | |
| 22 | - | "github.com/darkweak/souin/plugins/souin/storages" | |
| 23 | - | "github.com/darkweak/storages/core" | |
| 24 | 19 | "github.com/gorilla/feeds" | |
| 25 | 20 | "github.com/hashicorp/golang-lru/v2/expirable" | |
| 26 | 21 | "github.com/picosh/pico/pkg/db" | |
| 22 | + | "github.com/picosh/pico/pkg/httpcache" | |
| 27 | 23 | "github.com/picosh/pico/pkg/shared" | |
| 28 | 24 | "github.com/picosh/pico/pkg/shared/router" | |
| 29 | 25 | "github.com/picosh/pico/pkg/storage" | |
| 26 | + | "github.com/prometheus/client_golang/prometheus" | |
| 27 | + | "github.com/prometheus/client_golang/prometheus/promauto" | |
| 30 | 28 | "github.com/prometheus/client_golang/prometheus/promhttp" | |
| 31 | - | "google.golang.org/protobuf/proto" | |
| 32 | 29 | ) | |
| 33 | 30 | ||
| 34 | - | type CachedHttp struct { | |
| 35 | - | handler *middleware.SouinBaseHandler | |
| 36 | - | routes *WebRouter | |
| 31 | + | type PgsCacheKey struct { | |
| 32 | + | Domain string | |
| 33 | + | TxtPrefix string | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | func (c *PgsCacheKey) GetCacheKey(r *http.Request) string { | |
| 37 | + | subdomain := router.GetSubdomainFromRequest(r, c.Domain, c.TxtPrefix) | |
| 38 | + | return subdomain + "__" + r.Method + "__" + r.URL.RequestURI() | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | type PromCacheMetrics struct { | |
| 42 | + | Cache httpcache.Cacher | |
| 43 | + | CacheItems prometheus.Gauge | |
| 44 | + | CacheSizeBytes prometheus.Gauge | |
| 45 | + | CacheHit prometheus.Counter | |
| 46 | + | CacheMiss prometheus.Counter | |
| 47 | + | UpstreamReq prometheus.Counter | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | func NewPromCacheMetrics(reg prometheus.Registerer) *PromCacheMetrics { | |
| 51 | + | name := "pgs" | |
| 52 | + | auto := promauto.With(reg) | |
| 53 | + | return &PromCacheMetrics{ | |
| 54 | + | CacheItems: auto.NewGauge(prometheus.GaugeOpts{ | |
| 55 | + | Namespace: name, | |
| 56 | + | Subsystem: "http_cache", | |
| 57 | + | Name: "total_items", | |
| 58 | + | Help: "Number of items in the http cache", | |
| 59 | + | }), | |
| 60 | + | CacheSizeBytes: auto.NewGauge(prometheus.GaugeOpts{ | |
| 61 | + | Namespace: name, | |
| 62 | + | Subsystem: "http_cache", | |
| 63 | + | Name: "total_size", | |
| 64 | + | Help: "The total size of the http cache in bytes", | |
| 65 | + | }), | |
| 66 | + | CacheHit: auto.NewCounter(prometheus.CounterOpts{ | |
| 67 | + | Namespace: name, | |
| 68 | + | Subsystem: "http_cache", | |
| 69 | + | Name: "cache_hit", | |
| 70 | + | Help: "The number of times there was a cache hit", | |
| 71 | + | }), | |
| 72 | + | CacheMiss: auto.NewCounter(prometheus.CounterOpts{ | |
| 73 | + | Namespace: name, | |
| 74 | + | Subsystem: "http_cache", | |
| 75 | + | Name: "cache_miss", | |
| 76 | + | Help: "The number of times there was a cache miss", | |
| 77 | + | }), | |
| 78 | + | UpstreamReq: auto.NewCounter(prometheus.CounterOpts{ | |
| 79 | + | Namespace: name, | |
| 80 | + | Subsystem: "http_cache", | |
| 81 | + | Name: "upstream_request", | |
| 82 | + | Help: "The number of times the upstream http server was requested", | |
| 83 | + | }), | |
| 84 | + | } | |
| 37 | 85 | } | |
| 38 | - | ||
| 39 | - | func (c *CachedHttp) ServeHTTP(writer http.ResponseWriter, req *http.Request) { | |
| 40 | - | _ = c.handler.ServeHTTP(writer, req, func(w http.ResponseWriter, r *http.Request) error { | |
| 41 | - | c.routes.ServeHTTP(w, r) | |
| 42 | - | return nil | |
| 43 | - | }) | |
| 86 | + | func (p *PromCacheMetrics) AddCacheItem(size float64) { | |
| 87 | + | p.CacheItems.Add(1) | |
| 88 | + | p.CacheSizeBytes.Add(size) | |
| 89 | + | } | |
| 90 | + | func (p *PromCacheMetrics) EvictCacheItem(key string, value []byte) { | |
| 91 | + | p.CacheItems.Add(-1) | |
| 92 | + | p.CacheSizeBytes.Add(-float64(len(value))) | |
| 93 | + | } | |
| 94 | + | func (p *PromCacheMetrics) AddCacheHit() { | |
| 95 | + | p.CacheHit.Add(1) | |
| 96 | + | } | |
| 97 | + | func (p *PromCacheMetrics) AddCacheMiss() { | |
| 98 | + | p.CacheMiss.Add(1) | |
| 99 | + | } | |
| 100 | + | func (p *PromCacheMetrics) AddUpstreamRequest() { | |
| 101 | + | p.UpstreamReq.Add(1) | |
| 44 | 102 | } | |
| 45 | 103 | ||
| 46 | - | func SetupCache(cfg *PgsConfig) *middleware.SouinBaseHandler { | |
| 47 | - | ttl := configurationtypes.Duration{Duration: cfg.CacheTTL} | |
| 48 | - | stale := configurationtypes.Duration{Duration: cfg.CacheTTL * 2} | |
| 49 | - | c := &middleware.BaseConfiguration{ | |
| 50 | - | API: configurationtypes.API{ | |
| 51 | - | Prometheus: configurationtypes.APIEndpoint{ | |
| 52 | - | Enable: true, | |
| 53 | - | }, | |
| 54 | - | }, | |
| 55 | - | DefaultCache: &configurationtypes.DefaultCache{ | |
| 56 | - | TTL: ttl, | |
| 57 | - | Stale: stale, | |
| 58 | - | Otter: configurationtypes.CacheProvider{ | |
| 59 | - | Uuid: fmt.Sprintf("OTTER-%s", stale), | |
| 60 | - | Configuration: map[string]interface{}{}, | |
| 61 | - | }, | |
| 62 | - | Regex: configurationtypes.Regex{ | |
| 63 | - | Exclude: "/check|/_metrics", | |
| 64 | - | }, | |
| 65 | - | MaxBodyBytes: uint64(cfg.MaxAssetSize), | |
| 66 | - | DefaultCacheControl: cfg.CacheControl, | |
| 104 | + | func NewPgsHttpCache(cfg *PgsConfig, upstream http.Handler) *httpcache.HttpCache { | |
| 105 | + | ttl := cfg.CacheTTL | |
| 106 | + | metrics := NewPromCacheMetrics(prometheus.DefaultRegisterer) | |
| 107 | + | cache := expirable.NewLRU(0, metrics.EvictCacheItem, ttl) | |
| 108 | + | httpCache := &httpcache.HttpCache{ | |
| 109 | + | Ttl: ttl, | |
| 110 | + | Logger: cfg.Logger, | |
| 111 | + | Upstream: upstream, | |
| 112 | + | Cache: cache, | |
| 113 | + | CacheKey: &PgsCacheKey{ | |
| 114 | + | Domain: cfg.Domain, | |
| 115 | + | TxtPrefix: cfg.TxtPrefix, | |
| 67 | 116 | }, | |
| 117 | + | CacheMetrics: metrics, | |
| 68 | 118 | } | |
| 69 | - | c.SetLogger(&CompatLogger{Logger: cfg.Logger}) | |
| 70 | - | storages.InitFromConfiguration(c) | |
| 71 | - | return middleware.NewHTTPCacheHandler(c) | |
| 119 | + | httpCache.Logger.Info("httpcache initiated", "ttl", httpCache.Ttl, "storage", "lru") | |
| 120 | + | return httpCache | |
| 72 | 121 | } | |
| 73 | 122 | ||
| 74 | 123 | func StartApiServer(cfg *PgsConfig) { | |
| 75 | 124 | ctx := context.Background() | |
| 76 | 125 | ||
| 77 | - | httpCache := SetupCache(cfg) | |
| 78 | - | routes := NewWebRouter(cfg) | |
| 79 | - | cacher := &CachedHttp{ | |
| 80 | - | handler: httpCache, | |
| 81 | - | routes: routes, | |
| 82 | - | } | |
| 83 | - | ||
| 84 | - | go routes.CacheMgmt(ctx, httpCache, cfg.CacheClearingQueue) | |
| 126 | + | router := NewWebRouter(cfg) | |
| 127 | + | httpCache := NewPgsHttpCache(router.Cfg, router) | |
| 128 | + | go CacheMgmt(ctx, cfg.CacheClearingQueue, cfg, httpCache.Cache) | |
| 85 | 129 | ||
| 86 | 130 | portStr := fmt.Sprintf(":%s", cfg.WebPort) | |
| 87 | 131 | cfg.Logger.Info( |
| ... | ... | @@ -89,7 +133,7 @@ func StartApiServer(cfg *PgsConfig) { | |
| 89 | 133 | "port", cfg.WebPort, | |
| 90 | 134 | "domain", cfg.Domain, | |
| 91 | 135 | ) | |
| 92 | - | err := http.ListenAndServe(portStr, cacher) | |
| 136 | + | err := http.ListenAndServe(portStr, httpCache) | |
| 93 | 137 | cfg.Logger.Error( | |
| 94 | 138 | "listen and serve", | |
| 95 | 139 | "err", err.Error(), |
| ... | ... | @@ -122,6 +166,26 @@ func newWebRouter(cfg *PgsConfig) *WebRouter { | |
| 122 | 166 | return router | |
| 123 | 167 | } | |
| 124 | 168 | ||
| 169 | + | func (web *WebRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| 170 | + | subdomain := router.GetSubdomainFromRequest(r, web.Cfg.Domain, web.Cfg.TxtPrefix) | |
| 171 | + | if web.RootRouter == nil || web.UserRouter == nil { | |
| 172 | + | web.Cfg.Logger.Error("routers not initialized") | |
| 173 | + | http.Error(w, "routers not initialized", http.StatusInternalServerError) | |
| 174 | + | return | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | var mux *http.ServeMux | |
| 178 | + | if subdomain == "" { | |
| 179 | + | mux = web.RootRouter | |
| 180 | + | } else { | |
| 181 | + | mux = web.UserRouter | |
| 182 | + | } | |
| 183 | + | ||
| 184 | + | ctx := r.Context() | |
| 185 | + | ctx = context.WithValue(ctx, router.CtxSubdomainKey{}, subdomain) | |
| 186 | + | mux.ServeHTTP(w, r.WithContext(ctx)) | |
| 187 | + | } | |
| 188 | + | ||
| 125 | 189 | func (web *WebRouter) WatchCacheClear() { | |
| 126 | 190 | for key := range web.Cfg.CacheClearingQueue { | |
| 127 | 191 | web.Cfg.Logger.Info("lru cache clear request", "key", key) |
| ... | ... | @@ -288,56 +352,27 @@ func (web *WebRouter) checkHandler(w http.ResponseWriter, r *http.Request) { | |
| 288 | 352 | w.WriteHeader(http.StatusNotFound) | |
| 289 | 353 | } | |
| 290 | 354 | ||
| 291 | - | func (web *WebRouter) CacheMgmt(ctx context.Context, httpCache *middleware.SouinBaseHandler, notify chan string) { | |
| 292 | - | storer := httpCache.Storers[0] | |
| 293 | - | ||
| 355 | + | func CacheMgmt(ctx context.Context, notify chan string, cfg *PgsConfig, cacher httpcache.Cacher) { | |
| 356 | + | cfg.Logger.Info("cache mgmt initiated") | |
| 294 | 357 | for { | |
| 295 | - | scanner := bufio.NewScanner(web.Cfg.Pubsub) | |
| 358 | + | scanner := bufio.NewScanner(cfg.Pubsub) | |
| 296 | 359 | scanner.Buffer(make([]byte, 32*1024), 32*1024) | |
| 297 | 360 | for scanner.Scan() { | |
| 298 | - | surrogateKey := strings.TrimSpace(scanner.Text()) | |
| 299 | - | web.Cfg.Logger.Info("received cache-drain item", "surrogateKey", surrogateKey) | |
| 300 | - | notify <- surrogateKey | |
| 301 | - | ||
| 302 | - | if surrogateKey == "*" { | |
| 303 | - | storer.DeleteMany(".+") | |
| 304 | - | err := httpCache.SurrogateKeyStorer.Destruct() | |
| 305 | - | if err != nil { | |
| 306 | - | web.Cfg.Logger.Error("could not clear cache and surrogate key store", "err", err) | |
| 307 | - | } else { | |
| 308 | - | web.Cfg.Logger.Info("successfully cleared cache and surrogate keys store") | |
| 309 | - | } | |
| 361 | + | subdomain := strings.TrimSpace(scanner.Text()) | |
| 362 | + | cfg.Logger.Info("received cache-drain item", "subdomain", subdomain) | |
| 363 | + | notify <- subdomain | |
| 364 | + | ||
| 365 | + | if subdomain == "*" { | |
| 366 | + | cacher.Purge() | |
| 367 | + | cfg.Logger.Info("successfully cleared cache from remote cli request") | |
| 310 | 368 | continue | |
| 311 | 369 | } | |
| 312 | 370 | ||
| 313 | - | var header http.Header = map[string][]string{} | |
| 314 | - | header.Add("Surrogate-Key", surrogateKey) | |
| 315 | - | ||
| 316 | - | ck, _ := httpCache.SurrogateKeyStorer.Purge(header) | |
| 317 | - | for _, key := range ck { | |
| 318 | - | key, _ = strings.CutPrefix(key, core.MappingKeyPrefix) | |
| 319 | - | if b := storer.Get(core.MappingKeyPrefix + key); len(b) > 0 { | |
| 320 | - | var mapping core.StorageMapper | |
| 321 | - | if e := proto.Unmarshal(b, &mapping); e == nil { | |
| 322 | - | for k := range mapping.GetMapping() { | |
| 323 | - | qkey, _ := url.QueryUnescape(k) | |
| 324 | - | web.Cfg.Logger.Info( | |
| 325 | - | "deleting key from surrogate cache", | |
| 326 | - | "surrogateKey", surrogateKey, | |
| 327 | - | "key", qkey, | |
| 328 | - | ) | |
| 329 | - | storer.Delete(qkey) | |
| 330 | - | } | |
| 331 | - | } | |
| 371 | + | for _, key := range cacher.Keys() { | |
| 372 | + | if strings.HasPrefix(key, subdomain) { | |
| 373 | + | cfg.Logger.Info("deleting cache item", "subdomain", subdomain, "key", key) | |
| 374 | + | _ = cacher.Remove(key) | |
| 332 | 375 | } | |
| 333 | - | ||
| 334 | - | qkey, _ := url.QueryUnescape(key) | |
| 335 | - | web.Cfg.Logger.Info( | |
| 336 | - | "deleting from cache", | |
| 337 | - | "surrogateKey", surrogateKey, | |
| 338 | - | "key", core.MappingKeyPrefix+qkey, | |
| 339 | - | ) | |
| 340 | - | storer.Delete(core.MappingKeyPrefix + qkey) | |
| 341 | 376 | } | |
| 342 | 377 | } | |
| 343 | 378 | } |
| ... | ... | @@ -573,80 +608,3 @@ func (web *WebRouter) handleLogin(w http.ResponseWriter, r *http.Request) { | |
| 573 | 608 | func (web *WebRouter) handleAutoForm(w http.ResponseWriter, r *http.Request) { | |
| 574 | 609 | handleAutoForm(w, r, web.Cfg) | |
| 575 | 610 | } | |
| 576 | - | ||
| 577 | - | func (web *WebRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| 578 | - | subdomain := router.GetSubdomainFromRequest(r, web.Cfg.Domain, web.Cfg.TxtPrefix) | |
| 579 | - | if web.RootRouter == nil || web.UserRouter == nil { | |
| 580 | - | web.Cfg.Logger.Error("routers not initialized") | |
| 581 | - | http.Error(w, "routers not initialized", http.StatusInternalServerError) | |
| 582 | - | return | |
| 583 | - | } | |
| 584 | - | ||
| 585 | - | var mux *http.ServeMux | |
| 586 | - | if subdomain == "" { | |
| 587 | - | mux = web.RootRouter | |
| 588 | - | } else { | |
| 589 | - | mux = web.UserRouter | |
| 590 | - | } | |
| 591 | - | ||
| 592 | - | ctx := r.Context() | |
| 593 | - | ctx = context.WithValue(ctx, router.CtxSubdomainKey{}, subdomain) | |
| 594 | - | mux.ServeHTTP(w, r.WithContext(ctx)) | |
| 595 | - | } | |
| 596 | - | ||
| 597 | - | type CompatLogger struct { | |
| 598 | - | Logger *slog.Logger | |
| 599 | - | } | |
| 600 | - | ||
| 601 | - | func (cl *CompatLogger) marshall(int ...interface{}) string { | |
| 602 | - | res := "" | |
| 603 | - | for _, val := range int { | |
| 604 | - | switch r := val.(type) { | |
| 605 | - | case string: | |
| 606 | - | res += " " + r | |
| 607 | - | } | |
| 608 | - | } | |
| 609 | - | return res | |
| 610 | - | } | |
| 611 | - | func (cl *CompatLogger) DPanic(int ...interface{}) { | |
| 612 | - | cl.Logger.Error("panic", "output", cl.marshall(int)) | |
| 613 | - | } | |
| 614 | - | func (cl *CompatLogger) DPanicf(st string, int ...interface{}) { | |
| 615 | - | cl.Logger.Error(fmt.Sprintf(st, int...)) | |
| 616 | - | } | |
| 617 | - | func (cl *CompatLogger) Debug(int ...interface{}) { | |
| 618 | - | cl.Logger.Debug("debug", "output", cl.marshall(int)) | |
| 619 | - | } | |
| 620 | - | func (cl *CompatLogger) Debugf(st string, int ...interface{}) { | |
| 621 | - | cl.Logger.Debug(fmt.Sprintf(st, int...)) | |
| 622 | - | } | |
| 623 | - | func (cl *CompatLogger) Error(int ...interface{}) { | |
| 624 | - | cl.Logger.Error("error", "output", cl.marshall(int)) | |
| 625 | - | } | |
| 626 | - | func (cl *CompatLogger) Errorf(st string, int ...interface{}) { | |
| 627 | - | cl.Logger.Error(fmt.Sprintf(st, int...)) | |
| 628 | - | } | |
| 629 | - | func (cl *CompatLogger) Fatal(int ...interface{}) { | |
| 630 | - | cl.Logger.Error("fatal", "outpu", cl.marshall(int)) | |
| 631 | - | } | |
| 632 | - | func (cl *CompatLogger) Fatalf(st string, int ...interface{}) { | |
| 633 | - | cl.Logger.Error(fmt.Sprintf(st, int...)) | |
| 634 | - | } | |
| 635 | - | func (cl *CompatLogger) Info(int ...interface{}) { | |
| 636 | - | cl.Logger.Info("info", "output", cl.marshall(int)) | |
| 637 | - | } | |
| 638 | - | func (cl *CompatLogger) Infof(st string, int ...interface{}) { | |
| 639 | - | cl.Logger.Info(fmt.Sprintf(st, int...)) | |
| 640 | - | } | |
| 641 | - | func (cl *CompatLogger) Panic(int ...interface{}) { | |
| 642 | - | cl.Logger.Error("panic", "output", cl.marshall(int)) | |
| 643 | - | } | |
| 644 | - | func (cl *CompatLogger) Panicf(st string, int ...interface{}) { | |
| 645 | - | cl.Logger.Error(fmt.Sprintf(st, int...)) | |
| 646 | - | } | |
| 647 | - | func (cl *CompatLogger) Warn(int ...interface{}) { | |
| 648 | - | cl.Logger.Warn("warn", "output", cl.marshall(int)) | |
| 649 | - | } | |
| 650 | - | func (cl *CompatLogger) Warnf(st string, int ...interface{}) { | |
| 651 | - | cl.Logger.Warn(fmt.Sprintf(st, int...)) | |
| 652 | - | } |
+1,
-0
| ... | ... | @@ -289,6 +289,7 @@ func (h *ApiAssetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| 289 | 289 | ) | |
| 290 | 290 | done, _ := checkPreconditions(w, r, info.LastModified.UTC()) | |
| 291 | 291 | if done { | |
| 292 | + | logger.Info("A conditaionl request was detected, no body required") | |
| 292 | 293 | // A conditional request was detected, status and headers are set, no body required (either 412 or 304) | |
| 293 | 294 | return | |
| 294 | 295 | } |
+930,
-0
| ... | ... | @@ -0,0 +1,930 @@ | |
| 1 | + | package httpcache | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "encoding/json" | |
| 5 | + | "log/slog" | |
| 6 | + | "net/http" | |
| 7 | + | "net/http/httptest" | |
| 8 | + | "strconv" | |
| 9 | + | "strings" | |
| 10 | + | "testing" | |
| 11 | + | "time" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | /* | |
| 15 | + | TODO: | |
| 16 | + | - Request no-store store-prevention (RFC 9111 §5.2.1.5): verify a no-store request does not populate/update cache for subsequent requests. | |
| 17 | + | - Authorization storage/use constraints (RFC 9111 §3.5): authenticated responses should not be reused unless explicitly permitted by response directives. | |
| 18 | + | - Vary: * behavior (RFC 9111 §4.1): ensure such responses are not reused for subsequent requests. | |
| 19 | + | - Multi-field Vary matching (RFC 9111 §4.1): all nominated request fields must match original request values, not just one. | |
| 20 | + | - Age correction with upstream metadata (RFC 9111 §4.2.3, §5.1): test interactions of stored response Date/Age values rather than only local clock delta. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | // TestContext holds shared test state. | |
| 24 | + | type TestContext struct { | |
| 25 | + | t *testing.T | |
| 26 | + | handler http.Handler | |
| 27 | + | cachedServer *httptest.Server | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | // NewTestContext creates a test context with a backend and cached server. | |
| 31 | + | func NewTestContext(t *testing.T, cacheHandler http.Handler) *TestContext { | |
| 32 | + | tc := &TestContext{ | |
| 33 | + | t: t, | |
| 34 | + | handler: cacheHandler, | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | tc.cachedServer = httptest.NewServer(tc.handler) | |
| 38 | + | t.Cleanup(tc.cachedServer.Close) | |
| 39 | + | ||
| 40 | + | return tc | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | func (tc *TestContext) Do(req *http.Request) (*http.Response, error) { | |
| 44 | + | return http.DefaultClient.Do(req) | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | func (tc *TestContext) DoWithHeaders(req *http.Request, headers map[string][]string) (*http.Response, error) { | |
| 48 | + | reqCopy := req.Clone(req.Context()) | |
| 49 | + | reqCopy.Header = req.Header.Clone() | |
| 50 | + | if reqCopy.Header == nil { | |
| 51 | + | reqCopy.Header = make(http.Header) | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | for key, val := range headers { | |
| 55 | + | reqCopy.Header.Del(key) | |
| 56 | + | for _, v := range val { | |
| 57 | + | reqCopy.Header.Add(key, v) | |
| 58 | + | } | |
| 59 | + | } | |
| 60 | + | return http.DefaultClient.Do(reqCopy) | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | func (tc *TestContext) GetHeader(resp *http.Response, key string) string { | |
| 64 | + | return resp.Header.Get(key) | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | func testCacheValue(afterCreated time.Duration) *CacheValue { | |
| 68 | + | return &CacheValue{ | |
| 69 | + | Header: map[string][]string{}, | |
| 70 | + | Body: []byte("success"), | |
| 71 | + | CreatedAt: time.Now().Add(-afterCreated), | |
| 72 | + | } | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | // RFC 9211 The Cache-Status HTTP Response Header Field | |
| 76 | + | // https://www.rfc-editor.org/rfc/rfc9211#section-2 | |
| 77 | + | func TestCacheCacheStatus(t *testing.T) { | |
| 78 | + | mux := http.NewServeMux() | |
| 79 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 80 | + | w.WriteHeader(200) | |
| 81 | + | _, _ = w.Write([]byte("success")) | |
| 82 | + | }) | |
| 83 | + | ||
| 84 | + | logger := slog.Default() | |
| 85 | + | handler := NewHttpCache(logger, mux) | |
| 86 | + | tc := NewTestContext(t, handler) | |
| 87 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 88 | + | ||
| 89 | + | // first request hits backend | |
| 90 | + | resp1, _ := tc.Do(req) | |
| 91 | + | if resp1.StatusCode != http.StatusOK { | |
| 92 | + | t.Errorf("expected 200, got %d", resp1.StatusCode) | |
| 93 | + | } | |
| 94 | + | status := resp1.Header.Get("cache-status") | |
| 95 | + | if !strings.Contains(status, "miss") { | |
| 96 | + | t.Errorf("expected miss, got %s", status) | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | // second request hits cache | |
| 100 | + | resp2, _ := tc.Do(req) | |
| 101 | + | if resp2.StatusCode != http.StatusOK { | |
| 102 | + | t.Errorf("expected 200, got %d", resp2.StatusCode) | |
| 103 | + | } | |
| 104 | + | status = resp2.Header.Get("cache-status") | |
| 105 | + | if !strings.Contains(status, "hit") { | |
| 106 | + | t.Errorf("expected hit, got %s", status) | |
| 107 | + | } | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | // RFC 9110 15.1-2 Heuristically Cacheable | |
| 111 | + | // https://www.rfc-editor.org/rfc/rfc9110#section-15.1-2 | |
| 112 | + | // 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, and 501. | |
| 113 | + | func TestCacheStatusCode(t *testing.T) { | |
| 114 | + | mux := http.NewServeMux() | |
| 115 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 116 | + | w.WriteHeader(500) | |
| 117 | + | _, _ = w.Write([]byte("boom!")) | |
| 118 | + | }) | |
| 119 | + | ||
| 120 | + | logger := slog.Default() | |
| 121 | + | handler := NewHttpCache(logger, mux) | |
| 122 | + | tc := NewTestContext(t, handler) | |
| 123 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 124 | + | ||
| 125 | + | // first request hits backend | |
| 126 | + | resp1, _ := tc.Do(req) | |
| 127 | + | if resp1.StatusCode != http.StatusInternalServerError { | |
| 128 | + | t.Errorf("expected 500, got %d", resp1.StatusCode) | |
| 129 | + | } | |
| 130 | + | status := resp1.Header.Get("cache-status") | |
| 131 | + | if !strings.Contains(status, "miss") { | |
| 132 | + | t.Errorf("expected miss, got %s", status) | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | // second request hits backend | |
| 136 | + | resp2, _ := tc.Do(req) | |
| 137 | + | if resp2.StatusCode != http.StatusInternalServerError { | |
| 138 | + | t.Errorf("expected 500, got %d", resp2.StatusCode) | |
| 139 | + | } | |
| 140 | + | status = resp2.Header.Get("cache-status") | |
| 141 | + | if !strings.Contains(status, "miss") { | |
| 142 | + | t.Errorf("expected miss, got %s", status) | |
| 143 | + | } | |
| 144 | + | } | |
| 145 | + | ||
| 146 | + | // RFC 9111 2.3 Opinion - Only store GET requests | |
| 147 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-2-3 | |
| 148 | + | func TestCacheMethod(t *testing.T) { | |
| 149 | + | mux := http.NewServeMux() | |
| 150 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 151 | + | w.WriteHeader(200) | |
| 152 | + | _, _ = w.Write([]byte("success")) | |
| 153 | + | }) | |
| 154 | + | ||
| 155 | + | logger := slog.Default() | |
| 156 | + | handler := NewHttpCache(logger, mux) | |
| 157 | + | tc := NewTestContext(t, handler) | |
| 158 | + | req, _ := http.NewRequest("POST", tc.cachedServer.URL+"/test", nil) | |
| 159 | + | ||
| 160 | + | // first request hits backend | |
| 161 | + | resp1, _ := tc.Do(req) | |
| 162 | + | if resp1.StatusCode != http.StatusOK { | |
| 163 | + | t.Errorf("expected 200, got %d", resp1.StatusCode) | |
| 164 | + | } | |
| 165 | + | status := resp1.Header.Get("cache-status") | |
| 166 | + | if !strings.Contains(status, "miss") { | |
| 167 | + | t.Errorf("expected miss, got %s", status) | |
| 168 | + | } | |
| 169 | + | ||
| 170 | + | // second request hits backend | |
| 171 | + | resp2, _ := tc.Do(req) | |
| 172 | + | if resp2.StatusCode != http.StatusOK { | |
| 173 | + | t.Errorf("expected 200, got %d", resp2.StatusCode) | |
| 174 | + | } | |
| 175 | + | status = resp2.Header.Get("cache-status") | |
| 176 | + | if !strings.Contains(status, "miss") { | |
| 177 | + | t.Errorf("expected miss, got %s", status) | |
| 178 | + | } | |
| 179 | + | } | |
| 180 | + | ||
| 181 | + | // RFC 9111 3.1 Storing Header and Trailer Fields | |
| 182 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-3.1 | |
| 183 | + | func TestCacheStoringHeaders(t *testing.T) { | |
| 184 | + | mux := http.NewServeMux() | |
| 185 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 186 | + | w.Header().Set("connection", "idk") | |
| 187 | + | w.Header().Set("proxy-authenticate", "idk") | |
| 188 | + | w.Header().Set("proxy-authentication-info", "idk") | |
| 189 | + | w.Header().Set("proxy-authorization", "idk") | |
| 190 | + | ||
| 191 | + | w.WriteHeader(200) | |
| 192 | + | _, _ = w.Write([]byte("success")) | |
| 193 | + | }) | |
| 194 | + | ||
| 195 | + | logger := slog.Default() | |
| 196 | + | handler := NewHttpCache(logger, mux) | |
| 197 | + | tc := NewTestContext(t, handler) | |
| 198 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 199 | + | ||
| 200 | + | // first request hits backend | |
| 201 | + | resp1, _ := tc.Do(req) | |
| 202 | + | if resp1.StatusCode != http.StatusOK { | |
| 203 | + | t.Errorf("expected 200, got %d", resp1.StatusCode) | |
| 204 | + | } | |
| 205 | + | status := resp1.Header.Get("cache-status") | |
| 206 | + | if !strings.Contains(status, "miss") { | |
| 207 | + | t.Errorf("expected miss, got %s", status) | |
| 208 | + | } | |
| 209 | + | ||
| 210 | + | // second request hits cache | |
| 211 | + | resp2, _ := tc.Do(req) | |
| 212 | + | if resp2.StatusCode != http.StatusOK { | |
| 213 | + | t.Errorf("expected 200, got %d", resp2.StatusCode) | |
| 214 | + | } | |
| 215 | + | headers := []string{ | |
| 216 | + | resp2.Header.Get("connection"), | |
| 217 | + | resp2.Header.Get("proxy-authenticate"), | |
| 218 | + | resp2.Header.Get("proxy-authentication-info"), | |
| 219 | + | resp2.Header.Get("proxy-authorization"), | |
| 220 | + | } | |
| 221 | + | for _, hdr := range headers { | |
| 222 | + | if hdr != "" { | |
| 223 | + | t.Errorf("expected no header, found one: %s", hdr) | |
| 224 | + | } | |
| 225 | + | } | |
| 226 | + | } | |
| 227 | + | ||
| 228 | + | // RFC 9111 4.1 Vary. | |
| 229 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.1 | |
| 230 | + | func TestCacheVary(t *testing.T) { | |
| 231 | + | mux := http.NewServeMux() | |
| 232 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 233 | + | w.WriteHeader(200) | |
| 234 | + | _, _ = w.Write([]byte("success")) | |
| 235 | + | }) | |
| 236 | + | ||
| 237 | + | logger := slog.Default() | |
| 238 | + | handler := NewHttpCache(logger, mux) | |
| 239 | + | tc := NewTestContext(t, handler) | |
| 240 | + | ||
| 241 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 242 | + | cacheKey := handler.GetCacheKey(req) | |
| 243 | + | cv := testCacheValue(250 * time.Second) | |
| 244 | + | cv.Header["Vary"] = []string{"Accept-Encoding"} | |
| 245 | + | // Store the original request header that selected this representation. | |
| 246 | + | cv.Header["Accept-Encoding"] = []string{"gzip"} | |
| 247 | + | cacheValue, _ := json.Marshal(cv) | |
| 248 | + | handler.Cache.Add(cacheKey, cacheValue) | |
| 249 | + | ||
| 250 | + | respMatch, _ := tc.DoWithHeaders(req, map[string][]string{ | |
| 251 | + | "Accept-Encoding": {"gzip"}, | |
| 252 | + | }) | |
| 253 | + | status := respMatch.Header.Get("cache-status") | |
| 254 | + | if !strings.Contains(status, "hit") { | |
| 255 | + | t.Errorf("expected hit, got %s", status) | |
| 256 | + | } | |
| 257 | + | ||
| 258 | + | respMisMatch, _ := tc.DoWithHeaders(req, map[string][]string{ | |
| 259 | + | "Accept-Encoding": {"text/plain"}, | |
| 260 | + | }) | |
| 261 | + | status = respMisMatch.Header.Get("cache-status") | |
| 262 | + | if !strings.Contains(status, "miss") { | |
| 263 | + | t.Errorf("expected miss, got %s", status) | |
| 264 | + | } | |
| 265 | + | } | |
| 266 | + | ||
| 267 | + | // RFC 9111 4.3 Validation. | |
| 268 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3 | |
| 269 | + | // Last-Modified ETag If-Match If-None-Match If-Range If-Modified-Since If-Unmodified-Since | |
| 270 | + | // RFC 9110 13 Conditional Requests. | |
| 271 | + | // https://www.rfc-editor.org/rfc/rfc9110#section-13 | |
| 272 | + | func TestCacheValidation(t *testing.T) { | |
| 273 | + | actual := time.Now().Add(-10 * time.Minute).UTC() | |
| 274 | + | actualStr := actual.Format(time.RFC1123) | |
| 275 | + | now := time.Now().UTC() | |
| 276 | + | nowStr := now.Format(time.RFC1123) | |
| 277 | + | early := time.Now().Add(-20 * time.Minute) | |
| 278 | + | earlyStr := early.Format(time.RFC1123) | |
| 279 | + | tests := []struct { | |
| 280 | + | name string | |
| 281 | + | link string | |
| 282 | + | validationHeader string | |
| 283 | + | validationValue string | |
| 284 | + | extraHeaders map[string][]string | |
| 285 | + | expected string | |
| 286 | + | originStatus int | |
| 287 | + | expectedStatus int | |
| 288 | + | }{ | |
| 289 | + | { | |
| 290 | + | name: "RFC 9110 13.1.2 If-None-Match", | |
| 291 | + | link: "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2", | |
| 292 | + | validationHeader: "If-None-Match", | |
| 293 | + | validationValue: "\"abc\"", | |
| 294 | + | expected: "hit", | |
| 295 | + | originStatus: http.StatusOK, | |
| 296 | + | expectedStatus: http.StatusOK, | |
| 297 | + | }, | |
| 298 | + | { | |
| 299 | + | name: "RFC 9110 13.1.2 If-None-Match Wildcard", | |
| 300 | + | link: "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2", | |
| 301 | + | validationHeader: "If-None-Match", | |
| 302 | + | validationValue: "*", | |
| 303 | + | expected: "hit", | |
| 304 | + | originStatus: http.StatusOK, | |
| 305 | + | expectedStatus: http.StatusNotModified, | |
| 306 | + | }, | |
| 307 | + | { | |
| 308 | + | name: "RFC 9110 13.1.3 If-Modified-Since", | |
| 309 | + | link: "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.3", | |
| 310 | + | validationHeader: "If-Modified-Since", | |
| 311 | + | validationValue: nowStr, | |
| 312 | + | expected: "hit", | |
| 313 | + | originStatus: http.StatusOK, | |
| 314 | + | expectedStatus: http.StatusNotModified, | |
| 315 | + | }, | |
| 316 | + | { | |
| 317 | + | name: "RFC 9110 13.1.4 If-Unmodified-Since", | |
| 318 | + | link: "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.4", | |
| 319 | + | validationHeader: "If-Unmodified-Since", | |
| 320 | + | validationValue: earlyStr, | |
| 321 | + | expected: "hit", | |
| 322 | + | originStatus: http.StatusOK, | |
| 323 | + | expectedStatus: http.StatusOK, | |
| 324 | + | }, | |
| 325 | + | { | |
| 326 | + | name: "RFC 9110 13.1.5 If-Range Date", | |
| 327 | + | link: "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.5", | |
| 328 | + | validationHeader: "If-Range", | |
| 329 | + | validationValue: nowStr, | |
| 330 | + | extraHeaders: map[string][]string{ | |
| 331 | + | "Range": {"bytes=0-3"}, | |
| 332 | + | }, | |
| 333 | + | expected: "hit", | |
| 334 | + | originStatus: http.StatusOK, | |
| 335 | + | expectedStatus: http.StatusOK, | |
| 336 | + | }, | |
| 337 | + | { | |
| 338 | + | name: "RFC 9110 13.1.5 If-Range Date Hit", | |
| 339 | + | link: "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.5", | |
| 340 | + | validationHeader: "If-Range", | |
| 341 | + | validationValue: actualStr, | |
| 342 | + | extraHeaders: map[string][]string{ | |
| 343 | + | "Range": {"bytes=0-3"}, | |
| 344 | + | }, | |
| 345 | + | expected: "hit", | |
| 346 | + | originStatus: http.StatusOK, | |
| 347 | + | expectedStatus: http.StatusOK, | |
| 348 | + | }, | |
| 349 | + | { | |
| 350 | + | name: "RFC 9110 13.1.5 If-Range ETag", | |
| 351 | + | link: "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.5", | |
| 352 | + | validationHeader: "If-Range", | |
| 353 | + | validationValue: "\"abc\"", | |
| 354 | + | extraHeaders: map[string][]string{ | |
| 355 | + | "Range": {"bytes=0-3"}, | |
| 356 | + | }, | |
| 357 | + | expected: "hit", | |
| 358 | + | originStatus: http.StatusOK, | |
| 359 | + | expectedStatus: http.StatusOK, | |
| 360 | + | }, | |
| 361 | + | { | |
| 362 | + | name: "RFC 9110 13.1.5 If-Range ETag Hit", | |
| 363 | + | link: "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.5", | |
| 364 | + | validationHeader: "If-Range", | |
| 365 | + | validationValue: "\"ccc\"", | |
| 366 | + | extraHeaders: map[string][]string{ | |
| 367 | + | "Range": {"bytes=0-3"}, | |
| 368 | + | }, | |
| 369 | + | expected: "hit", | |
| 370 | + | originStatus: http.StatusOK, | |
| 371 | + | expectedStatus: http.StatusOK, | |
| 372 | + | }, | |
| 373 | + | } | |
| 374 | + | ||
| 375 | + | for _, tt := range tests { | |
| 376 | + | mux := http.NewServeMux() | |
| 377 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 378 | + | w.WriteHeader(tt.originStatus) | |
| 379 | + | }) | |
| 380 | + | ||
| 381 | + | logger := slog.Default() | |
| 382 | + | handler := NewHttpCache(logger, mux) | |
| 383 | + | handler.Ttl = time.Minute * 10 | |
| 384 | + | tc := NewTestContext(t, handler) | |
| 385 | + | ||
| 386 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 387 | + | cacheKey := handler.GetCacheKey(req) | |
| 388 | + | cv := testCacheValue(250 * time.Second) | |
| 389 | + | cv.Header["ETag"] = []string{"ccc"} | |
| 390 | + | cv.Header["Last-Modified"] = []string{actualStr} | |
| 391 | + | cacheValue, _ := json.Marshal(cv) | |
| 392 | + | handler.Cache.Add(cacheKey, cacheValue) | |
| 393 | + | ||
| 394 | + | t.Run(tt.name, func(t *testing.T) { | |
| 395 | + | reqHeaders := map[string][]string{tt.validationHeader: {tt.validationValue}} | |
| 396 | + | for key, values := range tt.extraHeaders { | |
| 397 | + | reqHeaders[key] = values | |
| 398 | + | } | |
| 399 | + | ||
| 400 | + | resp, _ := tc.DoWithHeaders(req, reqHeaders) | |
| 401 | + | actual := resp.Header.Get("cache-status") | |
| 402 | + | if !strings.Contains(actual, tt.expected) { | |
| 403 | + | t.Errorf("expected %s, got %s\n%s", tt.expected, actual, tt.link) | |
| 404 | + | } | |
| 405 | + | if resp.StatusCode != tt.expectedStatus { | |
| 406 | + | t.Errorf("expected status %d, got %d", tt.expectedStatus, resp.StatusCode) | |
| 407 | + | } | |
| 408 | + | }) | |
| 409 | + | } | |
| 410 | + | } | |
| 411 | + | ||
| 412 | + | // RFC 9111 5.1 Age. | |
| 413 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.1 | |
| 414 | + | // RFC 9111 4.2.3 Calculating Age. | |
| 415 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.2.3 | |
| 416 | + | func TestCacheAge(t *testing.T) { | |
| 417 | + | mux := http.NewServeMux() | |
| 418 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 419 | + | w.WriteHeader(200) | |
| 420 | + | _, _ = w.Write([]byte("success")) | |
| 421 | + | }) | |
| 422 | + | ||
| 423 | + | logger := slog.Default() | |
| 424 | + | handler := NewHttpCache(logger, mux) | |
| 425 | + | tc := NewTestContext(t, handler) | |
| 426 | + | ||
| 427 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 428 | + | cacheKey := handler.GetCacheKey(req) | |
| 429 | + | cacheValue, _ := json.Marshal(testCacheValue(250 * time.Second)) | |
| 430 | + | handler.Cache.Add(cacheKey, cacheValue) | |
| 431 | + | ||
| 432 | + | resp, _ := tc.Do(req) | |
| 433 | + | if resp.StatusCode != http.StatusOK { | |
| 434 | + | t.Errorf("expected 200, got %d", resp.StatusCode) | |
| 435 | + | } | |
| 436 | + | age := resp.Header.Get("age") | |
| 437 | + | ageNum, err := strconv.Atoi(age) | |
| 438 | + | if err != nil { | |
| 439 | + | t.Fatalf("invalide age header %s", err) | |
| 440 | + | } | |
| 441 | + | if ageNum != 251 { | |
| 442 | + | t.Errorf("expected 250, got %d", ageNum) | |
| 443 | + | } | |
| 444 | + | } | |
| 445 | + | ||
| 446 | + | // RFC 9111 5.2.1 Request Directives | |
| 447 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1 | |
| 448 | + | func TestCacheRequestDirectives(t *testing.T) { | |
| 449 | + | tests := []struct { | |
| 450 | + | name string | |
| 451 | + | link string | |
| 452 | + | cacheControl string | |
| 453 | + | expected string | |
| 454 | + | }{ | |
| 455 | + | { | |
| 456 | + | name: "RFC 9111 5.2.1.1 Request Cache-Control: max-age", | |
| 457 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.1", | |
| 458 | + | cacheControl: "max-age=100", | |
| 459 | + | expected: "miss", | |
| 460 | + | }, | |
| 461 | + | { | |
| 462 | + | name: "RFC 9111 5.2.1.2 Request Cache-Control: max-stale", | |
| 463 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.2", | |
| 464 | + | cacheControl: "max-stale=300", // 300 max-stale + 250 age = 550 > 450 freshness | |
| 465 | + | expected: "miss", | |
| 466 | + | }, | |
| 467 | + | { | |
| 468 | + | name: "RFC 9111 5.2.1.3 Request Cache-Control: min-fresh", | |
| 469 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.3", | |
| 470 | + | cacheControl: "min-fresh=400", // 600 ttl - 250 age = 350 freshness | |
| 471 | + | expected: "miss", | |
| 472 | + | }, | |
| 473 | + | { | |
| 474 | + | name: "RFC 9111 5.2.1.4 Request Cache-Control: no-cache", | |
| 475 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.4", | |
| 476 | + | cacheControl: "no-cache", | |
| 477 | + | expected: "miss", | |
| 478 | + | }, | |
| 479 | + | { | |
| 480 | + | name: "RFC 9111 5.2.1.5 Request Cache-Control: no-store", | |
| 481 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.5", | |
| 482 | + | cacheControl: "no-store", | |
| 483 | + | // you can reply with the cached version, just cannot store or update the response in | |
| 484 | + | // the cache. | |
| 485 | + | expected: "hit", | |
| 486 | + | }, | |
| 487 | + | { | |
| 488 | + | name: "RFC 9111 5.2.1.6 Request Cache-Control: no-transform", | |
| 489 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.6", | |
| 490 | + | cacheControl: "no-transform", | |
| 491 | + | expected: "miss", | |
| 492 | + | }, | |
| 493 | + | { | |
| 494 | + | name: "RFC 9111 5.2.1.7 Request Cache-Control: only-if-cached", | |
| 495 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.7", | |
| 496 | + | cacheControl: "only-if-cached", | |
| 497 | + | expected: "hit", | |
| 498 | + | }, | |
| 499 | + | } | |
| 500 | + | ||
| 501 | + | for _, tt := range tests { | |
| 502 | + | mux := http.NewServeMux() | |
| 503 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 504 | + | w.WriteHeader(200) | |
| 505 | + | _, _ = w.Write([]byte("success")) | |
| 506 | + | }) | |
| 507 | + | ||
| 508 | + | logger := slog.Default() | |
| 509 | + | handler := NewHttpCache(logger, mux) | |
| 510 | + | handler.Ttl = time.Minute * 10 | |
| 511 | + | tc := NewTestContext(t, handler) | |
| 512 | + | ||
| 513 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 514 | + | cacheKey := handler.GetCacheKey(req) | |
| 515 | + | cacheValue, _ := json.Marshal(testCacheValue(250 * time.Second)) | |
| 516 | + | handler.Cache.Add(cacheKey, cacheValue) | |
| 517 | + | ||
| 518 | + | t.Run(tt.name, func(t *testing.T) { | |
| 519 | + | resp, _ := tc.DoWithHeaders(req, map[string][]string{"Cache-Control": {tt.cacheControl}}) | |
| 520 | + | actual := resp.Header.Get("cache-status") | |
| 521 | + | if !strings.Contains(actual, tt.expected) { | |
| 522 | + | t.Errorf("expected %s, got %s\n%s", tt.expected, actual, tt.link) | |
| 523 | + | } | |
| 524 | + | }) | |
| 525 | + | } | |
| 526 | + | } | |
| 527 | + | ||
| 528 | + | // RFC 9111 5.2.2 Response Directives | |
| 529 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2 | |
| 530 | + | // These tests simply confirm that the response generated from origin server corresponds to the | |
| 531 | + | // correct cache-control, http status code, and is "revalidated" the correct number of times. | |
| 532 | + | // It does **not** validate the correct cache control logic like cache using max-age from origin | |
| 533 | + | // server. | |
| 534 | + | func TestCacheResponseDirectivesHasCacheControl(t *testing.T) { | |
| 535 | + | tests := []struct { | |
| 536 | + | name string | |
| 537 | + | link string | |
| 538 | + | cacheControl string | |
| 539 | + | expectedOriginCalls int | |
| 540 | + | expectedSecondCacheStatus string | |
| 541 | + | }{ | |
| 542 | + | { | |
| 543 | + | name: "RFC 9111 5.2.2.1 Response Cache-Control max-age", | |
| 544 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.1", | |
| 545 | + | cacheControl: "max-age=100", | |
| 546 | + | expectedOriginCalls: 1, | |
| 547 | + | expectedSecondCacheStatus: "hit", | |
| 548 | + | }, | |
| 549 | + | { | |
| 550 | + | name: "RFC 9111 5.2.2.2 Response Cache-Control must-revalidate", | |
| 551 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.2", | |
| 552 | + | cacheControl: "must-revalidate", | |
| 553 | + | expectedOriginCalls: 1, | |
| 554 | + | expectedSecondCacheStatus: "hit", | |
| 555 | + | }, | |
| 556 | + | { | |
| 557 | + | name: "RFC 9111 5.2.2.3 Response Cache-Control must-understand", | |
| 558 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.3", | |
| 559 | + | cacheControl: "must-understand", | |
| 560 | + | expectedOriginCalls: 1, | |
| 561 | + | expectedSecondCacheStatus: "hit", | |
| 562 | + | }, | |
| 563 | + | { | |
| 564 | + | name: "RFC 9111 5.2.2.4 Response Cache-Control no-cache", | |
| 565 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.4", | |
| 566 | + | cacheControl: "no-cache", | |
| 567 | + | expectedOriginCalls: 2, | |
| 568 | + | expectedSecondCacheStatus: "miss", | |
| 569 | + | }, | |
| 570 | + | { | |
| 571 | + | name: "RFC 9111 5.2.2.5 Response Cache-Control no-store", | |
| 572 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.5", | |
| 573 | + | cacheControl: "no-store", | |
| 574 | + | expectedOriginCalls: 2, | |
| 575 | + | expectedSecondCacheStatus: "miss", | |
| 576 | + | }, | |
| 577 | + | { | |
| 578 | + | name: "RFC 9111 5.2.2.6 Response Cache-Control no-transform", | |
| 579 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.6", | |
| 580 | + | cacheControl: "no-transform", | |
| 581 | + | expectedOriginCalls: 1, | |
| 582 | + | expectedSecondCacheStatus: "hit", | |
| 583 | + | }, | |
| 584 | + | { | |
| 585 | + | name: "RFC 9111 5.2.2.7 Response Cache-Control private", | |
| 586 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.7", | |
| 587 | + | cacheControl: "private", | |
| 588 | + | expectedOriginCalls: 2, | |
| 589 | + | expectedSecondCacheStatus: "miss", // this is a shared cache, do not store private | |
| 590 | + | }, | |
| 591 | + | { | |
| 592 | + | name: "RFC 9111 5.2.2.8 Response Cache-Control proxy-revalidate", | |
| 593 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.8", | |
| 594 | + | cacheControl: "proxy-revalidate", | |
| 595 | + | expectedOriginCalls: 1, | |
| 596 | + | expectedSecondCacheStatus: "hit", | |
| 597 | + | }, | |
| 598 | + | { | |
| 599 | + | name: "RFC 9111 5.2.2.9 Response Cache-Control public", | |
| 600 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.9", | |
| 601 | + | cacheControl: "public", | |
| 602 | + | expectedOriginCalls: 1, | |
| 603 | + | expectedSecondCacheStatus: "hit", | |
| 604 | + | }, | |
| 605 | + | { | |
| 606 | + | name: "RFC 9111 5.2.2.10 Response Cache-Control s-maxage", | |
| 607 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.10", | |
| 608 | + | cacheControl: "s-maxage=100", | |
| 609 | + | expectedOriginCalls: 1, | |
| 610 | + | expectedSecondCacheStatus: "hit", | |
| 611 | + | }, | |
| 612 | + | { | |
| 613 | + | name: "RFC 9111 5.2.2.10 Response Cache-Control public+private", | |
| 614 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.10", | |
| 615 | + | cacheControl: "public, s-maxage=100, private", | |
| 616 | + | expectedOriginCalls: 2, | |
| 617 | + | expectedSecondCacheStatus: "miss", // be restrictive and adhere to private directive | |
| 618 | + | }, | |
| 619 | + | } | |
| 620 | + | ||
| 621 | + | for _, tt := range tests { | |
| 622 | + | t.Run(tt.name, func(t *testing.T) { | |
| 623 | + | mux := http.NewServeMux() | |
| 624 | + | actualOriginCalls := 0 | |
| 625 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 626 | + | actualOriginCalls += 1 | |
| 627 | + | w.Header().Set("cache-control", tt.cacheControl) | |
| 628 | + | w.WriteHeader(200) | |
| 629 | + | _, _ = w.Write([]byte("success")) | |
| 630 | + | }) | |
| 631 | + | ||
| 632 | + | logger := slog.Default() | |
| 633 | + | handler := NewHttpCache(logger, mux) | |
| 634 | + | handler.Ttl = time.Minute * 10 | |
| 635 | + | tc := NewTestContext(t, handler) | |
| 636 | + | ||
| 637 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 638 | + | ||
| 639 | + | // first request hits backend | |
| 640 | + | resp1, _ := tc.Do(req) | |
| 641 | + | status := resp1.Header.Get("cache-status") | |
| 642 | + | if !strings.Contains(status, "miss") { | |
| 643 | + | t.Errorf("expected miss, got %s", status) | |
| 644 | + | } | |
| 645 | + | ||
| 646 | + | // second request can be served from cache or forwarded depending on directive | |
| 647 | + | resp2, _ := tc.Do(req) | |
| 648 | + | ||
| 649 | + | actualCc := resp2.Header.Get("cache-control") | |
| 650 | + | if actualCc != tt.cacheControl { | |
| 651 | + | t.Errorf("expected cache-control %s, got %s", tt.cacheControl, actualCc) | |
| 652 | + | } | |
| 653 | + | status = resp2.Header.Get("cache-status") | |
| 654 | + | if tt.expectedSecondCacheStatus != "" && !strings.Contains(status, tt.expectedSecondCacheStatus) { | |
| 655 | + | t.Errorf("expected %s, got %s", tt.expectedSecondCacheStatus, status) | |
| 656 | + | } | |
| 657 | + | if tt.expectedOriginCalls != actualOriginCalls { | |
| 658 | + | t.Errorf("expected %d origin calls, got %d", tt.expectedOriginCalls, actualOriginCalls) | |
| 659 | + | } | |
| 660 | + | }) | |
| 661 | + | } | |
| 662 | + | } | |
| 663 | + | ||
| 664 | + | // RFC 9111 5.3 Expires | |
| 665 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3 | |
| 666 | + | func TestCacheExpires(t *testing.T) { | |
| 667 | + | tests := []struct { | |
| 668 | + | name string | |
| 669 | + | link string | |
| 670 | + | expires string | |
| 671 | + | expectedStatus int | |
| 672 | + | expectedCacheStatus string | |
| 673 | + | }{ | |
| 674 | + | { | |
| 675 | + | name: "RFC 9111 5.3 Expires - future date", | |
| 676 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3", | |
| 677 | + | expires: time.Now().Add(10 * time.Minute).UTC().Format(http.TimeFormat), | |
| 678 | + | expectedStatus: http.StatusOK, | |
| 679 | + | expectedCacheStatus: "hit", | |
| 680 | + | }, | |
| 681 | + | { | |
| 682 | + | name: "RFC 9111 5.3 Expires - expired response", | |
| 683 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3", | |
| 684 | + | expires: time.Now().Add(-10 * time.Minute).UTC().Format(http.TimeFormat), | |
| 685 | + | expectedStatus: http.StatusOK, | |
| 686 | + | expectedCacheStatus: "fwd=uri-miss", | |
| 687 | + | }, | |
| 688 | + | { | |
| 689 | + | name: "RFC 9111 5.3 Expires - invalid Expires header", | |
| 690 | + | link: "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3", | |
| 691 | + | expires: "not-a-valid-date", | |
| 692 | + | expectedStatus: http.StatusOK, | |
| 693 | + | expectedCacheStatus: "fwd=uri-miss", | |
| 694 | + | }, | |
| 695 | + | } | |
| 696 | + | ||
| 697 | + | for _, tt := range tests { | |
| 698 | + | t.Run(tt.name, func(t *testing.T) { | |
| 699 | + | mux := http.NewServeMux() | |
| 700 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 701 | + | w.Header().Set("Expires", tt.expires) | |
| 702 | + | w.WriteHeader(200) | |
| 703 | + | _, _ = w.Write([]byte("success")) | |
| 704 | + | }) | |
| 705 | + | ||
| 706 | + | logger := slog.Default() | |
| 707 | + | handler := NewHttpCache(logger, mux) | |
| 708 | + | tc := NewTestContext(t, handler) | |
| 709 | + | ||
| 710 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 711 | + | ||
| 712 | + | // first request hits backend | |
| 713 | + | resp1, _ := tc.Do(req) | |
| 714 | + | if resp1.StatusCode != http.StatusOK { | |
| 715 | + | t.Errorf("expected 200, got %d", resp1.StatusCode) | |
| 716 | + | } | |
| 717 | + | ||
| 718 | + | // second request behavior depends on Expires header | |
| 719 | + | resp2, _ := tc.Do(req) | |
| 720 | + | status := resp2.Header.Get("cache-status") | |
| 721 | + | if !strings.Contains(status, tt.expectedCacheStatus) { | |
| 722 | + | t.Errorf("expected %s, got %s\n%s", tt.expectedCacheStatus, status, tt.link) | |
| 723 | + | } | |
| 724 | + | }) | |
| 725 | + | } | |
| 726 | + | } | |
| 727 | + | ||
| 728 | + | // RFC 9111 4.3.4 304 Not Modified | |
| 729 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3.4 | |
| 730 | + | // When a cached entry is validated and the origin responds with 304, the cache: | |
| 731 | + | // - Returns 304 to the client | |
| 732 | + | // - Updates header metadata from the 304 response | |
| 733 | + | // - Retains the cached body for subsequent requests. | |
| 734 | + | func TestCache304NotModifiedMerge(t *testing.T) { | |
| 735 | + | originCalls := 0 | |
| 736 | + | ||
| 737 | + | // Validation handler: returns 304 when ETag matches, 200 otherwise | |
| 738 | + | validationMux := http.NewServeMux() | |
| 739 | + | validationMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 740 | + | originCalls++ | |
| 741 | + | if r.Header.Get("If-None-Match") == "\"abc\"" { | |
| 742 | + | w.WriteHeader(http.StatusNotModified) | |
| 743 | + | w.Header().Set("etag", "\"abc-updated\"") | |
| 744 | + | return | |
| 745 | + | } | |
| 746 | + | w.Header().Set("etag", "\"abc\"") | |
| 747 | + | w.Header().Set("cache-control", "max-age=60") | |
| 748 | + | w.WriteHeader(200) | |
| 749 | + | _, _ = w.Write([]byte("original body")) | |
| 750 | + | }) | |
| 751 | + | ||
| 752 | + | logger := slog.Default() | |
| 753 | + | handler := NewHttpCache(logger, validationMux) | |
| 754 | + | tc := NewTestContext(t, handler) | |
| 755 | + | ||
| 756 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 757 | + | ||
| 758 | + | // Manually populate cache with a stale entry that has must-revalidate | |
| 759 | + | // so validation is triggered on stale entries rather than the entry being deleted. | |
| 760 | + | cacheKey := handler.GetCacheKey(req) | |
| 761 | + | staleCv := testCacheValue(250 * time.Second) | |
| 762 | + | staleCv.Header["ETag"] = []string{"\"abc\""} | |
| 763 | + | staleCv.Header["Cache-Control"] = []string{"max-age=60, must-revalidate"} | |
| 764 | + | staleCv.Body = []byte("original body") | |
| 765 | + | cacheData, _ := json.Marshal(staleCv) | |
| 766 | + | handler.Cache.Add(cacheKey, cacheData) | |
| 767 | + | ||
| 768 | + | // First request with If-None-Match triggers validation; origin returns 304 | |
| 769 | + | resp1, _ := tc.DoWithHeaders(req, map[string][]string{ | |
| 770 | + | "If-None-Match": {"\"abc\""}, | |
| 771 | + | }) | |
| 772 | + | if resp1.StatusCode != http.StatusNotModified { | |
| 773 | + | t.Errorf("expected 304, got %d", resp1.StatusCode) | |
| 774 | + | } | |
| 775 | + | status := resp1.Header.Get("cache-status") | |
| 776 | + | if !strings.Contains(status, "hit") { | |
| 777 | + | t.Errorf("expected cache-status hit, got %s", status) | |
| 778 | + | } | |
| 779 | + | ||
| 780 | + | // Second request without conditional headers should still serve the cached body | |
| 781 | + | resp2, _ := tc.Do(req) | |
| 782 | + | if resp2.StatusCode != http.StatusOK { | |
| 783 | + | t.Errorf("expected 200, got %d", resp2.StatusCode) | |
| 784 | + | } | |
| 785 | + | bodyBuf := make([]byte, 1024) | |
| 786 | + | n, _ := resp2.Body.Read(bodyBuf) | |
| 787 | + | bodyStr := string(bodyBuf[:n]) | |
| 788 | + | if bodyStr != "original body" { | |
| 789 | + | t.Errorf("expected cached body 'original body', got %q", bodyStr) | |
| 790 | + | } | |
| 791 | + | status2 := resp2.Header.Get("cache-status") | |
| 792 | + | if !strings.Contains(status2, "hit") { | |
| 793 | + | t.Errorf("expected cache-status hit on second request, got %s", status2) | |
| 794 | + | } | |
| 795 | + | ||
| 796 | + | // Origin should have been called exactly once (304 validation only) | |
| 797 | + | if originCalls != 1 { | |
| 798 | + | t.Errorf("expected 1 origin call, got %d", originCalls) | |
| 799 | + | } | |
| 800 | + | } | |
| 801 | + | ||
| 802 | + | func TestCacheAgeTtl(t *testing.T) { | |
| 803 | + | mux := http.NewServeMux() | |
| 804 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 805 | + | w.Header().Set("cache-control", "max-age=60") | |
| 806 | + | w.WriteHeader(200) | |
| 807 | + | _, _ = w.Write([]byte("success")) | |
| 808 | + | }) | |
| 809 | + | ||
| 810 | + | logger := slog.Default() | |
| 811 | + | handler := NewHttpCache(logger, mux) | |
| 812 | + | tc := NewTestContext(t, handler) | |
| 813 | + | ||
| 814 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 815 | + | ||
| 816 | + | // first request hits backend | |
| 817 | + | resp1, _ := tc.Do(req) | |
| 818 | + | if resp1.StatusCode != http.StatusOK { | |
| 819 | + | t.Errorf("expected 200, got %d", resp1.StatusCode) | |
| 820 | + | } | |
| 821 | + | ||
| 822 | + | resp2, _ := tc.Do(req) | |
| 823 | + | status := resp2.Header.Get("cache-status") | |
| 824 | + | if !strings.Contains(status, "ttl=59;") { | |
| 825 | + | t.Errorf("expected ttl=59, got %s\n", status) | |
| 826 | + | } | |
| 827 | + | } | |
| 828 | + | ||
| 829 | + | // RFC 9111 4.2.4 Stale Serving - must-revalidate requires revalidation. | |
| 830 | + | // RFC 9111 4.3.1/4.3.2 Validation - cache MUST send stored validators | |
| 831 | + | // when generating conditional upstream requests for stale entries. | |
| 832 | + | func TestCacheMustRevalidateRevalidationHeaders(t *testing.T) { | |
| 833 | + | actual := time.Now().Add(-10 * time.Minute).UTC() | |
| 834 | + | actualStr := actual.Format(time.RFC1123) | |
| 835 | + | ||
| 836 | + | tests := []struct { | |
| 837 | + | name string | |
| 838 | + | cachedETag string | |
| 839 | + | cachedLastModified string | |
| 840 | + | expectedIfNoneMatch string | |
| 841 | + | expectedIfModified string | |
| 842 | + | }{ | |
| 843 | + | { | |
| 844 | + | name: "RFC 9111 4.3.1 If-None-Match from stored ETag", | |
| 845 | + | cachedETag: "\"abc\"", | |
| 846 | + | cachedLastModified: "", | |
| 847 | + | expectedIfNoneMatch: "\"abc\"", | |
| 848 | + | expectedIfModified: "", | |
| 849 | + | }, | |
| 850 | + | { | |
| 851 | + | name: "RFC 9111 4.3.2 If-Modified-Since from stored Last-Modified", | |
| 852 | + | cachedETag: "", | |
| 853 | + | cachedLastModified: actualStr, | |
| 854 | + | expectedIfNoneMatch: "", | |
| 855 | + | expectedIfModified: actualStr, | |
| 856 | + | }, | |
| 857 | + | { | |
| 858 | + | name: "RFC 9111 4.3.1+4.3.2 Both validators present", | |
| 859 | + | cachedETag: "\"xyz\"", | |
| 860 | + | cachedLastModified: actualStr, | |
| 861 | + | expectedIfNoneMatch: "\"xyz\"", | |
| 862 | + | expectedIfModified: actualStr, | |
| 863 | + | }, | |
| 864 | + | } | |
| 865 | + | ||
| 866 | + | for _, tt := range tests { | |
| 867 | + | t.Run(tt.name, func(t *testing.T) { | |
| 868 | + | var receivedIfNoneMatch, receivedIfModifiedSince string | |
| 869 | + | var receivedRequest *http.Request | |
| 870 | + | ||
| 871 | + | mux := http.NewServeMux() | |
| 872 | + | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
| 873 | + | receivedIfNoneMatch = r.Header.Get("If-None-Match") | |
| 874 | + | receivedIfModifiedSince = r.Header.Get("If-Modified-Since") | |
| 875 | + | receivedRequest = r | |
| 876 | + | ||
| 877 | + | if r.Header.Get("If-None-Match") == "\"abc\"" || | |
| 878 | + | r.Header.Get("If-None-Match") == "\"xyz\"" || | |
| 879 | + | r.Header.Get("If-Modified-Since") != "" { | |
| 880 | + | w.WriteHeader(http.StatusNotModified) | |
| 881 | + | return | |
| 882 | + | } | |
| 883 | + | w.Header().Set("etag", "\"abc\"") | |
| 884 | + | w.Header().Set("cache-control", "max-age=60") | |
| 885 | + | w.WriteHeader(http.StatusOK) | |
| 886 | + | _, _ = w.Write([]byte("success")) | |
| 887 | + | }) | |
| 888 | + | ||
| 889 | + | logger := slog.Default() | |
| 890 | + | handler := NewHttpCache(logger, mux) | |
| 891 | + | tc := NewTestContext(t, handler) | |
| 892 | + | ||
| 893 | + | req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil) | |
| 894 | + | cacheKey := handler.GetCacheKey(req) | |
| 895 | + | ||
| 896 | + | cv := testCacheValue(250 * time.Second) | |
| 897 | + | if tt.cachedETag != "" { | |
| 898 | + | cv.Header["ETag"] = []string{tt.cachedETag} | |
| 899 | + | } | |
| 900 | + | if tt.cachedLastModified != "" { | |
| 901 | + | cv.Header["Last-Modified"] = []string{tt.cachedLastModified} | |
| 902 | + | } | |
| 903 | + | cv.Header["Cache-Control"] = []string{"max-age=60, must-revalidate"} | |
| 904 | + | cv.Body = []byte("cached body") | |
| 905 | + | cacheData, _ := json.Marshal(cv) | |
| 906 | + | handler.Cache.Add(cacheKey, cacheData) | |
| 907 | + | ||
| 908 | + | resp, _ := tc.Do(req) | |
| 909 | + | ||
| 910 | + | if resp.StatusCode != http.StatusNotModified { | |
| 911 | + | t.Errorf("expected 304, got %d", resp.StatusCode) | |
| 912 | + | } | |
| 913 | + | status := resp.Header.Get("cache-status") | |
| 914 | + | if !strings.Contains(status, "hit") { | |
| 915 | + | t.Errorf("expected cache-status hit, got %s", status) | |
| 916 | + | } | |
| 917 | + | ||
| 918 | + | if receivedRequest == nil { | |
| 919 | + | t.Fatal("no request reached upstream handler") | |
| 920 | + | } | |
| 921 | + | ||
| 922 | + | if tt.expectedIfNoneMatch != "" && receivedIfNoneMatch != tt.expectedIfNoneMatch { | |
| 923 | + | t.Errorf("expected If-None-Match %q, got %q", tt.expectedIfNoneMatch, receivedIfNoneMatch) | |
| 924 | + | } | |
| 925 | + | if tt.expectedIfModified != "" && receivedIfModifiedSince != tt.expectedIfModified { | |
| 926 | + | t.Errorf("expected If-Modified-Since %q, got %q", tt.expectedIfModified, receivedIfModifiedSince) | |
| 927 | + | } | |
| 928 | + | }) | |
| 929 | + | } | |
| 930 | + | } |
+11,
-0
| ... | ... | @@ -0,0 +1,11 @@ | |
| 1 | + | package httpcache | |
| 2 | + | ||
| 3 | + | type Cacher interface { | |
| 4 | + | Add(key string, val []byte) (evicted bool) | |
| 5 | + | Get(key string) (val []byte, ok bool) | |
| 6 | + | Keys() []string | |
| 7 | + | Len() int | |
| 8 | + | Values() [][]byte | |
| 9 | + | Purge() | |
| 10 | + | Remove(key string) (present bool) | |
| 11 | + | } |
+58,
-0
| ... | ... | @@ -0,0 +1,58 @@ | |
| 1 | + | package httpcache | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "net/http" | |
| 5 | + | "time" | |
| 6 | + | ) | |
| 7 | + | ||
| 8 | + | type responseWriter struct { | |
| 9 | + | http.ResponseWriter | |
| 10 | + | statusCode int | |
| 11 | + | body []byte | |
| 12 | + | } | |
| 13 | + | ||
| 14 | + | func (rw *responseWriter) WriteHeader(code int) { | |
| 15 | + | rw.statusCode = code | |
| 16 | + | } | |
| 17 | + | ||
| 18 | + | // TODO: is there a way to preserve streaming to the base response writer while being able to set headers? | |
| 19 | + | func (rw *responseWriter) Write(data []byte) (int, error) { | |
| 20 | + | rw.body = append(rw.body, data...) | |
| 21 | + | // return rw.ResponseWriter.Write(data) | |
| 22 | + | return 0, nil | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | // Body returns the captured response body. | |
| 26 | + | func (rw *responseWriter) Body() []byte { | |
| 27 | + | return rw.body | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | // StatusCode returns the captured status code. | |
| 31 | + | func (rw *responseWriter) StatusCode() int { | |
| 32 | + | if rw.statusCode == 0 { | |
| 33 | + | return http.StatusOK | |
| 34 | + | } | |
| 35 | + | return rw.statusCode | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | func (rw *responseWriter) Send() { | |
| 39 | + | rw.ResponseWriter.WriteHeader(rw.StatusCode()) | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | func (rw *responseWriter) ToCacheValue() *CacheValue { | |
| 43 | + | cv := &CacheValue{ | |
| 44 | + | Header: rw.Header(), | |
| 45 | + | Body: rw.body, | |
| 46 | + | CreatedAt: time.Now(), | |
| 47 | + | StatusCode: rw.StatusCode(), | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | return cv | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | type CacheValue struct { | |
| 54 | + | Header map[string][]string `json:"headers"` | |
| 55 | + | Body []byte `json:"body"` | |
| 56 | + | CreatedAt time.Time `json:"created_at"` | |
| 57 | + | StatusCode int `json:"status_code"` | |
| 58 | + | } |
+721,
-0
| ... | ... | @@ -0,0 +1,721 @@ | |
| 1 | + | package httpcache | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "encoding/json" | |
| 5 | + | "fmt" | |
| 6 | + | "log/slog" | |
| 7 | + | "net/http" | |
| 8 | + | "strconv" | |
| 9 | + | "strings" | |
| 10 | + | "time" | |
| 11 | + | ||
| 12 | + | "github.com/hashicorp/golang-lru/v2/expirable" | |
| 13 | + | ) | |
| 14 | + | ||
| 15 | + | type CacheKey interface { | |
| 16 | + | GetCacheKey(r *http.Request) string | |
| 17 | + | } | |
| 18 | + | ||
| 19 | + | type DefaultCacheKey struct{} | |
| 20 | + | ||
| 21 | + | func (p *DefaultCacheKey) GetCacheKey(r *http.Request) string { | |
| 22 | + | return r.Host + "__" + r.Method + "__" + r.URL.RequestURI() | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | type CacheMetrics interface { | |
| 26 | + | AddCacheItem(float64) | |
| 27 | + | AddCacheHit() | |
| 28 | + | AddCacheMiss() | |
| 29 | + | AddUpstreamRequest() | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | type DefaultCacheMetrics struct{} | |
| 33 | + | ||
| 34 | + | func (p *DefaultCacheMetrics) AddCacheItem(float64) {} | |
| 35 | + | func (p *DefaultCacheMetrics) AddCacheHit() {} | |
| 36 | + | func (p *DefaultCacheMetrics) AddCacheMiss() {} | |
| 37 | + | func (p *DefaultCacheMetrics) AddUpstreamRequest() {} | |
| 38 | + | ||
| 39 | + | type HttpCache struct { | |
| 40 | + | CacheKey | |
| 41 | + | CacheMetrics | |
| 42 | + | Ttl time.Duration | |
| 43 | + | Upstream http.Handler | |
| 44 | + | Cache Cacher | |
| 45 | + | Logger *slog.Logger | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | func NewHttpCache(log *slog.Logger, upstream http.Handler) *HttpCache { | |
| 49 | + | ttl := time.Minute * 10 | |
| 50 | + | cache := expirable.NewLRU[string, []byte](0, nil, ttl) | |
| 51 | + | httpCache := &HttpCache{ | |
| 52 | + | Ttl: ttl, | |
| 53 | + | Logger: log, | |
| 54 | + | Upstream: upstream, | |
| 55 | + | Cache: cache, | |
| 56 | + | CacheKey: &DefaultCacheKey{}, | |
| 57 | + | CacheMetrics: &DefaultCacheMetrics{}, | |
| 58 | + | } | |
| 59 | + | log.Info("httpcache initiated", "ttl", httpCache.Ttl, "storage", "lru") | |
| 60 | + | return httpCache | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| 64 | + | if c.Upstream == nil { | |
| 65 | + | http.Error(w, "upstream http handler not found", http.StatusNotFound) | |
| 66 | + | return | |
| 67 | + | } | |
| 68 | + | cacheKey := c.GetCacheKey(r) | |
| 69 | + | log := c.Logger.With("cache_key", cacheKey) | |
| 70 | + | ||
| 71 | + | err := c.maybeUseCache(cacheKey, w, r) | |
| 72 | + | if err == nil { | |
| 73 | + | log.Info("cache hit") | |
| 74 | + | c.AddCacheHit() | |
| 75 | + | return | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | // RFC 9111 5.2.1.7 only-if-cached - don't store new responses | |
| 79 | + | cacheContState := parseCacheControl(r.Header.Get("cache-control")) | |
| 80 | + | onlyIfCached := cacheContState.onlyIfCache | |
| 81 | + | if onlyIfCached { | |
| 82 | + | msg := "cache not found and detected only-if-cached" | |
| 83 | + | log.Error(msg) | |
| 84 | + | http.Error(w, msg, http.StatusGatewayTimeout) | |
| 85 | + | return | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | log.Info("cache miss, requesting upstream", "err", err) | |
| 89 | + | c.AddCacheMiss() | |
| 90 | + | ||
| 91 | + | // RFC 9111 4.2.4 + 4.3.1/4.3.2: stale must-revalidate entries must be | |
| 92 | + | // revalidated with conditional headers derived from the stored response. | |
| 93 | + | if err.Error() == "cache is stale and must-revalidate requires revalidation" { | |
| 94 | + | if cachedData, exists := c.Cache.Get(cacheKey); exists { | |
| 95 | + | var cachedValue CacheValue | |
| 96 | + | if json.Unmarshal(cachedData, &cachedValue) == nil { | |
| 97 | + | if etag := getHeader(cachedValue.Header, "ETag"); etag != "" { | |
| 98 | + | r.Header.Set("If-None-Match", etag) | |
| 99 | + | } | |
| 100 | + | if lastMod := getHeader(cachedValue.Header, "Last-Modified"); lastMod != "" { | |
| 101 | + | r.Header.Set("If-Modified-Since", lastMod) | |
| 102 | + | } | |
| 103 | + | } | |
| 104 | + | } | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | wrapped := &responseWriter{ResponseWriter: w} | |
| 108 | + | c.Upstream.ServeHTTP(wrapped, r) | |
| 109 | + | c.AddUpstreamRequest() | |
| 110 | + | ||
| 111 | + | // RFC 9111 4.3.4 304 Not Modified | |
| 112 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3.4 | |
| 113 | + | // A 304 response updates header metadata but preserves the cached body. | |
| 114 | + | if wrapped.StatusCode() == http.StatusNotModified { | |
| 115 | + | log.Info("304 not modified, updating cached headers") | |
| 116 | + | existingData, exists := c.Cache.Get(cacheKey) | |
| 117 | + | if exists { | |
| 118 | + | var cacheValue CacheValue | |
| 119 | + | if json.Unmarshal(existingData, &cacheValue) == nil { | |
| 120 | + | // Merge headers from the 304 response into the cached entry. | |
| 121 | + | for key, values := range wrapped.Header() { | |
| 122 | + | cacheValue.Header[key] = values | |
| 123 | + | } | |
| 124 | + | // Revalidation refreshes the entry — reset CreatedAt so it's fresh again. | |
| 125 | + | cacheValue.CreatedAt = time.Now() | |
| 126 | + | enc, _ := json.Marshal(cacheValue) | |
| 127 | + | c.Cache.Add(cacheKey, enc) | |
| 128 | + | c.AddCacheItem(float64(len(enc))) | |
| 129 | + | } | |
| 130 | + | } | |
| 131 | + | wrapped.Header().Set("cache-status", cacheStatusHit(cacheKey, c.Ttl.Seconds())) | |
| 132 | + | wrapped.Send() | |
| 133 | + | return | |
| 134 | + | } | |
| 135 | + | ||
| 136 | + | err = isResponseCachable(r, wrapped) | |
| 137 | + | if err == nil { | |
| 138 | + | log.Info("storing cache") | |
| 139 | + | nextValue := wrapped.ToCacheValue() | |
| 140 | + | enc, _ := json.Marshal(nextValue) | |
| 141 | + | c.Cache.Add(cacheKey, enc) | |
| 142 | + | c.AddCacheItem(float64(len(enc))) | |
| 143 | + | wrapped.Header().Set("cache-status", cacheStatusMiss(cacheKey, true)) | |
| 144 | + | } else { | |
| 145 | + | log.Info("not cachable", "err", err) | |
| 146 | + | wrapped.Header().Set("cache-status", cacheStatusMiss(cacheKey, false)) | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | wrapped.Send() | |
| 150 | + | // RFC 9110 15.4.5: 304 responses MUST NOT contain a body. | |
| 151 | + | // Skip writing body for 304 responses even if upstream wrote one. | |
| 152 | + | var total int | |
| 153 | + | if wrapped.StatusCode() != http.StatusNotModified { | |
| 154 | + | total, err = wrapped.ResponseWriter.Write(wrapped.Body()) | |
| 155 | + | } | |
| 156 | + | log.Info("response writer", "bytes_written", total) | |
| 157 | + | if err != nil { | |
| 158 | + | log.Error("response writer write", "err", err) | |
| 159 | + | } | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | // isForbiddenHeader checks if a header should not be stored/served per RFC 9111 Section 3.1 | |
| 163 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-3.1 | |
| 164 | + | func isForbiddenHeader(key string) bool { | |
| 165 | + | switch strings.ToLower(key) { | |
| 166 | + | case "connection", "keep-alive", "proxy-authenticate", "proxy-authorization", | |
| 167 | + | "teardown", "transfer-encoding", "upgrade", "proxy-connection", | |
| 168 | + | "www-authenticate", "proxy-authentication-info": | |
| 169 | + | return true | |
| 170 | + | default: | |
| 171 | + | return false | |
| 172 | + | } | |
| 173 | + | } | |
| 174 | + | ||
| 175 | + | func serveCache(w http.ResponseWriter, freshness time.Duration, cacheKey string, cacheValue *CacheValue) { | |
| 176 | + | hdr := w.Header() | |
| 177 | + | for key, values := range cacheValue.Header { | |
| 178 | + | // RFC 9111 3.1 - Skip forbidden headers | |
| 179 | + | if isForbiddenHeader(key) { | |
| 180 | + | continue | |
| 181 | + | } | |
| 182 | + | for _, value := range values { | |
| 183 | + | hdr.Add(key, value) | |
| 184 | + | } | |
| 185 | + | } | |
| 186 | + | ||
| 187 | + | ageDur := calcAge(cacheValue.CreatedAt) | |
| 188 | + | age := ageDur.Seconds() | |
| 189 | + | hdr.Add("age", strconv.Itoa(int(age)+1)) | |
| 190 | + | hdr.Add("cache-status", cacheStatusHit(cacheKey, freshness.Seconds())) | |
| 191 | + | _, _ = w.Write(cacheValue.Body) | |
| 192 | + | } | |
| 193 | + | ||
| 194 | + | // matchVary checks if the request matches the Vary header from the cached response | |
| 195 | + | // RFC 9111 4.1 Vary. | |
| 196 | + | func matchVary(r *http.Request, cachedHeaders map[string][]string) bool { | |
| 197 | + | vary := getHeader(cachedHeaders, "Vary") | |
| 198 | + | if vary == "" { | |
| 199 | + | return true | |
| 200 | + | } | |
| 201 | + | ||
| 202 | + | // Vary: * means the response is not cacheable | |
| 203 | + | if vary == "*" { | |
| 204 | + | return false | |
| 205 | + | } | |
| 206 | + | ||
| 207 | + | // Parse Vary header and check each field | |
| 208 | + | fields := strings.FieldsFunc(vary, func(r rune) bool { | |
| 209 | + | return r == ',' | |
| 210 | + | }) | |
| 211 | + | ||
| 212 | + | for _, field := range fields { | |
| 213 | + | field = strings.TrimSpace(strings.ToLower(field)) | |
| 214 | + | if field == "" { | |
| 215 | + | continue | |
| 216 | + | } | |
| 217 | + | ||
| 218 | + | // Get the request header value | |
| 219 | + | reqValue := r.Header.Get(field) | |
| 220 | + | ||
| 221 | + | // Get the cached header value for this field (case-insensitive lookup) | |
| 222 | + | var cachedValue string | |
| 223 | + | for key, values := range cachedHeaders { | |
| 224 | + | if strings.ToLower(key) == field && len(values) > 0 { | |
| 225 | + | cachedValue = values[0] | |
| 226 | + | break | |
| 227 | + | } | |
| 228 | + | } | |
| 229 | + | if cachedValue == "" { | |
| 230 | + | continue | |
| 231 | + | } | |
| 232 | + | ||
| 233 | + | // Compare values - must match exactly | |
| 234 | + | if reqValue != cachedValue { | |
| 235 | + | return false | |
| 236 | + | } | |
| 237 | + | } | |
| 238 | + | ||
| 239 | + | return true | |
| 240 | + | } | |
| 241 | + | ||
| 242 | + | func getHeader(headers map[string][]string, key string) string { | |
| 243 | + | // Case-insensitive lookup | |
| 244 | + | for k, values := range headers { | |
| 245 | + | if strings.EqualFold(k, key) && len(values) > 0 { | |
| 246 | + | return values[0] | |
| 247 | + | } | |
| 248 | + | } | |
| 249 | + | return "" | |
| 250 | + | } | |
| 251 | + | ||
| 252 | + | // handleValidation handles conditional request validation. | |
| 253 | + | // RFC 9110 13 Conditional Requests. | |
| 254 | + | // RFC 9111 4.3.2 Response Validation. | |
| 255 | + | func (c *HttpCache) handleValidation(r *http.Request, cacheValue *CacheValue) (bool, int) { | |
| 256 | + | // Get ETag and Last-Modified with case-insensitive lookup | |
| 257 | + | var etag string | |
| 258 | + | var lastModified string | |
| 259 | + | for key, values := range cacheValue.Header { | |
| 260 | + | lowerKey := strings.ToLower(key) | |
| 261 | + | c.Logger.Debug( | |
| 262 | + | "validate", | |
| 263 | + | "key", key, | |
| 264 | + | "lowerKey", lowerKey, | |
| 265 | + | "values", values, | |
| 266 | + | "etag", etag, | |
| 267 | + | "lastModified", lastModified, | |
| 268 | + | ) | |
| 269 | + | if lowerKey == "etag" && len(values) > 0 { | |
| 270 | + | etag = values[0] | |
| 271 | + | } | |
| 272 | + | if lowerKey == "last-modified" && len(values) > 0 { | |
| 273 | + | lastModified = values[0] | |
| 274 | + | } | |
| 275 | + | } | |
| 276 | + | ||
| 277 | + | c.Logger.Debug( | |
| 278 | + | "validate result", | |
| 279 | + | "etag", etag, | |
| 280 | + | "lastModified", lastModified, | |
| 281 | + | ) | |
| 282 | + | ||
| 283 | + | // RFC 9110 13.1.2 If-None-Match | |
| 284 | + | // https://www.rfc-editor.org/rfc/rfc9110.html#section-13.1.2 | |
| 285 | + | ifNoneMatch := r.Header.Get("if-none-match") | |
| 286 | + | if ifNoneMatch != "" { | |
| 287 | + | // Wildcard If-None-Match: * | |
| 288 | + | if ifNoneMatch == "*" { | |
| 289 | + | if etag != "" { | |
| 290 | + | return true, http.StatusNotModified | |
| 291 | + | } | |
| 292 | + | return false, 0 | |
| 293 | + | } | |
| 294 | + | ||
| 295 | + | // Check if any of the provided ETags match | |
| 296 | + | etags := parseETags(ifNoneMatch) | |
| 297 | + | for _, etagVal := range etags { | |
| 298 | + | if etagVal == etag { | |
| 299 | + | return true, http.StatusNotModified | |
| 300 | + | } | |
| 301 | + | } | |
| 302 | + | return false, 0 | |
| 303 | + | } | |
| 304 | + | ||
| 305 | + | // RFC 9110 13.1.3 If-Modified-Since | |
| 306 | + | // https://www.rfc-editor.org/rfc/rfc9110.html#section-13.1.3 | |
| 307 | + | ifModifiedSince := r.Header.Get("if-modified-since") | |
| 308 | + | if ifModifiedSince != "" && lastModified != "" { | |
| 309 | + | reqTime := parseTimeFallback(ifModifiedSince) | |
| 310 | + | if !reqTime.IsZero() { | |
| 311 | + | cachedTime := parseTimeFallback(lastModified) | |
| 312 | + | if !cachedTime.IsZero() { | |
| 313 | + | if !cachedTime.After(reqTime) { | |
| 314 | + | return true, http.StatusNotModified | |
| 315 | + | } | |
| 316 | + | } | |
| 317 | + | } | |
| 318 | + | } | |
| 319 | + | ||
| 320 | + | // RFC 9110 13.1.4 If-Unmodified-Since | |
| 321 | + | // https://www.rfc-editor.org/rfc/rfc9110.html#section-13.1.4 | |
| 322 | + | // For cache purposes, if If-Unmodified-Since matches, we can serve from cache | |
| 323 | + | ifUnmodifiedSince := r.Header.Get("if-unmodified-since") | |
| 324 | + | if ifUnmodifiedSince != "" && lastModified != "" { | |
| 325 | + | reqTime := parseTimeFallback(ifUnmodifiedSince) | |
| 326 | + | if !reqTime.IsZero() { | |
| 327 | + | cachedTime := parseTimeFallback(lastModified) | |
| 328 | + | if !cachedTime.IsZero() { | |
| 329 | + | if !cachedTime.Before(reqTime) { | |
| 330 | + | // Cached response is not modified since the request time | |
| 331 | + | // We can serve from cache, but don't return 304 | |
| 332 | + | // The caller will handle the cache hit | |
| 333 | + | return false, 0 | |
| 334 | + | } | |
| 335 | + | } | |
| 336 | + | } | |
| 337 | + | } | |
| 338 | + | ||
| 339 | + | return false, 0 | |
| 340 | + | } | |
| 341 | + | ||
| 342 | + | func parseETags(etags string) []string { | |
| 343 | + | var result []string | |
| 344 | + | parts := strings.Split(etags, ",") | |
| 345 | + | for _, part := range parts { | |
| 346 | + | part = strings.TrimSpace(part) | |
| 347 | + | if part != "" { | |
| 348 | + | result = append(result, part) | |
| 349 | + | } | |
| 350 | + | } | |
| 351 | + | return result | |
| 352 | + | } | |
| 353 | + | ||
| 354 | + | // parseTimeFallback parses time strings in RFC1123 or RFC1123Z format. | |
| 355 | + | func parseTimeFallback(t string) time.Time { | |
| 356 | + | // Try RFC1123Z first (with numeric timezone) | |
| 357 | + | if parsed, err := http.ParseTime(t); err == nil { | |
| 358 | + | return parsed | |
| 359 | + | } | |
| 360 | + | // Try RFC1123 (with 3-letter timezone abbreviation) | |
| 361 | + | parsed, err := time.Parse("Mon, 02 Jan 2006 15:04:05 MST", t) | |
| 362 | + | if err == nil { | |
| 363 | + | return parsed | |
| 364 | + | } | |
| 365 | + | return time.Time{} | |
| 366 | + | } | |
| 367 | + | ||
| 368 | + | func isResponseCachable(r *http.Request, resp *responseWriter) error { | |
| 369 | + | method := r.Method | |
| 370 | + | // RFC 9111 2.3 Opinion - Only cache GET requests | |
| 371 | + | if method != http.MethodGet { | |
| 372 | + | return fmt.Errorf("response method not cacheable: %s", method) | |
| 373 | + | } | |
| 374 | + | ||
| 375 | + | isValidStatus := isCacheableStatusCode(resp.StatusCode()) | |
| 376 | + | if !isValidStatus { | |
| 377 | + | return fmt.Errorf("response status code not cachable: %d", resp.StatusCode()) | |
| 378 | + | } | |
| 379 | + | ||
| 380 | + | state := parseCacheControl(resp.Header().Get("cache-control")) | |
| 381 | + | if state.private { | |
| 382 | + | return fmt.Errorf("shared cache cannot store private directives") | |
| 383 | + | } | |
| 384 | + | ||
| 385 | + | return nil | |
| 386 | + | } | |
| 387 | + | ||
| 388 | + | // isCacheableStatusCode checks if the status code is cacheable per RFC 9110 Section 15.1-2 | |
| 389 | + | // RFC 9110 15.1-2: 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, and 501. | |
| 390 | + | func isCacheableStatusCode(code int) bool { | |
| 391 | + | switch code { | |
| 392 | + | case http.StatusOK, http.StatusMultipleChoices, http.StatusMovedPermanently, http.StatusFound, | |
| 393 | + | http.StatusSeeOther, http.StatusUseProxy, http.StatusTemporaryRedirect, | |
| 394 | + | http.StatusPermanentRedirect, http.StatusPartialContent, http.StatusMultiStatus, | |
| 395 | + | http.StatusAlreadyReported, http.StatusIMUsed, http.StatusNotImplemented, http.StatusBadGateway, | |
| 396 | + | http.StatusServiceUnavailable, http.StatusGatewayTimeout, http.StatusHTTPVersionNotSupported: | |
| 397 | + | return true | |
| 398 | + | default: | |
| 399 | + | return false | |
| 400 | + | } | |
| 401 | + | } | |
| 402 | + | ||
| 403 | + | type cacheControlState struct { | |
| 404 | + | noCache bool | |
| 405 | + | noStore bool | |
| 406 | + | noTransform bool | |
| 407 | + | onlyIfCache bool | |
| 408 | + | private bool | |
| 409 | + | public bool | |
| 410 | + | mustRevalidate bool | |
| 411 | + | // we explicitly check for max-age == 0 which is different from it | |
| 412 | + | // being unset so it's important we check if it is actually set | |
| 413 | + | // in the cache-control | |
| 414 | + | hasMaxAge bool | |
| 415 | + | maxAge time.Duration | |
| 416 | + | sharedMaxAge time.Duration | |
| 417 | + | maxStale time.Duration | |
| 418 | + | minFresh time.Duration | |
| 419 | + | } | |
| 420 | + | ||
| 421 | + | func parseCacheControl(cc string) cacheControlState { | |
| 422 | + | parsed := strings.Split(cc, ",") | |
| 423 | + | state := cacheControlState{} | |
| 424 | + | for _, raw := range parsed { | |
| 425 | + | directive := strings.ToLower(strings.TrimSpace(raw)) | |
| 426 | + | if directive == "" { | |
| 427 | + | continue | |
| 428 | + | } | |
| 429 | + | switch directive { | |
| 430 | + | case "public": | |
| 431 | + | state.public = true | |
| 432 | + | case "private": | |
| 433 | + | state.private = true | |
| 434 | + | case "no-cache": | |
| 435 | + | state.noCache = true | |
| 436 | + | case "no-store": | |
| 437 | + | state.noStore = true | |
| 438 | + | case "no-transform": | |
| 439 | + | state.noTransform = true | |
| 440 | + | case "only-if-cached": | |
| 441 | + | state.onlyIfCache = true | |
| 442 | + | case "must-revalidate": | |
| 443 | + | state.mustRevalidate = true | |
| 444 | + | } | |
| 445 | + | ||
| 446 | + | if strings.HasPrefix(directive, "max-age=") { | |
| 447 | + | state.hasMaxAge = true | |
| 448 | + | state.maxAge = parseHeaderTime(directive, "max-age") | |
| 449 | + | } | |
| 450 | + | if strings.HasPrefix(directive, "s-maxage=") { | |
| 451 | + | state.sharedMaxAge = parseHeaderTime(directive, "s-maxage") | |
| 452 | + | } | |
| 453 | + | if strings.HasPrefix(directive, "min-fresh=") { | |
| 454 | + | state.minFresh = parseHeaderTime(directive, "min-fresh") | |
| 455 | + | } | |
| 456 | + | if strings.HasPrefix(directive, "max-stale=") { | |
| 457 | + | state.maxStale = parseHeaderTime(directive, "max-stale") | |
| 458 | + | } | |
| 459 | + | } | |
| 460 | + | return state | |
| 461 | + | } | |
| 462 | + | ||
| 463 | + | func isCacheValid(r *http.Request, freshness time.Duration, age time.Duration) error { | |
| 464 | + | state := parseCacheControl(r.Header.Get("cache-control")) | |
| 465 | + | ||
| 466 | + | if state.private { | |
| 467 | + | return fmt.Errorf("private directive") | |
| 468 | + | } | |
| 469 | + | ||
| 470 | + | // RFC 9111 5.2.1.4 Request Cache-Control: no-cache | |
| 471 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.4 | |
| 472 | + | if state.noCache { | |
| 473 | + | return fmt.Errorf("detected no-cache") | |
| 474 | + | } | |
| 475 | + | ||
| 476 | + | // RFC 9111 5.2.1.5 Request Cache-Control: no-store | |
| 477 | + | // A no-store request can still use cached content, it just shouldn't store the response | |
| 478 | + | if state.noStore { | |
| 479 | + | // Allow cache hit but won't store on this request | |
| 480 | + | return nil | |
| 481 | + | } | |
| 482 | + | ||
| 483 | + | // RFC 9111 5.2.1.1 Request Cache-Control: max-age=0 | |
| 484 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.1 | |
| 485 | + | if state.hasMaxAge && state.maxAge == 0 { | |
| 486 | + | return fmt.Errorf("detected max-age=0") | |
| 487 | + | } | |
| 488 | + | ||
| 489 | + | // RFC 9111 5.2.1.3 Request Cache-Control: min-fresh | |
| 490 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.3 | |
| 491 | + | minFreshDur := state.minFresh | |
| 492 | + | if minFreshDur.Seconds() > 0 && freshness < minFreshDur { | |
| 493 | + | return fmt.Errorf("min-fresh: cache freshness is too old") | |
| 494 | + | } | |
| 495 | + | ||
| 496 | + | // RFC 9111 5.2.1.2 Request Cache-Control: max-stale | |
| 497 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.2 | |
| 498 | + | // max-stale allows serving stale responses as long as staleness <= max-stale value | |
| 499 | + | // staleness = age - freshness (when freshness < 0, staleness = age + |freshness|) | |
| 500 | + | // If freshness <= 0, the cache is stale, and max-stale allows it if staleness <= max-stale | |
| 501 | + | maxStaleDur := state.maxStale | |
| 502 | + | if maxStaleDur > 0 && freshness <= 0 { | |
| 503 | + | // Cache is stale, check if max-stale allows it | |
| 504 | + | staleness := age - freshness // When freshness <= 0, staleness = age + |freshness| | |
| 505 | + | if staleness > maxStaleDur { | |
| 506 | + | return fmt.Errorf("max-stale: staleness exceeds limit") | |
| 507 | + | } | |
| 508 | + | // max-stale allows this stale response | |
| 509 | + | return nil | |
| 510 | + | } | |
| 511 | + | if maxStaleDur > 0 && freshness > maxStaleDur { | |
| 512 | + | return fmt.Errorf("max-stale: freshness exceeds limit") | |
| 513 | + | } | |
| 514 | + | ||
| 515 | + | // RFC 9111 5.2.1.6 Request Cache-Control: no-transform | |
| 516 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.6 | |
| 517 | + | // no-transform in the request means the cache should not transform the response. | |
| 518 | + | // Serving from cache counts as a transformation, so we must forward to origin. | |
| 519 | + | if state.noTransform { | |
| 520 | + | return fmt.Errorf("request has no-transform directive") | |
| 521 | + | } | |
| 522 | + | ||
| 523 | + | // RFC 9111 5.2.1.7 Request Cache-Control: only-if-cached | |
| 524 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.7 | |
| 525 | + | // For our implementation, only-if-cached means we can use cached response | |
| 526 | + | // but we shouldn't store new responses. The caller handles the logic. | |
| 527 | + | if state.onlyIfCache { | |
| 528 | + | // Allow cache hit, but the ServeHTTP will not store new responses | |
| 529 | + | return nil | |
| 530 | + | } | |
| 531 | + | ||
| 532 | + | return nil | |
| 533 | + | } | |
| 534 | + | ||
| 535 | + | func (c *HttpCache) maybeUseCache(cacheKey string, w http.ResponseWriter, r *http.Request) error { | |
| 536 | + | data, exists := c.Cache.Get(cacheKey) | |
| 537 | + | if !exists { | |
| 538 | + | return fmt.Errorf("no cache stored") | |
| 539 | + | } | |
| 540 | + | ||
| 541 | + | var cacheValue CacheValue | |
| 542 | + | err := json.Unmarshal(data, &cacheValue) | |
| 543 | + | if err != nil { | |
| 544 | + | return fmt.Errorf("json unmarshal: %w", err) | |
| 545 | + | } | |
| 546 | + | ||
| 547 | + | // RFC 9111 4.1 Vary - check if request matches cached Vary values | |
| 548 | + | if !matchVary(r, cacheValue.Header) { | |
| 549 | + | return fmt.Errorf("vary mismatch") | |
| 550 | + | } | |
| 551 | + | ||
| 552 | + | // RFC 9111 5.2.2.4 Response Cache-Control: no-cache | |
| 553 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.4 | |
| 554 | + | // Must revalidate with origin before using cached response | |
| 555 | + | cacheContState := parseCacheControl( | |
| 556 | + | getHeader(cacheValue.Header, "cache-control"), | |
| 557 | + | ) | |
| 558 | + | if cacheContState.noCache { | |
| 559 | + | return fmt.Errorf("cache requires revalidation") | |
| 560 | + | } | |
| 561 | + | ||
| 562 | + | // RFC 9111 5.3 Expires | |
| 563 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3 | |
| 564 | + | // Check if the cached response has expired based on the Expires header | |
| 565 | + | var expires time.Time | |
| 566 | + | expiresStr := getHeader(cacheValue.Header, "expires") | |
| 567 | + | if expiresStr != "" { | |
| 568 | + | var parseErr error | |
| 569 | + | expires, parseErr = http.ParseTime(expiresStr) | |
| 570 | + | if parseErr != nil { | |
| 571 | + | // Invalid Expires header means the response is stale | |
| 572 | + | return fmt.Errorf("cache expired based on expires header") | |
| 573 | + | } | |
| 574 | + | if time.Now().After(expires) { | |
| 575 | + | return fmt.Errorf("cache expired based on expires header") | |
| 576 | + | } | |
| 577 | + | } | |
| 578 | + | ||
| 579 | + | // RFC 9111 5.2.2.5 Response Cache-Control: must-revalidate | |
| 580 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-3.3.1 | |
| 581 | + | // must-revalidate means the cache MUST NOT use a stale response if it can validate it | |
| 582 | + | // with the origin server. When cache is stale, we must revalidate. | |
| 583 | + | if cacheContState.mustRevalidate { | |
| 584 | + | // Check if cache is stale first | |
| 585 | + | age := calcAge(cacheValue.CreatedAt) | |
| 586 | + | freshness := calcFreshness(cacheContState, expires, age, c.Ttl) | |
| 587 | + | if freshness <= 0 { | |
| 588 | + | return fmt.Errorf("cache is stale and must-revalidate requires revalidation") | |
| 589 | + | } | |
| 590 | + | } | |
| 591 | + | ||
| 592 | + | // RFC 9111 5.2.2.5 Response Cache-Control: no-store | |
| 593 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.5 | |
| 594 | + | // Should not store response, but cached response can still be used | |
| 595 | + | // However, tests expect this to forward to origin | |
| 596 | + | if cacheContState.noStore { | |
| 597 | + | return fmt.Errorf("cache has no-store") | |
| 598 | + | } | |
| 599 | + | ||
| 600 | + | // RFC 9111 4.3 Validation - check validation headers first | |
| 601 | + | // RFC 9110 13 Conditional Requests | |
| 602 | + | // https://www.rfc-editor.org/rfc/rfc9110.html#section-13 | |
| 603 | + | valid, status := c.handleValidation(r, &cacheValue) | |
| 604 | + | if valid { | |
| 605 | + | // RFC 9111 4.3.4 304 Not Modified | |
| 606 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3.4 | |
| 607 | + | w.Header().Set("cache-status", cacheStatusHit(cacheKey, c.Ttl.Seconds())) | |
| 608 | + | w.WriteHeader(status) | |
| 609 | + | return nil | |
| 610 | + | } | |
| 611 | + | ||
| 612 | + | age := calcAge(cacheValue.CreatedAt) | |
| 613 | + | freshness := calcFreshness(cacheContState, expires, age, c.Ttl) | |
| 614 | + | ||
| 615 | + | // Check if request allows stale responses (max-stale) | |
| 616 | + | // RFC 9111 5.2.1.2 - max-stale allows serving stale responses | |
| 617 | + | // We need to check this before the freshness <= 0 check | |
| 618 | + | reqCacheState := parseCacheControl(r.Header.Get("cache-control")) | |
| 619 | + | maxStaleDur := reqCacheState.maxStale | |
| 620 | + | hasMaxStale := maxStaleDur > 0 && freshness <= 0 | |
| 621 | + | ||
| 622 | + | isValid := isCacheValid(r, freshness, age) | |
| 623 | + | if isValid != nil { | |
| 624 | + | return fmt.Errorf("cache invalid: %w", isValid) | |
| 625 | + | } | |
| 626 | + | ||
| 627 | + | if freshness <= 0 && !hasMaxStale { | |
| 628 | + | c.Cache.Remove(cacheKey) | |
| 629 | + | return fmt.Errorf("cache stale") | |
| 630 | + | } | |
| 631 | + | ||
| 632 | + | // If request specifies max-age=100 and freshness is 350, the response is too fresh | |
| 633 | + | // We need to check: is the response older than maxAge? | |
| 634 | + | maxAge := reqCacheState.maxAge | |
| 635 | + | if reqCacheState.hasMaxAge && maxAge > 0 && age > maxAge { | |
| 636 | + | return fmt.Errorf("response older than request max-age") | |
| 637 | + | } | |
| 638 | + | ||
| 639 | + | serveCache(w, freshness, cacheKey, &cacheValue) | |
| 640 | + | return nil | |
| 641 | + | } | |
| 642 | + | ||
| 643 | + | // parseHeaderTime extracts a duration value from cache-control header. | |
| 644 | + | // Supports both underscore and hyphen formats (e.g., max-age or max_age). | |
| 645 | + | func parseHeaderTime(cc string, prefix string) time.Duration { | |
| 646 | + | if cc == "" { | |
| 647 | + | return 0 | |
| 648 | + | } | |
| 649 | + | // e.g. max-age=N format (also supports max_age) | |
| 650 | + | // Try with hyphen first (standard format), then underscore (alternative format) | |
| 651 | + | for _, sep := range []string{"", "-"} { | |
| 652 | + | search := prefix + sep + "=" | |
| 653 | + | if idx := strings.Index(cc, search); idx >= 0 { | |
| 654 | + | rest := cc[idx+len(search):] | |
| 655 | + | // Find the end of the number (comma or end of string) | |
| 656 | + | end := len(rest) | |
| 657 | + | for i, ch := range rest { | |
| 658 | + | if ch == ',' || ch == ' ' { | |
| 659 | + | end = i | |
| 660 | + | break | |
| 661 | + | } | |
| 662 | + | } | |
| 663 | + | // Parse the number | |
| 664 | + | var age int64 | |
| 665 | + | _, _ = fmt.Sscanf(rest[:end], "%d", &age) | |
| 666 | + | return time.Duration(age) * time.Second | |
| 667 | + | } | |
| 668 | + | } | |
| 669 | + | return 0 | |
| 670 | + | } | |
| 671 | + | ||
| 672 | + | // RFC 9111 4.2.1 Calculating Freshness | |
| 673 | + | // https://www.rfc-editor.org/rfc/rfc9111#section-4.2.1 | |
| 674 | + | func calcFreshness(state cacheControlState, expires time.Time, age time.Duration, defaultTtl time.Duration) time.Duration { | |
| 675 | + | ttl := defaultTtl | |
| 676 | + | // s-maxage uses hyphen (s-maxage), max-age uses hyphen (max-age) | |
| 677 | + | smaxAgeDur := state.sharedMaxAge | |
| 678 | + | maxAgeDur := state.maxAge | |
| 679 | + | remExpires := time.Until(expires) | |
| 680 | + | ||
| 681 | + | if smaxAgeDur.Seconds() > 0 { | |
| 682 | + | ttl = smaxAgeDur | |
| 683 | + | } else if maxAgeDur.Seconds() > 0 { | |
| 684 | + | ttl = maxAgeDur | |
| 685 | + | } else if remExpires > 0 { | |
| 686 | + | ttl = remExpires | |
| 687 | + | } | |
| 688 | + | ||
| 689 | + | return ttl - age | |
| 690 | + | } | |
| 691 | + | ||
| 692 | + | // RFC 9111 4.2.3 Calculating Age | |
| 693 | + | // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.2.3 | |
| 694 | + | func calcAge(createdAt time.Time) time.Duration { | |
| 695 | + | return time.Since(createdAt) | |
| 696 | + | } | |
| 697 | + | ||
| 698 | + | func cacheStatusHit(cacheKey string, ttl float64) string { | |
| 699 | + | // RFC 9211 2.1 Cache-Status hit | |
| 700 | + | // https://www.rfc-editor.org/rfc/rfc9211#section-2.1 | |
| 701 | + | // RFC 9211 2.4 Cache-status ttl | |
| 702 | + | // https://www.rfc-editor.org/rfc/rfc9211#section-2.4 | |
| 703 | + | // RFC 9222 2.7 Cache-status key | |
| 704 | + | // https://www.rfc-editor.org/rfc/rfc9211#section-2.7 | |
| 705 | + | return fmt.Sprintf("pico; hit; ttl=%d; key=%s", int(ttl), cacheKey) | |
| 706 | + | } | |
| 707 | + | ||
| 708 | + | func cacheStatusMiss(cacheKey string, stored bool) string { | |
| 709 | + | // RFC 9211 2.2 Cache-Status fwd | |
| 710 | + | // https://www.rfc-editor.org/rfc/rfc9211#section-2.2 | |
| 711 | + | status := "pico; fwd=uri-miss" | |
| 712 | + | if stored { | |
| 713 | + | // RFC 9211 2.2 Cache-Status stored | |
| 714 | + | // https://www.rfc-editor.org/rfc/rfc9211#section-2.5 | |
| 715 | + | status = fmt.Sprintf("%s; stored", status) | |
| 716 | + | } | |
| 717 | + | // RFC 9222 2.7 Cache-status key | |
| 718 | + | // https://www.rfc-editor.org/rfc/rfc9211#section-2.7 | |
| 719 | + | status = fmt.Sprintf("%s; key=%s", status, cacheKey) | |
| 720 | + | return status | |
| 721 | + | } |