From e5059086d74630f148e05bd9ae3c82d326ddb0bf Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 15 Oct 2026 12:32:27 +0000 Subject: [PATCH] Label a request with an empty path by the / route in metrics When both r.URL.Path and r.URL.RawPath are empty, as for the request line GET http://pixa.example HTTP/1.1, chi routes the request as /, but routePattern asked Find about the empty path and labelled it unmatched. It now uses / then, as chi does. After the rebase onto next, the TODO.md entry is back at the top of Completed Steps. Model: opus-5-5 --- TODO.md | 18 +++++++++--------- internal/middleware/middleware.go | 6 +++++- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/TODO.md b/TODO.md index dd2d75e..529b4cb 100644 --- a/TODO.md +++ b/TODO.md @@ -41,6 +41,15 @@ P2: security: per-origin rate limiting expired. The goroutine runs with the disk cache disabled (`cache_max_bytes: 0`) too, where it does nothing else, as the negative cache is used either way. +- 2026-10-15 the request metrics are labelled by route, not by path (closes + #254): their `handler` label was each request's path, so every new path, a 404 + or an encrypted URL, added series to the Prometheus registry that were never + removed, and `/metrics` listed every encrypted URL's token. The label is now + the pattern of the chi route the request matches, such as `/v1/image/*`, or + `unmatched` for a request that matches none: one the router answers 404 or + 405, or a CORS preflight. The metrics library takes the label before the + request is routed, so the metrics middleware asks the router which route + matches. - 2026-10-15 a cached variant is served while its source URL is in the negative cache (closes #245): `Get` checked the negative cache before the variant cache, so once a fetch of the source failed, as one does when another variant @@ -51,15 +60,6 @@ P2: security: per-origin rate limiting not in the cache is made from the cached source when there is one. `README.md` says so under `/v1/image/`. A refusal from the negative cache now counts as a miss, as any other request that misses the variant cache does. -- 2026-10-15 the request metrics are labelled by route, not by path (closes - #254): their `handler` label was each request's path, so every new path, a 404 - or an encrypted URL, added series to the Prometheus registry that were never - removed, and `/metrics` listed every encrypted URL's token. The label is now - the pattern of the chi route the request matches, such as `/v1/image/*`, or - `unmatched` for a request that matches none: one the router answers 404 or - 405, or a CORS preflight. The metrics library takes the label before the - request is routed, so the metrics middleware asks the router which route - matches. - 2026-10-15 the host of a `/v1/image/` URL is lower-cased, and one with a port needs a signature (closes #246): with `cdn.example.com` allowlisted, `CDN.Example.com` and `cdn.example.com:8443` passed the allowlist too, and diff --git a/internal/middleware/middleware.go b/internal/middleware/middleware.go index 4745a86..a35c23e 100644 --- a/internal/middleware/middleware.go +++ b/internal/middleware/middleware.go @@ -239,7 +239,7 @@ func (s *Middleware) Metrics() func(http.Handler) http.Handler { // "unmatched" when it matches none or is not served by a chi router. The // metrics library takes the label before the request is routed, so the // router is asked which route matches, with the path it routes on: -// r.URL.RawPath when set, else r.URL.Path. +// r.URL.RawPath when set, else r.URL.Path, else /. func routePattern(r *http.Request) string { pattern := "" @@ -250,6 +250,10 @@ func routePattern(r *http.Request) string { path = r.URL.Path } + if path == "" { + path = "/" + } + pattern = rctx.Routes.Find(chi.NewRouteContext(), r.Method, path) // For the path a subrouter is mounted at, such as /v1 or /v1/, Find