-
Notifications
You must be signed in to change notification settings - Fork 1
/
method.go
136 lines (128 loc) · 2.96 KB
/
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package htadaptor
import (
"io"
"net/http"
"reflect"
"strings"
)
// MethodSwitch provides method selections for [NewMethodMux].
type MethodSwitch struct {
Get http.Handler
Post http.Handler
Put http.Handler
Patch http.Handler
Delete http.Handler
Head http.Handler
}
func (ms *MethodSwitch) AllowedMethods() (methods []string) {
if ms.Get != nil {
methods = append(methods, http.MethodGet)
}
if ms.Post != nil {
methods = append(methods, http.MethodPost)
}
if ms.Put != nil {
methods = append(methods, http.MethodPut)
}
if ms.Patch != nil {
methods = append(methods, http.MethodPatch)
}
if ms.Delete != nil {
methods = append(methods, http.MethodDelete)
}
return
}
type methodMux struct {
Get http.Handler
Post http.Handler
Put http.Handler
Patch http.Handler
Delete http.Handler
Head http.Handler
allowed string
}
// NewMethodMux returns a handler that is able to satisfy REST
// interface expectations. It does not modify response status codes,
// but they can be updated using [WithStatusCode] option for
// individual handlers.
func NewMethodMux(ms *MethodSwitch) http.Handler {
if ms == nil {
return &getPostMux{}
}
allowed := ms.AllowedMethods()
if len(allowed) == 2 && reflect.DeepEqual(allowed, []string{"GET", "POST"}) {
return &getPostMux{
Get: ms.Get,
Post: ms.Post,
}
}
if ms.Head == nil {
ms.Head = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
}
return &methodMux{
Get: ms.Get,
Post: ms.Post,
Put: ms.Put,
Patch: ms.Patch,
Delete: ms.Delete,
Head: ms.Head,
allowed: strings.Join(append(allowed, http.MethodHead), ", "),
}
}
func (m *methodMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.Method { // http.Request ALWAYS has a method
case http.MethodGet:
if m.Get != nil {
m.Get.ServeHTTP(w, r)
return
}
case http.MethodPost:
if m.Post != nil {
m.Post.ServeHTTP(w, r)
return
}
case http.MethodPut:
if m.Put != nil {
m.Put.ServeHTTP(w, r)
return
}
case http.MethodPatch:
if m.Patch != nil {
m.Patch.ServeHTTP(w, r)
return
}
case http.MethodDelete:
if m.Delete != nil {
m.Delete.ServeHTTP(w, r)
return
}
case http.MethodOptions:
w.Header().Set("Allow", m.allowed)
return
case http.MethodHead:
m.Head.ServeHTTP(w, r)
}
w.WriteHeader(http.StatusMethodNotAllowed)
_, _ = io.WriteString(w, http.StatusText(http.StatusMethodNotAllowed))
}
type getPostMux struct {
Get http.Handler
Post http.Handler
}
func (m *getPostMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.Method { // http.Request ALWAYS has a method
case http.MethodGet:
m.Get.ServeHTTP(w, r)
return
case http.MethodPost:
m.Post.ServeHTTP(w, r)
return
case http.MethodOptions:
w.Header().Set("Allow", "GET, POST, HEAD")
return
case http.MethodHead:
return // no operation
}
w.WriteHeader(http.StatusMethodNotAllowed)
_, _ = io.WriteString(w, http.StatusText(http.StatusMethodNotAllowed))
}