Files
go-omni/httpx/bridge.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
}