blob: 782a34b22a6084db1085924cafe97ca8a374d3d1 [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001// Copyright 2012 The Gorilla Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package mux
6
7import (
8 "context"
9 "errors"
10 "fmt"
11 "net/http"
12 "path"
13 "regexp"
14)
15
16var (
17 // ErrMethodMismatch is returned when the method in the request does not match
18 // the method defined against the route.
19 ErrMethodMismatch = errors.New("method is not allowed")
20 // ErrNotFound is returned when no route match is found.
21 ErrNotFound = errors.New("no matching route was found")
22)
23
24// NewRouter returns a new router instance.
25func NewRouter() *Router {
26 return &Router{namedRoutes: make(map[string]*Route)}
27}
28
29// Router registers routes to be matched and dispatches a handler.
30//
31// It implements the http.Handler interface, so it can be registered to serve
32// requests:
33//
34// var router = mux.NewRouter()
35//
36// func main() {
37// http.Handle("/", router)
38// }
39//
40// Or, for Google App Engine, register it in a init() function:
41//
42// func init() {
43// http.Handle("/", router)
44// }
45//
46// This will send all incoming requests to the router.
47type Router struct {
48 // Configurable Handler to be used when no route matches.
49 NotFoundHandler http.Handler
50
51 // Configurable Handler to be used when the request method does not match the route.
52 MethodNotAllowedHandler http.Handler
53
54 // Routes to be matched, in order.
55 routes []*Route
56
57 // Routes by name for URL building.
58 namedRoutes map[string]*Route
59
60 // If true, do not clear the request context after handling the request.
61 //
62 // Deprecated: No effect, since the context is stored on the request itself.
63 KeepContext bool
64
65 // Slice of middlewares to be called after a match is found
66 middlewares []middleware
67
68 // configuration shared with `Route`
69 routeConf
70}
71
72// common route configuration shared between `Router` and `Route`
73type routeConf struct {
74 // If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
75 useEncodedPath bool
76
77 // If true, when the path pattern is "/path/", accessing "/path" will
78 // redirect to the former and vice versa.
79 strictSlash bool
80
81 // If true, when the path pattern is "/path//to", accessing "/path//to"
82 // will not redirect
83 skipClean bool
84
85 // Manager for the variables from host and path.
86 regexp routeRegexpGroup
87
88 // List of matchers.
89 matchers []matcher
90
91 // The scheme used when building URLs.
92 buildScheme string
93
94 buildVarsFunc BuildVarsFunc
95}
96
97// returns an effective deep copy of `routeConf`
98func copyRouteConf(r routeConf) routeConf {
99 c := r
100
101 if r.regexp.path != nil {
102 c.regexp.path = copyRouteRegexp(r.regexp.path)
103 }
104
105 if r.regexp.host != nil {
106 c.regexp.host = copyRouteRegexp(r.regexp.host)
107 }
108
109 c.regexp.queries = make([]*routeRegexp, 0, len(r.regexp.queries))
110 for _, q := range r.regexp.queries {
111 c.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q))
112 }
113
114 c.matchers = make([]matcher, len(r.matchers))
115 copy(c.matchers, r.matchers)
116
117 return c
118}
119
120func copyRouteRegexp(r *routeRegexp) *routeRegexp {
121 c := *r
122 return &c
123}
124
125// Match attempts to match the given request against the router's registered routes.
126//
127// If the request matches a route of this router or one of its subrouters the Route,
128// Handler, and Vars fields of the the match argument are filled and this function
129// returns true.
130//
131// If the request does not match any of this router's or its subrouters' routes
132// then this function returns false. If available, a reason for the match failure
133// will be filled in the match argument's MatchErr field. If the match failure type
134// (eg: not found) has a registered handler, the handler is assigned to the Handler
135// field of the match argument.
136func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
137 for _, route := range r.routes {
138 if route.Match(req, match) {
139 // Build middleware chain if no error was found
140 if match.MatchErr == nil {
141 for i := len(r.middlewares) - 1; i >= 0; i-- {
142 match.Handler = r.middlewares[i].Middleware(match.Handler)
143 }
144 }
145 return true
146 }
147 }
148
149 if match.MatchErr == ErrMethodMismatch {
150 if r.MethodNotAllowedHandler != nil {
151 match.Handler = r.MethodNotAllowedHandler
152 return true
153 }
154
155 return false
156 }
157
158 // Closest match for a router (includes sub-routers)
159 if r.NotFoundHandler != nil {
160 match.Handler = r.NotFoundHandler
161 match.MatchErr = ErrNotFound
162 return true
163 }
164
165 match.MatchErr = ErrNotFound
166 return false
167}
168
169// ServeHTTP dispatches the handler registered in the matched route.
170//
171// When there is a match, the route variables can be retrieved calling
172// mux.Vars(request).
173func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
174 if !r.skipClean {
175 path := req.URL.Path
176 if r.useEncodedPath {
177 path = req.URL.EscapedPath()
178 }
179 // Clean path to canonical form and redirect.
180 if p := cleanPath(path); p != path {
181
182 // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
183 // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
184 // http://code.google.com/p/go/issues/detail?id=5252
185 url := *req.URL
186 url.Path = p
187 p = url.String()
188
189 w.Header().Set("Location", p)
190 w.WriteHeader(http.StatusMovedPermanently)
191 return
192 }
193 }
194 var match RouteMatch
195 var handler http.Handler
196 if r.Match(req, &match) {
197 handler = match.Handler
198 req = requestWithVars(req, match.Vars)
199 req = requestWithRoute(req, match.Route)
200 }
201
202 if handler == nil && match.MatchErr == ErrMethodMismatch {
203 handler = methodNotAllowedHandler()
204 }
205
206 if handler == nil {
207 handler = http.NotFoundHandler()
208 }
209
210 handler.ServeHTTP(w, req)
211}
212
213// Get returns a route registered with the given name.
214func (r *Router) Get(name string) *Route {
215 return r.namedRoutes[name]
216}
217
218// GetRoute returns a route registered with the given name. This method
219// was renamed to Get() and remains here for backwards compatibility.
220func (r *Router) GetRoute(name string) *Route {
221 return r.namedRoutes[name]
222}
223
224// StrictSlash defines the trailing slash behavior for new routes. The initial
225// value is false.
226//
227// When true, if the route path is "/path/", accessing "/path" will perform a redirect
228// to the former and vice versa. In other words, your application will always
229// see the path as specified in the route.
230//
231// When false, if the route path is "/path", accessing "/path/" will not match
232// this route and vice versa.
233//
234// The re-direct is a HTTP 301 (Moved Permanently). Note that when this is set for
235// routes with a non-idempotent method (e.g. POST, PUT), the subsequent re-directed
236// request will be made as a GET by most clients. Use middleware or client settings
237// to modify this behaviour as needed.
238//
239// Special case: when a route sets a path prefix using the PathPrefix() method,
240// strict slash is ignored for that route because the redirect behavior can't
241// be determined from a prefix alone. However, any subrouters created from that
242// route inherit the original StrictSlash setting.
243func (r *Router) StrictSlash(value bool) *Router {
244 r.strictSlash = value
245 return r
246}
247
248// SkipClean defines the path cleaning behaviour for new routes. The initial
249// value is false. Users should be careful about which routes are not cleaned
250//
251// When true, if the route path is "/path//to", it will remain with the double
252// slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
253//
254// When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will
255// become /fetch/http/xkcd.com/534
256func (r *Router) SkipClean(value bool) *Router {
257 r.skipClean = value
258 return r
259}
260
261// UseEncodedPath tells the router to match the encoded original path
262// to the routes.
263// For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to".
264//
265// If not called, the router will match the unencoded path to the routes.
266// For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to"
267func (r *Router) UseEncodedPath() *Router {
268 r.useEncodedPath = true
269 return r
270}
271
272// ----------------------------------------------------------------------------
273// Route factories
274// ----------------------------------------------------------------------------
275
276// NewRoute registers an empty route.
277func (r *Router) NewRoute() *Route {
278 // initialize a route with a copy of the parent router's configuration
279 route := &Route{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes}
280 r.routes = append(r.routes, route)
281 return route
282}
283
284// Name registers a new route with a name.
285// See Route.Name().
286func (r *Router) Name(name string) *Route {
287 return r.NewRoute().Name(name)
288}
289
290// Handle registers a new route with a matcher for the URL path.
291// See Route.Path() and Route.Handler().
292func (r *Router) Handle(path string, handler http.Handler) *Route {
293 return r.NewRoute().Path(path).Handler(handler)
294}
295
296// HandleFunc registers a new route with a matcher for the URL path.
297// See Route.Path() and Route.HandlerFunc().
298func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
299 *http.Request)) *Route {
300 return r.NewRoute().Path(path).HandlerFunc(f)
301}
302
303// Headers registers a new route with a matcher for request header values.
304// See Route.Headers().
305func (r *Router) Headers(pairs ...string) *Route {
306 return r.NewRoute().Headers(pairs...)
307}
308
309// Host registers a new route with a matcher for the URL host.
310// See Route.Host().
311func (r *Router) Host(tpl string) *Route {
312 return r.NewRoute().Host(tpl)
313}
314
315// MatcherFunc registers a new route with a custom matcher function.
316// See Route.MatcherFunc().
317func (r *Router) MatcherFunc(f MatcherFunc) *Route {
318 return r.NewRoute().MatcherFunc(f)
319}
320
321// Methods registers a new route with a matcher for HTTP methods.
322// See Route.Methods().
323func (r *Router) Methods(methods ...string) *Route {
324 return r.NewRoute().Methods(methods...)
325}
326
327// Path registers a new route with a matcher for the URL path.
328// See Route.Path().
329func (r *Router) Path(tpl string) *Route {
330 return r.NewRoute().Path(tpl)
331}
332
333// PathPrefix registers a new route with a matcher for the URL path prefix.
334// See Route.PathPrefix().
335func (r *Router) PathPrefix(tpl string) *Route {
336 return r.NewRoute().PathPrefix(tpl)
337}
338
339// Queries registers a new route with a matcher for URL query values.
340// See Route.Queries().
341func (r *Router) Queries(pairs ...string) *Route {
342 return r.NewRoute().Queries(pairs...)
343}
344
345// Schemes registers a new route with a matcher for URL schemes.
346// See Route.Schemes().
347func (r *Router) Schemes(schemes ...string) *Route {
348 return r.NewRoute().Schemes(schemes...)
349}
350
351// BuildVarsFunc registers a new route with a custom function for modifying
352// route variables before building a URL.
353func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route {
354 return r.NewRoute().BuildVarsFunc(f)
355}
356
357// Walk walks the router and all its sub-routers, calling walkFn for each route
358// in the tree. The routes are walked in the order they were added. Sub-routers
359// are explored depth-first.
360func (r *Router) Walk(walkFn WalkFunc) error {
361 return r.walk(walkFn, []*Route{})
362}
363
364// SkipRouter is used as a return value from WalkFuncs to indicate that the
365// router that walk is about to descend down to should be skipped.
366var SkipRouter = errors.New("skip this router")
367
368// WalkFunc is the type of the function called for each route visited by Walk.
369// At every invocation, it is given the current route, and the current router,
370// and a list of ancestor routes that lead to the current route.
371type WalkFunc func(route *Route, router *Router, ancestors []*Route) error
372
373func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
374 for _, t := range r.routes {
375 err := walkFn(t, r, ancestors)
376 if err == SkipRouter {
377 continue
378 }
379 if err != nil {
380 return err
381 }
382 for _, sr := range t.matchers {
383 if h, ok := sr.(*Router); ok {
384 ancestors = append(ancestors, t)
385 err := h.walk(walkFn, ancestors)
386 if err != nil {
387 return err
388 }
389 ancestors = ancestors[:len(ancestors)-1]
390 }
391 }
392 if h, ok := t.handler.(*Router); ok {
393 ancestors = append(ancestors, t)
394 err := h.walk(walkFn, ancestors)
395 if err != nil {
396 return err
397 }
398 ancestors = ancestors[:len(ancestors)-1]
399 }
400 }
401 return nil
402}
403
404// ----------------------------------------------------------------------------
405// Context
406// ----------------------------------------------------------------------------
407
408// RouteMatch stores information about a matched route.
409type RouteMatch struct {
410 Route *Route
411 Handler http.Handler
412 Vars map[string]string
413
414 // MatchErr is set to appropriate matching error
415 // It is set to ErrMethodMismatch if there is a mismatch in
416 // the request method and route method
417 MatchErr error
418}
419
420type contextKey int
421
422const (
423 varsKey contextKey = iota
424 routeKey
425)
426
427// Vars returns the route variables for the current request, if any.
428func Vars(r *http.Request) map[string]string {
429 if rv := r.Context().Value(varsKey); rv != nil {
430 return rv.(map[string]string)
431 }
432 return nil
433}
434
435// CurrentRoute returns the matched route for the current request, if any.
436// This only works when called inside the handler of the matched route
437// because the matched route is stored in the request context which is cleared
438// after the handler returns.
439func CurrentRoute(r *http.Request) *Route {
440 if rv := r.Context().Value(routeKey); rv != nil {
441 return rv.(*Route)
442 }
443 return nil
444}
445
446func requestWithVars(r *http.Request, vars map[string]string) *http.Request {
447 ctx := context.WithValue(r.Context(), varsKey, vars)
448 return r.WithContext(ctx)
449}
450
451func requestWithRoute(r *http.Request, route *Route) *http.Request {
452 ctx := context.WithValue(r.Context(), routeKey, route)
453 return r.WithContext(ctx)
454}
455
456// ----------------------------------------------------------------------------
457// Helpers
458// ----------------------------------------------------------------------------
459
460// cleanPath returns the canonical path for p, eliminating . and .. elements.
461// Borrowed from the net/http package.
462func cleanPath(p string) string {
463 if p == "" {
464 return "/"
465 }
466 if p[0] != '/' {
467 p = "/" + p
468 }
469 np := path.Clean(p)
470 // path.Clean removes trailing slash except for root;
471 // put the trailing slash back if necessary.
472 if p[len(p)-1] == '/' && np != "/" {
473 np += "/"
474 }
475
476 return np
477}
478
479// uniqueVars returns an error if two slices contain duplicated strings.
480func uniqueVars(s1, s2 []string) error {
481 for _, v1 := range s1 {
482 for _, v2 := range s2 {
483 if v1 == v2 {
484 return fmt.Errorf("mux: duplicated route variable %q", v2)
485 }
486 }
487 }
488 return nil
489}
490
491// checkPairs returns the count of strings passed in, and an error if
492// the count is not an even number.
493func checkPairs(pairs ...string) (int, error) {
494 length := len(pairs)
495 if length%2 != 0 {
496 return length, fmt.Errorf(
497 "mux: number of parameters must be multiple of 2, got %v", pairs)
498 }
499 return length, nil
500}
501
502// mapFromPairsToString converts variadic string parameters to a
503// string to string map.
504func mapFromPairsToString(pairs ...string) (map[string]string, error) {
505 length, err := checkPairs(pairs...)
506 if err != nil {
507 return nil, err
508 }
509 m := make(map[string]string, length/2)
510 for i := 0; i < length; i += 2 {
511 m[pairs[i]] = pairs[i+1]
512 }
513 return m, nil
514}
515
516// mapFromPairsToRegex converts variadic string parameters to a
517// string to regex map.
518func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) {
519 length, err := checkPairs(pairs...)
520 if err != nil {
521 return nil, err
522 }
523 m := make(map[string]*regexp.Regexp, length/2)
524 for i := 0; i < length; i += 2 {
525 regex, err := regexp.Compile(pairs[i+1])
526 if err != nil {
527 return nil, err
528 }
529 m[pairs[i]] = regex
530 }
531 return m, nil
532}
533
534// matchInArray returns true if the given string value is in the array.
535func matchInArray(arr []string, value string) bool {
536 for _, v := range arr {
537 if v == value {
538 return true
539 }
540 }
541 return false
542}
543
544// matchMapWithString returns true if the given key/value pairs exist in a given map.
545func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool {
546 for k, v := range toCheck {
547 // Check if key exists.
548 if canonicalKey {
549 k = http.CanonicalHeaderKey(k)
550 }
551 if values := toMatch[k]; values == nil {
552 return false
553 } else if v != "" {
554 // If value was defined as an empty string we only check that the
555 // key exists. Otherwise we also check for equality.
556 valueExists := false
557 for _, value := range values {
558 if v == value {
559 valueExists = true
560 break
561 }
562 }
563 if !valueExists {
564 return false
565 }
566 }
567 }
568 return true
569}
570
571// matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against
572// the given regex
573func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool {
574 for k, v := range toCheck {
575 // Check if key exists.
576 if canonicalKey {
577 k = http.CanonicalHeaderKey(k)
578 }
579 if values := toMatch[k]; values == nil {
580 return false
581 } else if v != nil {
582 // If value was defined as an empty string we only check that the
583 // key exists. Otherwise we also check for equality.
584 valueExists := false
585 for _, value := range values {
586 if v.MatchString(value) {
587 valueExists = true
588 break
589 }
590 }
591 if !valueExists {
592 return false
593 }
594 }
595 }
596 return true
597}
598
599// methodNotAllowed replies to the request with an HTTP status code 405.
600func methodNotAllowed(w http.ResponseWriter, r *http.Request) {
601 w.WriteHeader(http.StatusMethodNotAllowed)
602}
603
604// methodNotAllowedHandler returns a simple request handler
605// that replies to each request with a status code 405.
606func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) }