-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmiddleware.go
76 lines (67 loc) · 1.93 KB
/
middleware.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
package shift
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"runtime"
)
// Recover gracefully handle panics in the subsequent middlewares in the chain and the request handler.
// It returns HTTP 500 ([http.StatusInternalServerError]) status and write the stack trace to [os.Stderr].
//
// Use [RecoverWithWriter] to write to a different [io.Writer].
func Recover() MiddlewareFunc {
return RecoverWithWriter(os.Stderr)
}
// RecoverWithWriter gracefully handle panics in the subsequent middlewares in the chain and the request handler.
// It returns HTTP 500 ([http.StatusInternalServerError]) status and write the stack trace to the provided [io.Writer].
//
// Use [Recover] to write to [os.Stderr].
func RecoverWithWriter(w io.Writer) MiddlewareFunc {
return func(next HandlerFunc) HandlerFunc {
return func(rw http.ResponseWriter, r *http.Request, route Route) error {
defer func() {
rec := recover()
switch rec {
case nil:
// do nothing.
case http.ErrAbortHandler:
panic(rec)
default:
writeStack(w, rec, 3)
rw.WriteHeader(http.StatusInternalServerError)
}
}()
return next(rw, r, route)
}
}
}
func writeStack(w io.Writer, rec any, skipFrames int) {
buf := &bytes.Buffer{}
for i := skipFrames; ; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
f := runtime.FuncForPC(pc)
fmt.Fprintf(buf, "%s\n", f.Name())
fmt.Fprintf(buf, " %s:%d (%#x)\n", file, line, pc)
}
fmt.Fprintf(w, "panic: %v\n%s", rec, buf.String())
}
// RouteContext packs Route information into http.Request context.
//
// Use RouteOf to unpack Route information from the http.Request context.
func RouteContext() MiddlewareFunc {
return func(next HandlerFunc) HandlerFunc {
return func(w http.ResponseWriter, r *http.Request, route Route) error {
ctx := getCtx()
ctx.Context = r.Context()
ctx.Route = route
defer releaseCtx(ctx)
r = r.WithContext(ctx)
return next(w, r, route)
}
}
}