mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
145 lines
4.0 KiB
Go
145 lines
4.0 KiB
Go
|
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package router
|
||
|
|
||
|
import (
|
||
|
"net/http" // just for status codes
|
||
|
"sync"
|
||
|
|
||
|
"github.com/kataras/iris/context"
|
||
|
)
|
||
|
|
||
|
// ErrorCodeHandler is the entry
|
||
|
// of the list of all http error code handlers.
|
||
|
type ErrorCodeHandler struct {
|
||
|
StatusCode int
|
||
|
Handler context.Handler
|
||
|
mu sync.Mutex
|
||
|
}
|
||
|
|
||
|
// Fire executes the specific an error http error status.
|
||
|
// it's being wrapped to make sure that the handler
|
||
|
// will render correctly.
|
||
|
func (ch *ErrorCodeHandler) Fire(ctx context.Context) {
|
||
|
// if we can reset the body
|
||
|
if w, ok := ctx.IsRecording(); ok {
|
||
|
// reset if previous content and it's recorder
|
||
|
w.Reset()
|
||
|
} else if w, ok := ctx.ResponseWriter().(*context.GzipResponseWriter); ok {
|
||
|
// reset and disable the gzip in order to be an expected form of http error result
|
||
|
w.ResetBody()
|
||
|
w.Disable()
|
||
|
} else {
|
||
|
// if we can't reset the body and the body has been filled
|
||
|
// which means that the status code already sent,
|
||
|
// then do not fire this custom error code.
|
||
|
if ctx.ResponseWriter().Written() != -1 {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
// ctx.StopExecution() // not uncomment this, is here to remember why to.
|
||
|
// note for me: I don't stopping the execution of the other handlers
|
||
|
// because may the user want to add a fallback error code
|
||
|
// i.e
|
||
|
// users := app.Party("/users")
|
||
|
// users.Done(func(ctx context.Context){ if ctx.StatusCode() == 400 { /* custom error code for /users */ }})
|
||
|
ch.Handler(ctx)
|
||
|
}
|
||
|
|
||
|
func (ch *ErrorCodeHandler) updateHandler(h context.Handler) {
|
||
|
ch.mu.Lock()
|
||
|
ch.Handler = h
|
||
|
ch.mu.Unlock()
|
||
|
}
|
||
|
|
||
|
// ErrorCodeHandlers contains the http error code handlers.
|
||
|
// User of this struct can register, get
|
||
|
// a status code handler based on a status code or
|
||
|
// fire based on a receiver context.
|
||
|
type ErrorCodeHandlers struct {
|
||
|
handlers []*ErrorCodeHandler
|
||
|
}
|
||
|
|
||
|
func defaultErrorCodeHandlers() *ErrorCodeHandlers {
|
||
|
chs := new(ErrorCodeHandlers)
|
||
|
// register some common error handlers.
|
||
|
// Note that they can be registered on-fly but
|
||
|
// we don't want to reduce the performance even
|
||
|
// on the first failed request.
|
||
|
for _, statusCode := range []int{
|
||
|
http.StatusNotFound,
|
||
|
http.StatusMethodNotAllowed,
|
||
|
http.StatusInternalServerError} {
|
||
|
chs.Register(statusCode, statusText(statusCode))
|
||
|
}
|
||
|
|
||
|
return chs
|
||
|
}
|
||
|
|
||
|
func statusText(statusCode int) context.Handler {
|
||
|
return func(ctx context.Context) {
|
||
|
if _, err := ctx.WriteString(http.StatusText(statusCode)); err != nil {
|
||
|
// ctx.Application().Log("(status code: %d) %s",
|
||
|
// err.Error(), statusCode)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get returns an http error handler based on the "statusCode".
|
||
|
// If not found it returns nil.
|
||
|
func (s *ErrorCodeHandlers) Get(statusCode int) *ErrorCodeHandler {
|
||
|
for i, n := 0, len(s.handlers); i < n; i++ {
|
||
|
if h := s.handlers[i]; h.StatusCode == statusCode {
|
||
|
return h
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Register registers an error http status code
|
||
|
// based on the "statusCode" >= 400.
|
||
|
// The handler is being wrapepd by a generic
|
||
|
// handler which will try to reset
|
||
|
// the body if recorder was enabled
|
||
|
// and/or disable the gzip if gzip response recorder
|
||
|
// was active.
|
||
|
func (s *ErrorCodeHandlers) Register(statusCode int, handler context.Handler) *ErrorCodeHandler {
|
||
|
if statusCode < 400 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
h := s.Get(statusCode)
|
||
|
if h == nil {
|
||
|
ch := &ErrorCodeHandler{
|
||
|
StatusCode: statusCode,
|
||
|
Handler: handler,
|
||
|
}
|
||
|
s.handlers = append(s.handlers, ch)
|
||
|
// create new and add it
|
||
|
return ch
|
||
|
}
|
||
|
// otherwise update the handler
|
||
|
h.updateHandler(handler)
|
||
|
return h
|
||
|
}
|
||
|
|
||
|
// Fire executes an error http status code handler
|
||
|
// based on the context's status code.
|
||
|
//
|
||
|
// If a handler is not already registered,
|
||
|
// then it creates & registers a new trivial handler on the-fly.
|
||
|
func (s *ErrorCodeHandlers) Fire(ctx context.Context) {
|
||
|
statusCode := ctx.GetStatusCode()
|
||
|
if statusCode < 400 {
|
||
|
return
|
||
|
}
|
||
|
ch := s.Get(statusCode)
|
||
|
if ch == nil {
|
||
|
ch = s.Register(statusCode, statusText(statusCode))
|
||
|
}
|
||
|
|
||
|
ch.Fire(ctx)
|
||
|
}
|