-
Notifications
You must be signed in to change notification settings - Fork 3
/
middleware_select_method.go
117 lines (105 loc) · 3.58 KB
/
middleware_select_method.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
package uhttp
import (
"fmt"
"net/http"
"runtime/debug"
"strings"
"github.com/dunv/uhelpers"
)
func selectMethodMiddleware(u *UHTTP, handlerOpts handlerOptions) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
res, retCode := executeHandlerMethod(r, u, handlerOpts)
// Figure out how to respond
// if res == nil -> do not do ANYTHING with response (no header, no body)
if res != nil {
switch typed := res.(type) {
case error:
u.RenderErrorWithStatusCode(w, r, retCode, typed, u.opts.logHandlerErrors)
default:
u.RenderWithStatusCode(w, r, retCode, typed)
}
return
}
}
}
func executeHandlerMethod(r *http.Request, u *UHTTP, handlerOpts handlerOptions) (interface{}, int) {
// Figure out which method to invoke
var returnCode int
// We need to process the handler in a goroutine so we can recover from panics
// this channel will be used to tell the main routine that the handler was processed
handlerProcessed := make(chan interface{})
if r.Method == http.MethodGet && handlerOpts.get != nil {
go func() {
defer recoverFromPanic(u, handlerProcessed, r, &returnCode)
handlerProcessed <- handlerOpts.get(r, &returnCode)
}()
} else if r.Method == http.MethodGet && handlerOpts.getWithModel != nil {
go func() {
defer recoverFromPanic(u, handlerProcessed, r, &returnCode)
model := parsedModel(r)
handlerProcessed <- handlerOpts.getWithModel(r, model, &returnCode)
}()
} else if r.Method == http.MethodPost && handlerOpts.post != nil {
go func() {
defer recoverFromPanic(u, handlerProcessed, r, &returnCode)
handlerProcessed <- handlerOpts.post(r, &returnCode)
}()
} else if r.Method == http.MethodPost && handlerOpts.postWithModel != nil {
go func() {
defer recoverFromPanic(u, handlerProcessed, r, &returnCode)
model := parsedModel(r)
handlerProcessed <- handlerOpts.postWithModel(r, model, &returnCode)
}()
} else if r.Method == http.MethodDelete && handlerOpts.delete != nil {
go func() {
defer recoverFromPanic(u, handlerProcessed, r, &returnCode)
handlerProcessed <- handlerOpts.delete(r, &returnCode)
}()
} else if r.Method == http.MethodDelete && handlerOpts.deleteWithModel != nil {
go func() {
defer recoverFromPanic(u, handlerProcessed, r, &returnCode)
model := parsedModel(r)
handlerProcessed <- handlerOpts.deleteWithModel(r, model, &returnCode)
}()
} else {
return fmt.Errorf("method not allowed"), http.StatusMethodNotAllowed
}
res := <-handlerProcessed
if res != nil {
switch res.(type) {
case error:
if returnCode == 0 {
returnCode = http.StatusBadRequest
}
default:
if returnCode == 0 {
returnCode = http.StatusOK
}
}
}
return res, returnCode
}
func recoverFromPanic(u *UHTTP, handlerProcessed chan interface{}, r *http.Request, returnCode *int) {
if rec := recover(); rec != nil {
err := fmt.Errorf("panic: handlerExecution (%s)", rec)
u.opts.log.Errorf("panic [path: %s] %s", r.RequestURI, err)
stack := debug.Stack()
uhelpers.CallForByteArrayLineByLine(stack, u.opts.log.Errorf, fmt.Sprintf("panic [path: %s] ", r.RequestURI))
err = fmt.Errorf("%s stackTrace: %s", err, strings.ReplaceAll(string(stack), "\n", "\\n"))
*returnCode = http.StatusInternalServerError
// let caller know if a panic happened
// do this asynchronously
if u.opts.handleHandlerPanics != nil {
for _, fn := range u.opts.handleHandlerPanics {
go func(fn func(r *http.Request, err error)) {
fn(r, err)
}(fn)
}
}
if u.opts.sendPanicInfoToClient {
handlerProcessed <- err
return
}
handlerProcessed <- fmt.Errorf("internal server error")
}
}