package middleware import ( "crypto/rand" "encoding/hex" "strings" "kra/pkg/logging" "github.com/gin-gonic/gin" "github.com/google/uuid" ) func randomHex(bytes int) string { value := make([]byte, bytes) _, _ = rand.Read(value) return hex.EncodeToString(value) } func RequestMeta() gin.HandlerFunc { return func(c *gin.Context) { requestID := c.GetHeader("X-Request-Id") if !saneHeaderID(requestID) { requestID = uuid.NewString() } traceID, parentSpanID := "", "" if upstreamTraceID, upstreamSpanID, ok := parseTraceparent(c.GetHeader("traceparent")); ok { traceID, parentSpanID = upstreamTraceID, upstreamSpanID } else if candidate := c.GetHeader("X-Trace-Id"); saneHeaderID(candidate) { traceID = candidate } if traceID == "" { traceID = randomHex(16) } spanID := randomHex(8) c.Header("X-Request-Id", requestID) c.Header("X-Trace-Id", traceID) if validTraceID(traceID) { c.Header("traceparent", "00-"+traceID+"-"+spanID+"-01") } c.Set("request_id", requestID) c.Set("trace_id", traceID) c.Set("span_id", spanID) c.Set("parent_span_id", parentSpanID) c.Request = c.Request.WithContext(logging.WithContextFields(c.Request.Context(), &logging.ContextFields{ RequestID: requestID, TraceID: traceID, SpanID: spanID, ParentSpanID: parentSpanID, DeviceID: c.GetHeader("X-Device-Id"), ClientIP: c.ClientIP(), HTTPMethod: c.Request.Method, HTTPPath: c.Request.URL.Path, })) c.Next() } } func parseTraceparent(value string) (traceID, parentSpanID string, ok bool) { parts := strings.Split(value, "-") if len(parts) < 4 { return "", "", false } version, traceID, parentSpanID, flags := parts[0], parts[1], parts[2], parts[3] if !lowerHex(version, 2) || version == "ff" || version == "00" && len(parts) != 4 || !validTraceID(traceID) || !lowerHex(parentSpanID, 16) || allZero(parentSpanID) || !lowerHex(flags, 2) { return "", "", false } return traceID, parentSpanID, true } func saneHeaderID(value string) bool { if value == "" || len(value) > 64 { return false } for index := 0; index < len(value); index++ { if value[index] <= 0x20 || value[index] > 0x7e { return false } } return true } func validTraceID(value string) bool { return lowerHex(value, 32) && !allZero(value) } func lowerHex(value string, length int) bool { if len(value) != length { return false } for index := 0; index < len(value); index++ { if current := value[index]; current < '0' || current > '9' { if current < 'a' || current > 'f' { return false } } } return true } func allZero(value string) bool { for index := 0; index < len(value); index++ { if value[index] != '0' { return false } } return true }