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