Use vendored dependencies
[bloat] / vendor / github.com / gorilla / mux / route.go
1 // 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
5 package mux
6
7 import (
8         "errors"
9         "fmt"
10         "net/http"
11         "net/url"
12         "regexp"
13         "strings"
14 )
15
16 // Route stores information to match a request and build URLs.
17 type Route struct {
18         // Request handler for the route.
19         handler http.Handler
20         // If true, this route never matches: it is only used to build URLs.
21         buildOnly bool
22         // The name used to build URLs.
23         name string
24         // Error resulted from building a route.
25         err error
26
27         // "global" reference to all named routes
28         namedRoutes map[string]*Route
29
30         // config possibly passed in from `Router`
31         routeConf
32 }
33
34 // SkipClean reports whether path cleaning is enabled for this route via
35 // Router.SkipClean.
36 func (r *Route) SkipClean() bool {
37         return r.skipClean
38 }
39
40 // Match matches the route against the request.
41 func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
42         if r.buildOnly || r.err != nil {
43                 return false
44         }
45
46         var matchErr error
47
48         // Match everything.
49         for _, m := range r.matchers {
50                 if matched := m.Match(req, match); !matched {
51                         if _, ok := m.(methodMatcher); ok {
52                                 matchErr = ErrMethodMismatch
53                                 continue
54                         }
55
56                         // Ignore ErrNotFound errors. These errors arise from match call
57                         // to Subrouters.
58                         //
59                         // This prevents subsequent matching subrouters from failing to
60                         // run middleware. If not ignored, the middleware would see a
61                         // non-nil MatchErr and be skipped, even when there was a
62                         // matching route.
63                         if match.MatchErr == ErrNotFound {
64                                 match.MatchErr = nil
65                         }
66
67                         matchErr = nil
68                         return false
69                 }
70         }
71
72         if matchErr != nil {
73                 match.MatchErr = matchErr
74                 return false
75         }
76
77         if match.MatchErr == ErrMethodMismatch {
78                 // We found a route which matches request method, clear MatchErr
79                 match.MatchErr = nil
80                 // Then override the mis-matched handler
81                 match.Handler = r.handler
82         }
83
84         // Yay, we have a match. Let's collect some info about it.
85         if match.Route == nil {
86                 match.Route = r
87         }
88         if match.Handler == nil {
89                 match.Handler = r.handler
90         }
91         if match.Vars == nil {
92                 match.Vars = make(map[string]string)
93         }
94
95         // Set variables.
96         r.regexp.setMatch(req, match, r)
97         return true
98 }
99
100 // ----------------------------------------------------------------------------
101 // Route attributes
102 // ----------------------------------------------------------------------------
103
104 // GetError returns an error resulted from building the route, if any.
105 func (r *Route) GetError() error {
106         return r.err
107 }
108
109 // BuildOnly sets the route to never match: it is only used to build URLs.
110 func (r *Route) BuildOnly() *Route {
111         r.buildOnly = true
112         return r
113 }
114
115 // Handler --------------------------------------------------------------------
116
117 // Handler sets a handler for the route.
118 func (r *Route) Handler(handler http.Handler) *Route {
119         if r.err == nil {
120                 r.handler = handler
121         }
122         return r
123 }
124
125 // HandlerFunc sets a handler function for the route.
126 func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
127         return r.Handler(http.HandlerFunc(f))
128 }
129
130 // GetHandler returns the handler for the route, if any.
131 func (r *Route) GetHandler() http.Handler {
132         return r.handler
133 }
134
135 // Name -----------------------------------------------------------------------
136
137 // Name sets the name for the route, used to build URLs.
138 // It is an error to call Name more than once on a route.
139 func (r *Route) Name(name string) *Route {
140         if r.name != "" {
141                 r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
142                         r.name, name)
143         }
144         if r.err == nil {
145                 r.name = name
146                 r.namedRoutes[name] = r
147         }
148         return r
149 }
150
151 // GetName returns the name for the route, if any.
152 func (r *Route) GetName() string {
153         return r.name
154 }
155
156 // ----------------------------------------------------------------------------
157 // Matchers
158 // ----------------------------------------------------------------------------
159
160 // matcher types try to match a request.
161 type matcher interface {
162         Match(*http.Request, *RouteMatch) bool
163 }
164
165 // addMatcher adds a matcher to the route.
166 func (r *Route) addMatcher(m matcher) *Route {
167         if r.err == nil {
168                 r.matchers = append(r.matchers, m)
169         }
170         return r
171 }
172
173 // addRegexpMatcher adds a host or path matcher and builder to a route.
174 func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error {
175         if r.err != nil {
176                 return r.err
177         }
178         if typ == regexpTypePath || typ == regexpTypePrefix {
179                 if len(tpl) > 0 && tpl[0] != '/' {
180                         return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
181                 }
182                 if r.regexp.path != nil {
183                         tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
184                 }
185         }
186         rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{
187                 strictSlash:    r.strictSlash,
188                 useEncodedPath: r.useEncodedPath,
189         })
190         if err != nil {
191                 return err
192         }
193         for _, q := range r.regexp.queries {
194                 if err = uniqueVars(rr.varsN, q.varsN); err != nil {
195                         return err
196                 }
197         }
198         if typ == regexpTypeHost {
199                 if r.regexp.path != nil {
200                         if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
201                                 return err
202                         }
203                 }
204                 r.regexp.host = rr
205         } else {
206                 if r.regexp.host != nil {
207                         if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
208                                 return err
209                         }
210                 }
211                 if typ == regexpTypeQuery {
212                         r.regexp.queries = append(r.regexp.queries, rr)
213                 } else {
214                         r.regexp.path = rr
215                 }
216         }
217         r.addMatcher(rr)
218         return nil
219 }
220
221 // Headers --------------------------------------------------------------------
222
223 // headerMatcher matches the request against header values.
224 type headerMatcher map[string]string
225
226 func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
227         return matchMapWithString(m, r.Header, true)
228 }
229
230 // Headers adds a matcher for request header values.
231 // It accepts a sequence of key/value pairs to be matched. For example:
232 //
233 //     r := mux.NewRouter()
234 //     r.Headers("Content-Type", "application/json",
235 //               "X-Requested-With", "XMLHttpRequest")
236 //
237 // The above route will only match if both request header values match.
238 // If the value is an empty string, it will match any value if the key is set.
239 func (r *Route) Headers(pairs ...string) *Route {
240         if r.err == nil {
241                 var headers map[string]string
242                 headers, r.err = mapFromPairsToString(pairs...)
243                 return r.addMatcher(headerMatcher(headers))
244         }
245         return r
246 }
247
248 // headerRegexMatcher matches the request against the route given a regex for the header
249 type headerRegexMatcher map[string]*regexp.Regexp
250
251 func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
252         return matchMapWithRegex(m, r.Header, true)
253 }
254
255 // HeadersRegexp accepts a sequence of key/value pairs, where the value has regex
256 // support. For example:
257 //
258 //     r := mux.NewRouter()
259 //     r.HeadersRegexp("Content-Type", "application/(text|json)",
260 //               "X-Requested-With", "XMLHttpRequest")
261 //
262 // The above route will only match if both the request header matches both regular expressions.
263 // If the value is an empty string, it will match any value if the key is set.
264 // Use the start and end of string anchors (^ and $) to match an exact value.
265 func (r *Route) HeadersRegexp(pairs ...string) *Route {
266         if r.err == nil {
267                 var headers map[string]*regexp.Regexp
268                 headers, r.err = mapFromPairsToRegex(pairs...)
269                 return r.addMatcher(headerRegexMatcher(headers))
270         }
271         return r
272 }
273
274 // Host -----------------------------------------------------------------------
275
276 // Host adds a matcher for the URL host.
277 // It accepts a template with zero or more URL variables enclosed by {}.
278 // Variables can define an optional regexp pattern to be matched:
279 //
280 // - {name} matches anything until the next dot.
281 //
282 // - {name:pattern} matches the given regexp pattern.
283 //
284 // For example:
285 //
286 //     r := mux.NewRouter()
287 //     r.Host("www.example.com")
288 //     r.Host("{subdomain}.domain.com")
289 //     r.Host("{subdomain:[a-z]+}.domain.com")
290 //
291 // Variable names must be unique in a given route. They can be retrieved
292 // calling mux.Vars(request).
293 func (r *Route) Host(tpl string) *Route {
294         r.err = r.addRegexpMatcher(tpl, regexpTypeHost)
295         return r
296 }
297
298 // MatcherFunc ----------------------------------------------------------------
299
300 // MatcherFunc is the function signature used by custom matchers.
301 type MatcherFunc func(*http.Request, *RouteMatch) bool
302
303 // Match returns the match for a given request.
304 func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
305         return m(r, match)
306 }
307
308 // MatcherFunc adds a custom function to be used as request matcher.
309 func (r *Route) MatcherFunc(f MatcherFunc) *Route {
310         return r.addMatcher(f)
311 }
312
313 // Methods --------------------------------------------------------------------
314
315 // methodMatcher matches the request against HTTP methods.
316 type methodMatcher []string
317
318 func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
319         return matchInArray(m, r.Method)
320 }
321
322 // Methods adds a matcher for HTTP methods.
323 // It accepts a sequence of one or more methods to be matched, e.g.:
324 // "GET", "POST", "PUT".
325 func (r *Route) Methods(methods ...string) *Route {
326         for k, v := range methods {
327                 methods[k] = strings.ToUpper(v)
328         }
329         return r.addMatcher(methodMatcher(methods))
330 }
331
332 // Path -----------------------------------------------------------------------
333
334 // Path adds a matcher for the URL path.
335 // It accepts a template with zero or more URL variables enclosed by {}. The
336 // template must start with a "/".
337 // Variables can define an optional regexp pattern to be matched:
338 //
339 // - {name} matches anything until the next slash.
340 //
341 // - {name:pattern} matches the given regexp pattern.
342 //
343 // For example:
344 //
345 //     r := mux.NewRouter()
346 //     r.Path("/products/").Handler(ProductsHandler)
347 //     r.Path("/products/{key}").Handler(ProductsHandler)
348 //     r.Path("/articles/{category}/{id:[0-9]+}").
349 //       Handler(ArticleHandler)
350 //
351 // Variable names must be unique in a given route. They can be retrieved
352 // calling mux.Vars(request).
353 func (r *Route) Path(tpl string) *Route {
354         r.err = r.addRegexpMatcher(tpl, regexpTypePath)
355         return r
356 }
357
358 // PathPrefix -----------------------------------------------------------------
359
360 // PathPrefix adds a matcher for the URL path prefix. This matches if the given
361 // template is a prefix of the full URL path. See Route.Path() for details on
362 // the tpl argument.
363 //
364 // Note that it does not treat slashes specially ("/foobar/" will be matched by
365 // the prefix "/foo") so you may want to use a trailing slash here.
366 //
367 // Also note that the setting of Router.StrictSlash() has no effect on routes
368 // with a PathPrefix matcher.
369 func (r *Route) PathPrefix(tpl string) *Route {
370         r.err = r.addRegexpMatcher(tpl, regexpTypePrefix)
371         return r
372 }
373
374 // Query ----------------------------------------------------------------------
375
376 // Queries adds a matcher for URL query values.
377 // It accepts a sequence of key/value pairs. Values may define variables.
378 // For example:
379 //
380 //     r := mux.NewRouter()
381 //     r.Queries("foo", "bar", "id", "{id:[0-9]+}")
382 //
383 // The above route will only match if the URL contains the defined queries
384 // values, e.g.: ?foo=bar&id=42.
385 //
386 // If the value is an empty string, it will match any value if the key is set.
387 //
388 // Variables can define an optional regexp pattern to be matched:
389 //
390 // - {name} matches anything until the next slash.
391 //
392 // - {name:pattern} matches the given regexp pattern.
393 func (r *Route) Queries(pairs ...string) *Route {
394         length := len(pairs)
395         if length%2 != 0 {
396                 r.err = fmt.Errorf(
397                         "mux: number of parameters must be multiple of 2, got %v", pairs)
398                 return nil
399         }
400         for i := 0; i < length; i += 2 {
401                 if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], regexpTypeQuery); r.err != nil {
402                         return r
403                 }
404         }
405
406         return r
407 }
408
409 // Schemes --------------------------------------------------------------------
410
411 // schemeMatcher matches the request against URL schemes.
412 type schemeMatcher []string
413
414 func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
415         return matchInArray(m, r.URL.Scheme)
416 }
417
418 // Schemes adds a matcher for URL schemes.
419 // It accepts a sequence of schemes to be matched, e.g.: "http", "https".
420 func (r *Route) Schemes(schemes ...string) *Route {
421         for k, v := range schemes {
422                 schemes[k] = strings.ToLower(v)
423         }
424         if len(schemes) > 0 {
425                 r.buildScheme = schemes[0]
426         }
427         return r.addMatcher(schemeMatcher(schemes))
428 }
429
430 // BuildVarsFunc --------------------------------------------------------------
431
432 // BuildVarsFunc is the function signature used by custom build variable
433 // functions (which can modify route variables before a route's URL is built).
434 type BuildVarsFunc func(map[string]string) map[string]string
435
436 // BuildVarsFunc adds a custom function to be used to modify build variables
437 // before a route's URL is built.
438 func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
439         if r.buildVarsFunc != nil {
440                 // compose the old and new functions
441                 old := r.buildVarsFunc
442                 r.buildVarsFunc = func(m map[string]string) map[string]string {
443                         return f(old(m))
444                 }
445         } else {
446                 r.buildVarsFunc = f
447         }
448         return r
449 }
450
451 // Subrouter ------------------------------------------------------------------
452
453 // Subrouter creates a subrouter for the route.
454 //
455 // It will test the inner routes only if the parent route matched. For example:
456 //
457 //     r := mux.NewRouter()
458 //     s := r.Host("www.example.com").Subrouter()
459 //     s.HandleFunc("/products/", ProductsHandler)
460 //     s.HandleFunc("/products/{key}", ProductHandler)
461 //     s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
462 //
463 // Here, the routes registered in the subrouter won't be tested if the host
464 // doesn't match.
465 func (r *Route) Subrouter() *Router {
466         // initialize a subrouter with a copy of the parent route's configuration
467         router := &Router{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes}
468         r.addMatcher(router)
469         return router
470 }
471
472 // ----------------------------------------------------------------------------
473 // URL building
474 // ----------------------------------------------------------------------------
475
476 // URL builds a URL for the route.
477 //
478 // It accepts a sequence of key/value pairs for the route variables. For
479 // example, given this route:
480 //
481 //     r := mux.NewRouter()
482 //     r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
483 //       Name("article")
484 //
485 // ...a URL for it can be built using:
486 //
487 //     url, err := r.Get("article").URL("category", "technology", "id", "42")
488 //
489 // ...which will return an url.URL with the following path:
490 //
491 //     "/articles/technology/42"
492 //
493 // This also works for host variables:
494 //
495 //     r := mux.NewRouter()
496 //     r.Host("{subdomain}.domain.com").
497 //       HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
498 //       Name("article")
499 //
500 //     // url.String() will be "http://news.domain.com/articles/technology/42"
501 //     url, err := r.Get("article").URL("subdomain", "news",
502 //                                      "category", "technology",
503 //                                      "id", "42")
504 //
505 // All variables defined in the route are required, and their values must
506 // conform to the corresponding patterns.
507 func (r *Route) URL(pairs ...string) (*url.URL, error) {
508         if r.err != nil {
509                 return nil, r.err
510         }
511         values, err := r.prepareVars(pairs...)
512         if err != nil {
513                 return nil, err
514         }
515         var scheme, host, path string
516         queries := make([]string, 0, len(r.regexp.queries))
517         if r.regexp.host != nil {
518                 if host, err = r.regexp.host.url(values); err != nil {
519                         return nil, err
520                 }
521                 scheme = "http"
522                 if r.buildScheme != "" {
523                         scheme = r.buildScheme
524                 }
525         }
526         if r.regexp.path != nil {
527                 if path, err = r.regexp.path.url(values); err != nil {
528                         return nil, err
529                 }
530         }
531         for _, q := range r.regexp.queries {
532                 var query string
533                 if query, err = q.url(values); err != nil {
534                         return nil, err
535                 }
536                 queries = append(queries, query)
537         }
538         return &url.URL{
539                 Scheme:   scheme,
540                 Host:     host,
541                 Path:     path,
542                 RawQuery: strings.Join(queries, "&"),
543         }, nil
544 }
545
546 // URLHost builds the host part of the URL for a route. See Route.URL().
547 //
548 // The route must have a host defined.
549 func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
550         if r.err != nil {
551                 return nil, r.err
552         }
553         if r.regexp.host == nil {
554                 return nil, errors.New("mux: route doesn't have a host")
555         }
556         values, err := r.prepareVars(pairs...)
557         if err != nil {
558                 return nil, err
559         }
560         host, err := r.regexp.host.url(values)
561         if err != nil {
562                 return nil, err
563         }
564         u := &url.URL{
565                 Scheme: "http",
566                 Host:   host,
567         }
568         if r.buildScheme != "" {
569                 u.Scheme = r.buildScheme
570         }
571         return u, nil
572 }
573
574 // URLPath builds the path part of the URL for a route. See Route.URL().
575 //
576 // The route must have a path defined.
577 func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
578         if r.err != nil {
579                 return nil, r.err
580         }
581         if r.regexp.path == nil {
582                 return nil, errors.New("mux: route doesn't have a path")
583         }
584         values, err := r.prepareVars(pairs...)
585         if err != nil {
586                 return nil, err
587         }
588         path, err := r.regexp.path.url(values)
589         if err != nil {
590                 return nil, err
591         }
592         return &url.URL{
593                 Path: path,
594         }, nil
595 }
596
597 // GetPathTemplate returns the template used to build the
598 // route match.
599 // This is useful for building simple REST API documentation and for instrumentation
600 // against third-party services.
601 // An error will be returned if the route does not define a path.
602 func (r *Route) GetPathTemplate() (string, error) {
603         if r.err != nil {
604                 return "", r.err
605         }
606         if r.regexp.path == nil {
607                 return "", errors.New("mux: route doesn't have a path")
608         }
609         return r.regexp.path.template, nil
610 }
611
612 // GetPathRegexp returns the expanded regular expression used to match route path.
613 // This is useful for building simple REST API documentation and for instrumentation
614 // against third-party services.
615 // An error will be returned if the route does not define a path.
616 func (r *Route) GetPathRegexp() (string, error) {
617         if r.err != nil {
618                 return "", r.err
619         }
620         if r.regexp.path == nil {
621                 return "", errors.New("mux: route does not have a path")
622         }
623         return r.regexp.path.regexp.String(), nil
624 }
625
626 // GetQueriesRegexp returns the expanded regular expressions used to match the
627 // route queries.
628 // This is useful for building simple REST API documentation and for instrumentation
629 // against third-party services.
630 // An error will be returned if the route does not have queries.
631 func (r *Route) GetQueriesRegexp() ([]string, error) {
632         if r.err != nil {
633                 return nil, r.err
634         }
635         if r.regexp.queries == nil {
636                 return nil, errors.New("mux: route doesn't have queries")
637         }
638         var queries []string
639         for _, query := range r.regexp.queries {
640                 queries = append(queries, query.regexp.String())
641         }
642         return queries, nil
643 }
644
645 // GetQueriesTemplates returns the templates used to build the
646 // query matching.
647 // This is useful for building simple REST API documentation and for instrumentation
648 // against third-party services.
649 // An error will be returned if the route does not define queries.
650 func (r *Route) GetQueriesTemplates() ([]string, error) {
651         if r.err != nil {
652                 return nil, r.err
653         }
654         if r.regexp.queries == nil {
655                 return nil, errors.New("mux: route doesn't have queries")
656         }
657         var queries []string
658         for _, query := range r.regexp.queries {
659                 queries = append(queries, query.template)
660         }
661         return queries, nil
662 }
663
664 // GetMethods returns the methods the route matches against
665 // This is useful for building simple REST API documentation and for instrumentation
666 // against third-party services.
667 // An error will be returned if route does not have methods.
668 func (r *Route) GetMethods() ([]string, error) {
669         if r.err != nil {
670                 return nil, r.err
671         }
672         for _, m := range r.matchers {
673                 if methods, ok := m.(methodMatcher); ok {
674                         return []string(methods), nil
675                 }
676         }
677         return nil, errors.New("mux: route doesn't have methods")
678 }
679
680 // GetHostTemplate returns the template used to build the
681 // route match.
682 // This is useful for building simple REST API documentation and for instrumentation
683 // against third-party services.
684 // An error will be returned if the route does not define a host.
685 func (r *Route) GetHostTemplate() (string, error) {
686         if r.err != nil {
687                 return "", r.err
688         }
689         if r.regexp.host == nil {
690                 return "", errors.New("mux: route doesn't have a host")
691         }
692         return r.regexp.host.template, nil
693 }
694
695 // prepareVars converts the route variable pairs into a map. If the route has a
696 // BuildVarsFunc, it is invoked.
697 func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
698         m, err := mapFromPairsToString(pairs...)
699         if err != nil {
700                 return nil, err
701         }
702         return r.buildVars(m), nil
703 }
704
705 func (r *Route) buildVars(m map[string]string) map[string]string {
706         if r.buildVarsFunc != nil {
707                 m = r.buildVarsFunc(m)
708         }
709         return m
710 }