Files
httpx/server/middleware.go
Aleksey Shakhmatov b5259af73e Honor RoundTripper contract in middleware; validate incoming X-Request-Id
BearerAuth, BasicAuth and DefaultHeaders mutated the caller's request, which
violates the RoundTripper contract and risks races on shared/retried requests;
clone before writing headers (matching RequestID). Validate the incoming
X-Request-Id (length and character set) before propagating it to logs and the
response header, preventing log forging and header splitting from a
client-controlled value.
2026-05-23 13:47:38 +03:00

57 lines
1.7 KiB
Go

package server
import "net/http"
// Middleware wraps an http.Handler to add behavior.
// This is the server-side counterpart of the client middleware type
// func(http.RoundTripper) http.RoundTripper.
type Middleware func(http.Handler) http.Handler
// Chain composes middlewares so that Chain(A, B, C)(handler) == A(B(C(handler))).
// Middlewares are applied from right to left: C wraps handler first, then B wraps
// the result, then A wraps last. This means A is the outermost layer and sees
// every request first.
func Chain(mws ...Middleware) Middleware {
return func(h http.Handler) http.Handler {
for i := len(mws) - 1; i >= 0; i-- {
h = mws[i](h)
}
return h
}
}
// statusWriter wraps http.ResponseWriter to capture the response status code.
// It implements Unwrap() so that http.ResponseController can access the
// underlying ResponseWriter's optional interfaces (Flusher, Hijacker, etc.).
type statusWriter struct {
http.ResponseWriter
status int
written bool
}
// WriteHeader captures the status code and delegates to the underlying writer.
func (w *statusWriter) WriteHeader(code int) {
if !w.written {
w.status = code
w.written = true
}
w.ResponseWriter.WriteHeader(code)
}
// Write delegates to the underlying writer, defaulting status to 200 if
// WriteHeader was not called explicitly.
func (w *statusWriter) Write(b []byte) (int, error) {
if !w.written {
w.status = http.StatusOK
w.written = true
}
return w.ResponseWriter.Write(b)
}
// Unwrap returns the underlying ResponseWriter. This is required for
// http.ResponseController to detect optional interfaces like http.Flusher
// and http.Hijacker on the original writer.
func (w *statusWriter) Unwrap() http.ResponseWriter {
return w.ResponseWriter
}