-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmux.go
148 lines (126 loc) · 4.32 KB
/
mux.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
package mux
import (
"fmt"
"net/http"
"strings"
"github.com/teambition/trie-mux"
)
// Params represents named parameter values
type Params map[string]string
// HandlerFunc is a function that can be registered to a route to handle HTTP
// requests. Like http.HandlerFunc, but has a third parameter for the values of
// wildcards (variables).
type HandlerFunc func(http.ResponseWriter, *http.Request, Params)
// Mux is a tire base HTTP request router which can be used to
// dispatch requests to different handler functions.
type Mux struct {
trie *trie.Trie
otherwise HandlerFunc
}
// New returns a Mux instance.
func New(opts ...trie.Options) *Mux {
return &Mux{trie: trie.New(opts...)}
}
// Get registers a new GET route for a path with matching handler in the Mux.
func (m *Mux) Get(pattern string, handler HandlerFunc) {
m.Handle(http.MethodGet, pattern, handler)
}
// Head registers a new HEAD route for a path with matching handler in the Mux.
func (m *Mux) Head(pattern string, handler HandlerFunc) {
m.Handle(http.MethodHead, pattern, handler)
}
// Post registers a new POST route for a path with matching handler in the Mux.
func (m *Mux) Post(pattern string, handler HandlerFunc) {
m.Handle(http.MethodPost, pattern, handler)
}
// Put registers a new PUT route for a path with matching handler in the Mux.
func (m *Mux) Put(pattern string, handler HandlerFunc) {
m.Handle(http.MethodPut, pattern, handler)
}
// Patch registers a new PATCH route for a path with matching handler in the Mux.
func (m *Mux) Patch(pattern string, handler HandlerFunc) {
m.Handle(http.MethodPatch, pattern, handler)
}
// Delete registers a new DELETE route for a path with matching handler in the Mux.
func (m *Mux) Delete(pattern string, handler HandlerFunc) {
m.Handle(http.MethodDelete, pattern, handler)
}
// Options registers a new OPTIONS route for a path with matching handler in the Mux.
func (m *Mux) Options(pattern string, handler HandlerFunc) {
m.Handle(http.MethodOptions, pattern, handler)
}
// Otherwise registers a new handler in the Mux
// that will run if there is no other handler matching.
func (m *Mux) Otherwise(handler HandlerFunc) {
m.otherwise = handler
}
// Handle registers a new handler with method and path in the Mux.
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
//
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).
func (m *Mux) Handle(method, pattern string, handler HandlerFunc) {
if method == "" {
panic(fmt.Errorf("invalid method"))
}
m.trie.Define(pattern).Handle(strings.ToUpper(method), handler)
}
// Handler is an adapter which allows the usage of an http.Handler as a
// request handle.
func (m *Mux) Handler(method, path string, handler http.Handler) {
m.Handle(method, path, func(w http.ResponseWriter, req *http.Request, _ Params) {
handler.ServeHTTP(w, req)
})
}
// HandlerFunc is an adapter which allows the usage of an http.HandlerFunc as a
// request handle.
func (m *Mux) HandlerFunc(method, path string, handler http.HandlerFunc) {
m.Handler(method, path, handler)
}
// ServeHTTP implemented http.Handler interface
func (m *Mux) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var handler HandlerFunc
path := req.URL.Path
method := req.Method
res := m.trie.Match(path)
if res.Node == nil {
// FixedPathRedirect or TrailingSlashRedirect
if res.TSR != "" || res.FPR != "" {
req.URL.Path = res.TSR
if res.FPR != "" {
req.URL.Path = res.FPR
}
code := 301
if method != "GET" {
code = 307
}
http.Redirect(w, req, req.URL.String(), code)
return
}
if m.otherwise == nil {
http.Error(w, fmt.Sprintf(`"%s" not implemented`, path), 501)
return
}
handler = m.otherwise
} else {
ok := false
if handler, ok = res.Node.GetHandler(method).(HandlerFunc); !ok {
// OPTIONS support
if method == http.MethodOptions {
w.Header().Set("Allow", res.Node.GetAllow())
w.WriteHeader(204)
return
}
if m.otherwise == nil {
// If no route handler is returned, it's a 405 error
w.Header().Set("Allow", res.Node.GetAllow())
http.Error(w, fmt.Sprintf(`"%s" not allowed in "%s"`, method, path), 405)
return
}
handler = m.otherwise
}
}
handler(w, req, res.Params)
}