-
Notifications
You must be signed in to change notification settings - Fork 13
/
router.go
280 lines (235 loc) · 7.45 KB
/
router.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
package mux
import (
"net/http"
"path"
"sort"
"strings"
)
// NewRouter returns a new router instance.
func NewRouter() *Router {
return &Router{
routes: make(map[string]routes),
Validatoren: map[string]Validator{
"method": newMethodValidator(),
"path": newPathValidator(),
},
}
}
// Router registers routes to be matched and dispatches a handler.
//
// It implements the http.Handler interface, so it can be registered to serve
// requests:
//
// var router = mux.NewRouter()
//
// func main() {
// http.Handle("/", router)
// }
//
// This will send all incoming requests to the router.
type Router struct {
// Configurable Handler to be used when no route matches.
NotFoundHandler http.Handler
// Routes to be matched, in order.
routes map[string]routes
// This defines the flag for new routes.
StrictSlash bool
// This defines the flag for new routes.
SkipClean bool
// This defines a flag for all routes.
UseEncodedPath bool
// see Validator
Validatoren map[string]Validator
// This defines a flag for all routes.
CaseSensitiveURL bool
// this builds a route
constructRoute func(*Router) RouteInterface
}
// UseRoute that you can use diffrent instances routes
func (r *Router) UseRoute(constructer func(*Router) RouteInterface) {
r.constructRoute = constructer
}
// triggerMatching matches registered routes against the request.
func (r *Router) triggerMatching(req *http.Request) RouteInterface {
if routesForMethod, found := r.routes[req.Method]; found {
for _, route := range routesForMethod {
if route := route.Match(req); route != nil {
return route
}
}
}
return nil
}
// ServeHTTP dispatches the handler registered in the matched route.
//
// When there is a match, the route variables can be retrieved calling
// mux.GetVars(req).Get(":number") or mux.GetVars(req).GetAll()
//
// and the route queires can be retrieved calling
// mux.GetQueries(req).Get(":number") or mux.GetQueries(req).GetAll()
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if !r.SkipClean {
path := req.URL.Path
if r.UseEncodedPath {
path = req.URL.EscapedPath()
}
// Clean path to canonical form and redirect.
if cleanedPath := cleanPath(path); cleanedPath != path {
w.Header().Set("Location", cleanedPath)
w.WriteHeader(http.StatusMovedPermanently)
return
}
}
if !r.CaseSensitiveURL {
req.URL.Path = strings.ToLower(req.URL.Path)
}
route := r.triggerMatching(req)
if route == nil {
r.notFoundHandler().ServeHTTP(w, req)
return
}
req = AddCurrentRoute(req, route)
req = AddQueries(req)
if route.HasVars() {
req = AddVars(req, route.ExtractVars(req))
}
if !route.HasHandler() {
route.Handler(r.notFoundHandler())
}
route.GetHandler().ServeHTTP(w, req)
}
func (r *Router) notFoundHandler() http.Handler {
if r.NotFoundHandler == nil {
return http.NotFoundHandler()
}
return r.NotFoundHandler
}
// cleanPath returns the canonical path for p, eliminating . and .. elements.
// Borrowed from the net/http package.
// /net/http/server.go
func cleanPath(p string) string {
if p == "" {
return "/"
}
if p[0] != '/' {
p = "/" + p
}
np := path.Clean(p)
// path.Clean removes trailing slash except for root;
// put the trailing slash back if necessary.
if p[len(p)-1] == '/' && np != "/" {
np += "/"
}
return np
}
// NewRoute registers an empty route.
func (r *Router) NewRoute() RouteInterface {
return r.constructRoute(r)
}
// RegisterRoute registers and validates a new route
func (r *Router) RegisterRoute(method string, route RouteInterface) RouteInterface {
route.SetMethodName(method)
for _, validatorKey := range [2]string{"method", "path"} {
if validator, found := r.Validatoren[validatorKey]; found {
err := validator.Validate(route)
if err != nil {
route.SetError(NewBadRouteError(route, err.Error()))
break
}
}
}
r.routes[method] = append(r.routes[method], route)
return route
}
// Handle registers a new route with a matcher for the URL path.
// See Route.Path() and Route.Handler().
func (r *Router) Handle(method string, path string, handler http.Handler) RouteInterface {
route := r.NewRoute()
route.Path(path).Handler(handler)
return r.RegisterRoute(method, route)
}
// HandleFunc registers a new route with a matcher for the URL path.
// See Route.Path() and Route.HandlerFunc().
func (r *Router) HandleFunc(method string, path string, HandlerFunc func(http.ResponseWriter, *http.Request)) RouteInterface {
return r.RegisterRoute(method, r.NewRoute().Path(path).HandlerFunc(HandlerFunc))
}
// Get registers a new get route for the URL path
// See Route.Path() and Route.HandlerFunc()
func (r *Router) Get(path string, handlerFunc func(http.ResponseWriter, *http.Request)) RouteInterface {
return r.RegisterRoute(http.MethodGet, r.NewRoute().Path(path).HandlerFunc(handlerFunc))
}
// Put registers a new put route for the URL path
// See Route.Path() and Route.HandlerFunc()
func (r *Router) Put(path string, handlerFunc func(http.ResponseWriter, *http.Request)) RouteInterface {
return r.RegisterRoute(http.MethodPut, r.NewRoute().Path(path).HandlerFunc(handlerFunc))
}
// Post registers a new post route for the URL path
// See Route.Path() and Route.HandlerFunc()
func (r *Router) Post(path string, handlerFunc func(http.ResponseWriter, *http.Request)) RouteInterface {
return r.RegisterRoute(http.MethodPost, r.NewRoute().Path(path).HandlerFunc(handlerFunc))
}
// Delete registers a new delete route for the URL path
// See Route.Path() and Route.HandlerFunc()
func (r *Router) Delete(path string, handlerFunc func(http.ResponseWriter, *http.Request)) RouteInterface {
return r.RegisterRoute(http.MethodDelete, r.NewRoute().Path(path).HandlerFunc(handlerFunc))
}
// Options registers a new options route for the URL path
// See Route.Path() and Route.HandlerFunc()
func (r *Router) Options(path string, handlerFunc func(http.ResponseWriter, *http.Request)) RouteInterface {
return r.RegisterRoute(http.MethodOptions, r.NewRoute().Path(path).HandlerFunc(handlerFunc))
}
// Head registers a new head route for the URL path
// See Route.Path() and Route.HandlerFunc()
func (r *Router) Head(path string, handlerFunc func(http.ResponseWriter, *http.Request)) RouteInterface {
return r.RegisterRoute(http.MethodHead, r.NewRoute().Path(path).HandlerFunc(handlerFunc))
}
// ListenAndServe listens on the TCP network address addr
// and then calls Serve with handler to handle requests
// on incoming connections.
func (r *Router) ListenAndServe(port string, callback func(errs []error)) {
ok, errs := r.HasErrors()
if ok {
callback(errs)
return
}
r.SortRoutes()
errs = append(errs, http.ListenAndServe(port, r))
if 0 != len(errs) {
callback(errs)
}
}
// HasErrors checks if any errors exists
func (r *Router) HasErrors() (bool, []error) {
errors := make([]error, 0)
hasError := false
for _, v := range r.routes {
for _, vv := range v {
if vv.HasError() {
hasError = true
errors = append(errors, vv.GetError())
}
}
}
return hasError, errors
}
// SortRoutes sorts the routes (Rank: RegexPath, PathWithVars, PathNormal)
func (r *Router) SortRoutes() {
for _, v := range r.routes {
for _, vv := range v {
sort.Sort(vv.GetMatchers())
}
sort.Sort(v)
}
}
// routes implements the sort interface (len, swap, less)
// see sort.Sort (Standard Library)
type routes []RouteInterface
func (r routes) Len() int {
return len(r)
}
func (r routes) Swap(i, j int) {
r[i], r[j] = r[j], r[i]
}
func (r routes) Less(i, j int) bool {
return r[i].Kind() > r[j].Kind()
}