89 lines
2.7 KiB
Go
89 lines
2.7 KiB
Go
package httpx
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
)
|
|
|
|
type bridgeState struct{ parent *Context }
|
|
|
|
// WrapHandler runs an existing standard handler inside the App middleware chain.
|
|
// It preserves the original response, including downloads and streaming output.
|
|
func WrapHandler(handler http.Handler) Handler {
|
|
if handler == nil {
|
|
panic("httpx: nil HTTP handler")
|
|
}
|
|
return func(c *Context) error {
|
|
r := bridgeRequest(c)
|
|
defer CleanupMultipart(r)
|
|
handler.ServeHTTP(c.Raw(), r)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WrapMiddleware adapts synchronous standard HTTP middleware. The middleware is
|
|
// constructed once per chain, preserving its shared state across requests.
|
|
// Errors from next are encoded before unwinding so response-transforming
|
|
// middleware sees them. Preserve the incoming request context (derive with
|
|
// WithContext), and never call next after ServeHTTP returns, as with net/http.
|
|
func WrapMiddleware(middleware func(http.Handler) http.Handler) Middleware {
|
|
if middleware == nil {
|
|
panic("httpx: nil HTTP middleware")
|
|
}
|
|
return func(next Handler) Handler {
|
|
// Each nested bridge has its own key, including repeated use of one adapter.
|
|
key := &bridgeState{}
|
|
wrapped := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
state, ok := r.Context().Value(key).(*bridgeState)
|
|
if !ok {
|
|
panic("httpx: standard middleware must preserve the incoming request context")
|
|
}
|
|
c := state.parent
|
|
child := c.Clone(r.Context(), w)
|
|
child.request = r
|
|
if c.Responded() {
|
|
child.writer.(interface{ inheritCommit(int) }).inheritCommit(c.Status())
|
|
}
|
|
defer CleanupMultipart(r)
|
|
defer func() {
|
|
c.values = child.values
|
|
c.encoder = child.encoder
|
|
c.errorRegistry = child.errorRegistry
|
|
c.failure = child.failure
|
|
}()
|
|
if err := next(child); err != nil {
|
|
child.RecordError(err)
|
|
if !c.Responded() {
|
|
child.WriteError(err)
|
|
}
|
|
}
|
|
}))
|
|
if wrapped == nil {
|
|
panic("httpx: HTTP middleware returned nil handler")
|
|
}
|
|
return func(c *Context) error {
|
|
r := bridgeRequest(c).WithContext(context.WithValue(c.Context(), key, &bridgeState{parent: c}))
|
|
defer CleanupMultipart(r)
|
|
wrapped.ServeHTTP(c.Raw(), r)
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// WrapFuncMiddleware supports middleware shaped as func(http.HandlerFunc) http.HandlerFunc.
|
|
func WrapFuncMiddleware(middleware func(http.HandlerFunc) http.HandlerFunc) Middleware {
|
|
if middleware == nil {
|
|
panic("httpx: nil HTTP middleware")
|
|
}
|
|
return WrapMiddleware(func(next http.Handler) http.Handler { return middleware(next.ServeHTTP) })
|
|
}
|
|
|
|
// Populate Go 1.22 path values without mutating a parent router's request maps.
|
|
func bridgeRequest(c *Context) *http.Request {
|
|
r := c.Request().Clone(c.Context())
|
|
for name, value := range c.params {
|
|
r.SetPathValue(name, value)
|
|
}
|
|
return r
|
|
}
|