iris/core/router/status.go

145 lines
4.0 KiB
Go
Raw Normal View History

Publish the new version :airplane: | Look description please! # FAQ ### Looking for free support? http://support.iris-go.com https://kataras.rocket.chat/channel/iris ### Looking for previous versions? https://github.com/kataras/iris#version ### Should I upgrade my Iris? Developers are not forced to upgrade if they don't really need it. Upgrade whenever you feel ready. > Iris uses the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature, so you get truly reproducible builds, as this method guards against upstream renames and deletes. **How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`. For further installation support, please click [here](http://support.iris-go.com/d/16-how-to-install-iris-web-framework). ### About our new home page http://iris-go.com Thanks to [Santosh Anand](https://github.com/santoshanand) the http://iris-go.com has been upgraded and it's really awesome! [Santosh](https://github.com/santoshanand) is a freelancer, he has a great knowledge of nodejs and express js, Android, iOS, React Native, Vue.js etc, if you need a developer to find or create a solution for your problem or task, please contact with him. The amount of the next two or three donations you'll send they will be immediately transferred to his own account balance, so be generous please! Read more at https://github.com/kataras/iris/blob/master/HISTORY.md Former-commit-id: eec2d71bbe011d6b48d2526eb25919e36e5ad94e
2017-06-03 22:22:52 +02:00
// 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)
}