package correlation import ( "context" "crypto/rand" "encoding/hex" "net/http" "regexp" ) const Header = "X-Correlation-ID" type contextKey struct{} var validID = regexp.MustCompile(`^[A-Za-z0-9._:-]{8,64}$`) func New() string { bytes := make([]byte, 16) if _, err := rand.Read(bytes); err != nil { return "correlation-unavailable" } return hex.EncodeToString(bytes) } func FromContext(ctx context.Context) string { value, _ := ctx.Value(contextKey{}).(string) return value } func WithContext(ctx context.Context, id string) context.Context { return context.WithValue(ctx, contextKey{}, id) } func Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { id := request.Header.Get(Header) if !validID.MatchString(id) { id = New() } response.Header().Set(Header, id) next.ServeHTTP(response, request.WithContext(WithContext(request.Context(), id))) }) }