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