http/server/requestid/requestid.go (64 lines of code) (raw):

package requestid import ( "crypto/rand" "encoding/base64" "io" "net/http" "github.com/gorilla/mux" "google.golang.org/grpc/metadata" ) const ( CorrelationIDKey = "correlationID" OperationIDKey = "operationID" ARMClientRequestIDKey = "armClientRequestID" // Details can be found here: // https://github.com/Azure/azure-resource-manager-rpc/blob/master/v1.0/common-api-details.md#client-request-headers RequestCorrelationIDHeader = "x-ms-correlation-request-id" // RequestAcsOperationIDHeader is the http header name ACS RP adds for operation ID (AKS specific) RequestAcsOperationIDHeader = "x-ms-acs-operation-id" // RequestARMClientRequestIDHeader Caller-specified value identifying the request, in the form of a GUID RequestARMClientRequestIDHeader = "x-ms-client-request-id" ) // HeaderExtractor defines a function to extract headers from an HTTP request. // It returns a map where keys are metadata keys and values are the corresponding header values. type HeaderExtractor func(r *http.Request) map[string]string // NewRequestIDMiddleware creates a new RequestID middleware with the default extractor. func NewRequestIDMiddleware() mux.MiddlewareFunc { return NewRequestIDMiddlewareWithExtractor(DefaultHeaderExtractor) } // NewRequestIDMiddlewareWithExtractor creates a new RequestID middleware with a custom extractor. func NewRequestIDMiddlewareWithExtractor(extractor HeaderExtractor) mux.MiddlewareFunc { if extractor == nil { extractor = DefaultHeaderExtractor } return func(next http.Handler) http.Handler { return &requestIDMiddleware{ next: next, extractor: extractor, } } } type requestIDMiddleware struct { next http.Handler extractor HeaderExtractor } func (m *requestIDMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() headers := m.extractor(r) // Create metadata pairs from the extracted headers var mdPairs []string for key, value := range headers { mdPairs = append(mdPairs, key, value) } md := metadata.Pairs(mdPairs...) // Add headers to incoming context metadata to make them available for forwarding ctx = metadata.NewIncomingContext(ctx, md) m.next.ServeHTTP(w, r.WithContext(ctx)) } func DefaultHeaderExtractor(r *http.Request) map[string]string { headers := map[string]string{ string(CorrelationIDKey): r.Header.Get(RequestCorrelationIDHeader), string(OperationIDKey): r.Header.Get(RequestAcsOperationIDHeader), string(ARMClientRequestIDKey): r.Header.Get(RequestARMClientRequestIDHeader), } // Check if AcsOperationIDHeader is missing and generate a new one if needed // TODO (tomabraham): Merge with operationrequest package if headers[string(OperationIDKey)] == "" { newRequestID := generateRequestID() headers["request-id"] = newRequestID } return headers } func generateRequestID() string { b := make([]byte, 6) io.ReadFull(rand.Reader, b) return base64.RawURLEncoding.EncodeToString(b) }