Files
Cian Johnston 32354261d3 chore(coderd/httpmw): extract HTTPRoute middleware (#21498)
Extracts part of the prometheus middleware that stores the route
information in the request context into its own middleware. Also adds
request method information to context.

Relates to https://github.com/coder/internal/issues/1214
2026-01-15 10:26:50 +00:00

72 lines
1.7 KiB
Go

package httpmw
import (
"context"
"net/http"
"strings"
"github.com/go-chi/chi/v5"
)
type (
httpRouteInfoKey struct{}
)
type httpRouteInfo struct {
Route string
Method string
}
// ExtractHTTPRoute retrieves just the HTTP route pattern from context.
// Returns empty string if not set.
func ExtractHTTPRoute(ctx context.Context) string {
ri, _ := ctx.Value(httpRouteInfoKey{}).(httpRouteInfo)
return ri.Route
}
// ExtractHTTPMethod retrieves just the HTTP method from context.
// Returns empty string if not set.
func ExtractHTTPMethod(ctx context.Context) string {
ri, _ := ctx.Value(httpRouteInfoKey{}).(httpRouteInfo)
return ri.Method
}
// HTTPRoute is middleware that stores the HTTP route pattern and method in
// context for use by downstream handlers and services (e.g. prometheus).
func HTTPRoute(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
route := getRoutePattern(r)
ctx := context.WithValue(r.Context(), httpRouteInfoKey{}, httpRouteInfo{
Route: route,
Method: r.Method,
})
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func getRoutePattern(r *http.Request) string {
rctx := chi.RouteContext(r.Context())
if rctx == nil {
return ""
}
routePath := r.URL.Path
if r.URL.RawPath != "" {
routePath = r.URL.RawPath
}
tctx := chi.NewRouteContext()
routes := rctx.Routes
if routes != nil && !routes.Match(tctx, r.Method, routePath) {
// No matching pattern. /api/* requests will be matched as "UNKNOWN"
// All other ones will be matched as "STATIC".
if strings.HasPrefix(routePath, "/api/") {
return "UNKNOWN"
}
return "STATIC"
}
// tctx has the updated pattern, since Match mutates it
return tctx.RoutePattern()
}