xfy ebfa9cc7a8 feat(middleware/requestid): add request ID generation and propagation middleware
Implement Request-ID middleware that generates or propagates X-Request-ID
headers for distributed request tracing.

- Check incoming X-Request-ID header, reuse if present (trust downstream)
- Generate UUID v4 via google/uuid if no incoming ID
- Store ID in ctx.UserValue for variable system and access log access
- Set X-Request-ID response header for client-side tracing
- Add GetRequestID() helper for proxy header propagation
- Registered as first middleware (before AccessLog) so $request_id
  is available throughout the request lifecycle
- 8 unit tests covering generation, propagation, empty header, UUID format
2026-06-11 23:41:24 +08:00

60 lines
1.3 KiB
Go

package requestid
import (
"bytes"
"github.com/google/uuid"
"github.com/valyala/fasthttp"
"rua.plus/lolly/internal/middleware"
)
var requestIDHeader = []byte("X-Request-ID")
// RequestIDMiddleware generates or propagates X-Request-ID for request tracing.
type RequestIDMiddleware struct{}
var _ middleware.Middleware = (*RequestIDMiddleware)(nil)
// New creates a new Request-ID middleware.
func New() *RequestIDMiddleware {
return &RequestIDMiddleware{}
}
// Name returns the middleware name.
func (m *RequestIDMiddleware) Name() string { return "request_id" }
// Process implements the middleware.Middleware interface.
func (m *RequestIDMiddleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
var id string
incoming := ctx.Request.Header.PeekBytes(requestIDHeader)
if len(incoming) > 0 {
trimmed := bytes.TrimSpace(incoming)
if len(trimmed) > 0 {
id = string(trimmed)
}
}
if id == "" {
id = uuid.New().String()
}
ctx.SetUserValue("request_id", id)
ctx.Response.Header.SetBytesKV(requestIDHeader, []byte(id))
next(ctx)
}
}
// GetRequestID extracts the request ID from the request context.
func GetRequestID(ctx *fasthttp.RequestCtx) string {
if v := ctx.UserValue("request_id"); v != nil {
if s, ok := v.(string); ok {
return s
}
}
return ""
}