forked from kataras/iris
-
Notifications
You must be signed in to change notification settings - Fork 0
/
status.go
156 lines (138 loc) · 4.36 KB
/
status.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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
Handlers context.Handlers
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 {
if w.StatusCode() < 400 { // if not an error status code
w.WriteHeader(ch.StatusCode) // then set it manually here, otherwise it should be setted via ctx.StatusCode(...)
}
// reset if previous content and it's recorder, keep the status code.
w.ClearHeaders()
w.ResetBody()
} 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() > 0 { // != -1, rel: context/context.go#EndRequest
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 */ }})
// use .HandlerIndex
// that sets the current handler index to zero
// in order to:
// ignore previous runs that may changed the handler index,
// via ctx.Next or ctx.StopExecution, if any.
//
// use .Do
// that overrides the existing handlers and sets and runs these error handlers.
// in order to:
// ignore the route's after-handlers, if any.
ctx.HandlerIndex(0)
ctx.Do(ch.Handlers)
}
func (ch *ErrorCodeHandler) updateHandlers(handlers context.Handlers) {
ch.mu.Lock()
ch.Handlers = handlers
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) {
ctx.WriteString(http.StatusText(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, handlers ...context.Handler) *ErrorCodeHandler {
if statusCode < 400 {
return nil
}
h := s.Get(statusCode)
if h == nil {
// create new and add it
ch := &ErrorCodeHandler{
StatusCode: statusCode,
Handlers: handlers,
}
s.handlers = append(s.handlers, ch)
return ch
}
// otherwise update the handlers
h.updateHandlers(handlers)
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)
}