...
1
2
3
4
5
6
7
8 package hatpear
9
10 import (
11 "context"
12 "fmt"
13 "io"
14 "net/http"
15 "runtime"
16 )
17
18 type contextKey int
19
20 const (
21 errorKey contextKey = iota
22 )
23
24
25
26 func Store(r *http.Request, err error) {
27 errptr, ok := r.Context().Value(errorKey).(*error)
28 if !ok {
29 panic("hatpear: request not configured to store errors")
30 }
31
32 if err != nil {
33 *errptr = err
34 }
35 }
36
37
38
39 func Get(r *http.Request) error {
40 errptr, ok := r.Context().Value(errorKey).(*error)
41 if !ok {
42 return nil
43 }
44 return *errptr
45 }
46
47
48 type Middleware func(http.Handler) http.Handler
49
50
51
52
53
54 func Catch(h func(w http.ResponseWriter, r *http.Request, err error)) Middleware {
55 return func(next http.Handler) http.Handler {
56 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
57 var err error
58 ctx := context.WithValue(r.Context(), errorKey, &err)
59
60 next.ServeHTTP(w, r.WithContext(ctx))
61 if err != nil {
62 h(w, r, err)
63 }
64 })
65 }
66 }
67
68
69 type Handler interface {
70 ServeHTTP(w http.ResponseWriter, r *http.Request) error
71 }
72
73
74 type HandlerFunc func(w http.ResponseWriter, r *http.Request) error
75
76 func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
77 return f(w, r)
78 }
79
80
81
82 func Try(h Handler) http.Handler {
83 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
84 err := h.ServeHTTP(w, r)
85 Store(r, err)
86 })
87 }
88
89
90
91 func TryFunc(h func(http.ResponseWriter, *http.Request) error) http.Handler {
92 return Try(HandlerFunc(h))
93 }
94
95 var (
96
97 RecoverStackDepth = 32
98 )
99
100
101
102 func Recover() Middleware {
103 return func(next http.Handler) http.Handler {
104 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
105 defer func() {
106 if v := recover(); v != nil {
107 Store(r, PanicError{
108 value: v,
109 stack: stack(1),
110 })
111 }
112 }()
113 next.ServeHTTP(w, r)
114 })
115 }
116 }
117
118 func stack(skip int) []runtime.Frame {
119 rpc := make([]uintptr, RecoverStackDepth)
120
121 n := runtime.Callers(skip+2, rpc)
122 frames := runtime.CallersFrames(rpc[0:n])
123
124 var stack []runtime.Frame
125 for {
126 f, more := frames.Next()
127 if !more {
128 break
129 }
130 stack = append(stack, f)
131 }
132 return stack
133 }
134
135
136 type PanicError struct {
137 value interface{}
138 stack []runtime.Frame
139 }
140
141
142 func (e PanicError) Value() interface{} {
143 return e.value
144 }
145
146
147 func (e PanicError) StackTrace() []runtime.Frame {
148 return e.stack
149 }
150
151
152
153
154
155
156
157
158
159 func (e PanicError) Format(s fmt.State, verb rune) {
160 switch verb {
161 case 's':
162 io.WriteString(s, e.Error())
163 case 'v':
164 io.WriteString(s, e.Error())
165 for _, f := range e.stack {
166 io.WriteString(s, "\n")
167 if s.Flag('+') {
168 fmt.Fprintf(s, "%s\n\t", f.Function)
169 }
170 fmt.Fprintf(s, "%s:%d", f.File, f.Line)
171 }
172 }
173 }
174
175 func (e PanicError) Error() string {
176 v := e.value
177 if err, ok := v.(error); ok {
178 v = err.Error()
179 }
180 return fmt.Sprintf("panic: %v", v)
181 }
182
View as plain text