slack5-1770574304/pkg/middleware/request_id.go
jordan 0319f938c9
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/manual/woodpecker Pipeline was successful
Initialize project from skeleton template
2026-02-08 18:11:45 +00:00

76 lines
2.3 KiB
Go

package middleware
import (
"context"
"net/http"
"github.com/google/uuid"
"git.threesix.ai/jordan/slack5-1770574304/pkg/httpcontext"
"git.threesix.ai/jordan/slack5-1770574304/pkg/logging"
)
// RequestIDHeader is the header name for request IDs.
const RequestIDHeader = "X-Request-ID"
// RequestID returns middleware that generates/extracts request IDs.
//
// If X-Request-ID header is present in the incoming request, uses that value.
// This allows clients to set their own request IDs for tracking purposes.
// Otherwise generates a new UUID.
//
// The request ID is stored in context using httpcontext.SetRequestID and
// also set in the X-Request-ID response header for client correlation.
//
// This middleware is idempotent - if a request ID is already present in the
// context (from a previous middleware), it will not be overwritten.
//
// Usage:
//
// r.Use(middleware.RequestID())
// r.Use(middleware.RequestLogger(logger))
//
// In handlers, retrieve the request ID:
//
// requestID, ok := httpcontext.GetRequestID(r.Context())
func RequestID() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check if request ID already in context (idempotent)
if existingID, ok := httpcontext.GetRequestID(r.Context()); ok && existingID != "" {
// Already set, set response header and continue
w.Header().Set(RequestIDHeader, existingID)
next.ServeHTTP(w, r)
return
}
// Try to get request ID from incoming header
requestID := r.Header.Get(RequestIDHeader)
// If not present, generate a new UUID
if requestID == "" {
requestID = uuid.New().String()
}
// Store in context (both httpcontext and logging)
ctx := httpcontext.SetRequestID(r.Context(), requestID)
ctx = logging.WithRequestID(ctx, requestID)
// Set response header for client correlation
w.Header().Set(RequestIDHeader, requestID)
// Continue with request ID in context
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// GetRequestID returns the request ID from context.
// This is a convenience wrapper around httpcontext.GetRequestID.
//
// Returns the request ID string if present, empty string if not found.
func GetRequestID(ctx context.Context) string {
requestID, _ := httpcontext.GetRequestID(ctx)
return requestID
}