Introduces internal/requestid package with shared context key to avoid circular imports between server and middleware packages. Server's RequestID middleware now uses the shared key. Client middleware picks up the ID from context and sets X-Request-Id on outgoing requests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
52 lines
1.4 KiB
Go
52 lines
1.4 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"git.codelab.vc/pkg/httpx/internal/requestid"
|
|
)
|
|
|
|
// RequestID returns a middleware that assigns a unique request ID to each
|
|
// request. If the incoming request already has an X-Request-Id header, that
|
|
// value is used. Otherwise a new UUID v4 is generated via crypto/rand.
|
|
//
|
|
// The request ID is stored in the request context (retrieve with
|
|
// RequestIDFromContext) and set on the response X-Request-Id header.
|
|
func RequestID() Middleware {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
id := r.Header.Get("X-Request-Id")
|
|
if id == "" {
|
|
id = newUUID()
|
|
}
|
|
|
|
ctx := requestid.NewContext(r.Context(), id)
|
|
w.Header().Set("X-Request-Id", id)
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
})
|
|
}
|
|
}
|
|
|
|
// RequestIDFromContext returns the request ID from the context, or an empty
|
|
// string if none is set.
|
|
func RequestIDFromContext(ctx context.Context) string {
|
|
return requestid.FromContext(ctx)
|
|
}
|
|
|
|
// newUUID generates a UUID v4 string using crypto/rand.
|
|
func newUUID() string {
|
|
var uuid [16]byte
|
|
_, _ = rand.Read(uuid[:])
|
|
|
|
// Set version 4 (bits 12-15 of time_hi_and_version).
|
|
uuid[6] = (uuid[6] & 0x0f) | 0x40
|
|
// Set variant bits (10xx).
|
|
uuid[8] = (uuid[8] & 0x3f) | 0x80
|
|
|
|
return fmt.Sprintf("%x-%x-%x-%x-%x",
|
|
uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:16])
|
|
}
|