Route pattern silently lost for span names and metric labels when auth rebinds the request context
Summary
RoutePatternMiddleware (S01 middleware chain position # 7) reads r.Pattern on the exit path to recover the matched route pattern and propagate it to LabKit logging/tracing via httpserver.SetRoutePattern, giving spans low-cardinality names. The planned HTTP server metrics middleware (S03 metrics framework, #56 (closed), chain position #8 (closed)) follows the same pattern to populate its route metric label.
That read is fragile. Go's net/http.ServeMux stamps r.Pattern on the specific *http.Request value it dispatches. Any middleware positioned inner to #7 (closed)/#8 (closed) that rebinds the request via r = r.WithContext(...) produces a new *http.Request; the mux stamps that inner copy, while #7 (closed)/#8 (closed) still hold the outer copy whose Pattern is "". The matched pattern is silently lost.
Why it is latent today
The current authentication (S08 stub, position #9) and authorization (S09 stub, position #10) middlewares — both inner to #7 (closed)/#8 (closed) — pass the original *http.Request through unchanged (authz is return next; auth calls next.ServeHTTP(w, r) with the original r). So r.Pattern survives the inner chain and both #7 (closed) and #8 (closed) work correctly.
When it breaks
When real authentication replaces the stub and attaches the authenticated identity to the request context — the standard r = r.WithContext(ctxWithIdentity) pattern — route propagation silently breaks:
- tracing spans get high-cardinality / empty names (the exact problem #7 (closed) exists to prevent);
- the metrics
routelabel collapses to""for every request.
No error is raised; the degradation is silent. Latent until real auth lands, hence filed pre-emptively so the new auth implementation is written to avoid it.
Fix direction
Make the matched route pattern survive an inner context rebind, and have both #7 (closed) and #8 (closed) use one shared mechanism. Options:
- a context-stored pointer holder seeded by an outer middleware and written by an innermost shim wrapping the mux — a pointer reached via
ctx.ValuesurvivesWithContext, unlike ther.Patternstruct field; - have the auth/authz middlewares re-propagate
r.Patternonto the request they forward after rebinding; - expose LabKit's own
routePatternHolder(currently seeded only inside the tracing middleware, so inert until tracing lands per #57) independent of tracing.
The new authentication implementation should follow whichever pattern is chosen so it does not silently drop the route pattern.
References
internal/server/middleware.go—RoutePatternMiddleware- S01: HTTP server and routing — middleware chain order (#7 (closed) route pattern, #8 (closed) metrics, #9 auth, #10 authz)
- S03 metrics framework, HTTP server middleware (#56 (closed))