76 lines
2.3 KiB
Go
76 lines
2.3 KiB
Go
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"git.threesix.ai/jordan/slack-worker-1770281299/pkg/httpcontext"
|
|
"git.threesix.ai/jordan/slack-worker-1770281299/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
|
|
}
|