feat: middleware reach, response writer interfaces, and CORS fixes
- Run global middleware for all requests, including OPTIONS preflights, NotFound, and MethodNotAllowed — previously bypassed by httprouter's internal handling - Implement Hijacker, Flusher, and Pusher on the response writer for WebSocket, SSE, and HTTP/2 push support - Fix CORS: echo a single matching origin, handle AllowCredentials with wildcard, append Vary: Origin - Logger logs from a defer to capture correct status on panicked requests - Static and StaticFS accept route middleware; add Context.AddHeader; warn on NotFound handler override
This commit is contained in:
95
kite.go
95
kite.go
@@ -17,12 +17,16 @@ type Kite struct {
|
||||
errorHandler ErrorHandler
|
||||
mws []Middleware
|
||||
|
||||
customNotFound bool
|
||||
|
||||
serverReadTimeout time.Duration
|
||||
serverWriteTimeout time.Duration
|
||||
serverIdleTimeout time.Duration
|
||||
serverShutdownTimeout time.Duration
|
||||
}
|
||||
|
||||
type kiteContextKey struct{}
|
||||
|
||||
func New() *Kite {
|
||||
k := &Kite{
|
||||
r: httprouter.New(),
|
||||
@@ -34,7 +38,7 @@ func New() *Kite {
|
||||
}
|
||||
|
||||
k.SetErrorHandler(defaultErrorHandler)
|
||||
k.SetNotFoundHandler(defaultNotFoundHandler)
|
||||
k.setNotFoundHandler(defaultNotFoundHandler)
|
||||
k.SetMethodNotAllowedHandler(defaultMethodNotAllowedHandler)
|
||||
|
||||
return k
|
||||
@@ -44,17 +48,33 @@ func (k *Kite) SetErrorHandler(errorHandler ErrorHandler) {
|
||||
k.errorHandler = errorHandler
|
||||
}
|
||||
|
||||
func (k *Kite) SetNotFoundHandler(notFoundHandler NotFoundHandler) {
|
||||
func (k *Kite) setNotFoundHandler(notFoundHandler NotFoundHandler) {
|
||||
k.r.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := &Context{ctx: r.Context(), w: newResponseWriter(w), r: r}
|
||||
notFoundHandler(ctx)
|
||||
ctx := r.Context().Value(kiteContextKey{}).(*Context)
|
||||
if err := notFoundHandler(ctx); err != nil {
|
||||
if err := k.errorHandler(ctx, err); err != nil {
|
||||
log.Printf("[Kite] Error handler failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (k *Kite) SetNotFoundHandler(notFoundHandler NotFoundHandler) {
|
||||
if k.customNotFound {
|
||||
log.Println("[Kite] SetNotFoundHandler is overriding a previously configured NotFound handler")
|
||||
}
|
||||
k.setNotFoundHandler(notFoundHandler)
|
||||
k.customNotFound = true
|
||||
}
|
||||
|
||||
func (k *Kite) SetMethodNotAllowedHandler(methodNotAllowedHandler MethodNotAllowedHandler) {
|
||||
k.r.MethodNotAllowed = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := &Context{ctx: r.Context(), w: newResponseWriter(w), r: r}
|
||||
methodNotAllowedHandler(ctx)
|
||||
ctx := r.Context().Value(kiteContextKey{}).(*Context)
|
||||
if err := methodNotAllowedHandler(ctx); err != nil {
|
||||
if err := k.errorHandler(ctx, err); err != nil {
|
||||
log.Printf("[Kite] Error handler failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -77,7 +97,7 @@ func (k *Kite) SetServerShutdownTimeout(d time.Duration) {
|
||||
func (k *Kite) Start(listenAddr string) error {
|
||||
srv := &http.Server{
|
||||
Addr: listenAddr,
|
||||
Handler: k.r,
|
||||
Handler: k.buildHandler(),
|
||||
|
||||
ReadTimeout: k.serverReadTimeout,
|
||||
WriteTimeout: k.serverWriteTimeout,
|
||||
@@ -105,7 +125,7 @@ func (k *Kite) Start(listenAddr string) error {
|
||||
func (k *Kite) StartTLS(listenAddr, certFile, keyFile string) error {
|
||||
srv := &http.Server{
|
||||
Addr: listenAddr,
|
||||
Handler: k.r,
|
||||
Handler: k.buildHandler(),
|
||||
|
||||
ReadTimeout: k.serverReadTimeout,
|
||||
WriteTimeout: k.serverWriteTimeout,
|
||||
@@ -135,6 +155,11 @@ func (k *Kite) SPA(root string) {
|
||||
}
|
||||
|
||||
func (k *Kite) SPAFS(fs http.FileSystem) {
|
||||
if k.customNotFound {
|
||||
log.Println("[Kite] SPAFS is overriding a previously configured NotFound handler")
|
||||
}
|
||||
k.customNotFound = true
|
||||
|
||||
fileServer := http.FileServer(fs)
|
||||
|
||||
k.r.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -150,18 +175,24 @@ func (k *Kite) SPAFS(fs http.FileSystem) {
|
||||
})
|
||||
}
|
||||
|
||||
func (k *Kite) Static(prefix, root string) {
|
||||
k.StaticFS(prefix, http.Dir(root))
|
||||
func (k *Kite) Static(prefix, root string, mws ...Middleware) {
|
||||
k.StaticFS(prefix, http.Dir(root), mws...)
|
||||
}
|
||||
|
||||
func (k *Kite) StaticFS(prefix string, fs http.FileSystem) {
|
||||
func (k *Kite) StaticFS(prefix string, fs http.FileSystem, mws ...Middleware) {
|
||||
if prefix == "" || prefix[len(prefix)-1] != '/' {
|
||||
prefix += "/"
|
||||
}
|
||||
|
||||
handler := http.StripPrefix(prefix, http.FileServer(fs))
|
||||
k.r.Handler(http.MethodGet, prefix+"*filepath", handler)
|
||||
k.r.Handler(http.MethodHead, prefix+"*filepath", handler)
|
||||
|
||||
h := func(ctx *Context) error {
|
||||
handler.ServeHTTP(ctx.w, ctx.r)
|
||||
return nil
|
||||
}
|
||||
|
||||
k.handle(http.MethodGet, prefix+"*filepath", h, mws...)
|
||||
k.handle(http.MethodHead, prefix+"*filepath", h, mws...)
|
||||
}
|
||||
|
||||
func (k *Kite) Group(prefix string, mws ...Middleware) *Group {
|
||||
@@ -208,25 +239,41 @@ func (k *Kite) TRACE(path string, h Handler, mws ...Middleware) {
|
||||
k.handle(http.MethodTrace, path, h, mws...)
|
||||
}
|
||||
|
||||
func (k *Kite) buildHandler() http.Handler {
|
||||
inner := func(ctx *Context) error {
|
||||
ctx.r = ctx.r.WithContext(context.WithValue(ctx.r.Context(), kiteContextKey{}, ctx))
|
||||
k.r.ServeHTTP(ctx.w, ctx.r)
|
||||
return nil
|
||||
}
|
||||
|
||||
wrapped := inner
|
||||
for i := len(k.mws) - 1; i >= 0; i-- {
|
||||
wrapped = k.mws[i](wrapped)
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := &Context{
|
||||
w: newResponseWriter(w),
|
||||
r: r,
|
||||
}
|
||||
if err := wrapped(ctx); err != nil {
|
||||
if err := k.errorHandler(ctx, err); err != nil {
|
||||
log.Printf("[Kite] Error handler failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (k *Kite) handle(method, path string, h Handler, mws ...Middleware) {
|
||||
k.r.Handle(method, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||
ctx := &Context{
|
||||
ctx: r.Context(),
|
||||
w: newResponseWriter(w),
|
||||
r: r,
|
||||
p: p,
|
||||
}
|
||||
ctx := r.Context().Value(kiteContextKey{}).(*Context)
|
||||
ctx.p = p
|
||||
|
||||
wrappedHandler := h
|
||||
|
||||
for i := len(mws) - 1; i >= 0; i-- {
|
||||
wrappedHandler = mws[i](wrappedHandler)
|
||||
}
|
||||
|
||||
for i := len(k.mws) - 1; i >= 0; i-- {
|
||||
wrappedHandler = k.mws[i](wrappedHandler)
|
||||
}
|
||||
|
||||
if err := wrappedHandler(ctx); err != nil {
|
||||
if err := k.errorHandler(ctx, err); err != nil {
|
||||
log.Printf("[Kite] Error handler failed: %v\n", err)
|
||||
|
||||
Reference in New Issue
Block a user