]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/route.go
provider: Ensured Go 1.11 in TravisCI and README
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / fsouza / go-dockerclient / external / 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 // Parent where the route was registered (a Router).
19 parent parentRoute
20 // Request handler for the route.
21 handler http.Handler
22 // List of matchers.
23 matchers []matcher
24 // Manager for the variables from host and path.
25 regexp *routeRegexpGroup
26 // If true, when the path pattern is "/path/", accessing "/path" will
27 // redirect to the former and vice versa.
28 strictSlash bool
29 // If true, this route never matches: it is only used to build URLs.
30 buildOnly bool
31 // The name used to build URLs.
32 name string
33 // Error resulted from building a route.
34 err error
35
36 buildVarsFunc BuildVarsFunc
37 }
38
39 // Match matches the route against the request.
40 func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
41 if r.buildOnly || r.err != nil {
42 return false
43 }
44 // Match everything.
45 for _, m := range r.matchers {
46 if matched := m.Match(req, match); !matched {
47 return false
48 }
49 }
50 // Yay, we have a match. Let's collect some info about it.
51 if match.Route == nil {
52 match.Route = r
53 }
54 if match.Handler == nil {
55 match.Handler = r.handler
56 }
57 if match.Vars == nil {
58 match.Vars = make(map[string]string)
59 }
60 // Set variables.
61 if r.regexp != nil {
62 r.regexp.setMatch(req, match, r)
63 }
64 return true
65 }
66
67 // ----------------------------------------------------------------------------
68 // Route attributes
69 // ----------------------------------------------------------------------------
70
71 // GetError returns an error resulted from building the route, if any.
72 func (r *Route) GetError() error {
73 return r.err
74 }
75
76 // BuildOnly sets the route to never match: it is only used to build URLs.
77 func (r *Route) BuildOnly() *Route {
78 r.buildOnly = true
79 return r
80 }
81
82 // Handler --------------------------------------------------------------------
83
84 // Handler sets a handler for the route.
85 func (r *Route) Handler(handler http.Handler) *Route {
86 if r.err == nil {
87 r.handler = handler
88 }
89 return r
90 }
91
92 // HandlerFunc sets a handler function for the route.
93 func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
94 return r.Handler(http.HandlerFunc(f))
95 }
96
97 // GetHandler returns the handler for the route, if any.
98 func (r *Route) GetHandler() http.Handler {
99 return r.handler
100 }
101
102 // Name -----------------------------------------------------------------------
103
104 // Name sets the name for the route, used to build URLs.
105 // If the name was registered already it will be overwritten.
106 func (r *Route) Name(name string) *Route {
107 if r.name != "" {
108 r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
109 r.name, name)
110 }
111 if r.err == nil {
112 r.name = name
113 r.getNamedRoutes()[name] = r
114 }
115 return r
116 }
117
118 // GetName returns the name for the route, if any.
119 func (r *Route) GetName() string {
120 return r.name
121 }
122
123 // ----------------------------------------------------------------------------
124 // Matchers
125 // ----------------------------------------------------------------------------
126
127 // matcher types try to match a request.
128 type matcher interface {
129 Match(*http.Request, *RouteMatch) bool
130 }
131
132 // addMatcher adds a matcher to the route.
133 func (r *Route) addMatcher(m matcher) *Route {
134 if r.err == nil {
135 r.matchers = append(r.matchers, m)
136 }
137 return r
138 }
139
140 // addRegexpMatcher adds a host or path matcher and builder to a route.
141 func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error {
142 if r.err != nil {
143 return r.err
144 }
145 r.regexp = r.getRegexpGroup()
146 if !matchHost && !matchQuery {
147 if len(tpl) == 0 || tpl[0] != '/' {
148 return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
149 }
150 if r.regexp.path != nil {
151 tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
152 }
153 }
154 rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash)
155 if err != nil {
156 return err
157 }
158 for _, q := range r.regexp.queries {
159 if err = uniqueVars(rr.varsN, q.varsN); err != nil {
160 return err
161 }
162 }
163 if matchHost {
164 if r.regexp.path != nil {
165 if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
166 return err
167 }
168 }
169 r.regexp.host = rr
170 } else {
171 if r.regexp.host != nil {
172 if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
173 return err
174 }
175 }
176 if matchQuery {
177 r.regexp.queries = append(r.regexp.queries, rr)
178 } else {
179 r.regexp.path = rr
180 }
181 }
182 r.addMatcher(rr)
183 return nil
184 }
185
186 // Headers --------------------------------------------------------------------
187
188 // headerMatcher matches the request against header values.
189 type headerMatcher map[string]string
190
191 func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
192 return matchMapWithString(m, r.Header, true)
193 }
194
195 // Headers adds a matcher for request header values.
196 // It accepts a sequence of key/value pairs to be matched. For example:
197 //
198 // r := mux.NewRouter()
199 // r.Headers("Content-Type", "application/json",
200 // "X-Requested-With", "XMLHttpRequest")
201 //
202 // The above route will only match if both request header values match.
203 // If the value is an empty string, it will match any value if the key is set.
204 func (r *Route) Headers(pairs ...string) *Route {
205 if r.err == nil {
206 var headers map[string]string
207 headers, r.err = mapFromPairsToString(pairs...)
208 return r.addMatcher(headerMatcher(headers))
209 }
210 return r
211 }
212
213 // headerRegexMatcher matches the request against the route given a regex for the header
214 type headerRegexMatcher map[string]*regexp.Regexp
215
216 func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
217 return matchMapWithRegex(m, r.Header, true)
218 }
219
220 // Regular expressions can be used with headers as well.
221 // It accepts a sequence of key/value pairs, where the value has regex support. For example
222 // r := mux.NewRouter()
223 // r.HeadersRegexp("Content-Type", "application/(text|json)",
224 // "X-Requested-With", "XMLHttpRequest")
225 //
226 // The above route will only match if both the request header matches both regular expressions.
227 // It the value is an empty string, it will match any value if the key is set.
228 func (r *Route) HeadersRegexp(pairs ...string) *Route {
229 if r.err == nil {
230 var headers map[string]*regexp.Regexp
231 headers, r.err = mapFromPairsToRegex(pairs...)
232 return r.addMatcher(headerRegexMatcher(headers))
233 }
234 return r
235 }
236
237 // Host -----------------------------------------------------------------------
238
239 // Host adds a matcher for the URL host.
240 // It accepts a template with zero or more URL variables enclosed by {}.
241 // Variables can define an optional regexp pattern to be matched:
242 //
243 // - {name} matches anything until the next dot.
244 //
245 // - {name:pattern} matches the given regexp pattern.
246 //
247 // For example:
248 //
249 // r := mux.NewRouter()
250 // r.Host("www.example.com")
251 // r.Host("{subdomain}.domain.com")
252 // r.Host("{subdomain:[a-z]+}.domain.com")
253 //
254 // Variable names must be unique in a given route. They can be retrieved
255 // calling mux.Vars(request).
256 func (r *Route) Host(tpl string) *Route {
257 r.err = r.addRegexpMatcher(tpl, true, false, false)
258 return r
259 }
260
261 // MatcherFunc ----------------------------------------------------------------
262
263 // MatcherFunc is the function signature used by custom matchers.
264 type MatcherFunc func(*http.Request, *RouteMatch) bool
265
266 func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
267 return m(r, match)
268 }
269
270 // MatcherFunc adds a custom function to be used as request matcher.
271 func (r *Route) MatcherFunc(f MatcherFunc) *Route {
272 return r.addMatcher(f)
273 }
274
275 // Methods --------------------------------------------------------------------
276
277 // methodMatcher matches the request against HTTP methods.
278 type methodMatcher []string
279
280 func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
281 return matchInArray(m, r.Method)
282 }
283
284 // Methods adds a matcher for HTTP methods.
285 // It accepts a sequence of one or more methods to be matched, e.g.:
286 // "GET", "POST", "PUT".
287 func (r *Route) Methods(methods ...string) *Route {
288 for k, v := range methods {
289 methods[k] = strings.ToUpper(v)
290 }
291 return r.addMatcher(methodMatcher(methods))
292 }
293
294 // Path -----------------------------------------------------------------------
295
296 // Path adds a matcher for the URL path.
297 // It accepts a template with zero or more URL variables enclosed by {}. The
298 // template must start with a "/".
299 // Variables can define an optional regexp pattern to be matched:
300 //
301 // - {name} matches anything until the next slash.
302 //
303 // - {name:pattern} matches the given regexp pattern.
304 //
305 // For example:
306 //
307 // r := mux.NewRouter()
308 // r.Path("/products/").Handler(ProductsHandler)
309 // r.Path("/products/{key}").Handler(ProductsHandler)
310 // r.Path("/articles/{category}/{id:[0-9]+}").
311 // Handler(ArticleHandler)
312 //
313 // Variable names must be unique in a given route. They can be retrieved
314 // calling mux.Vars(request).
315 func (r *Route) Path(tpl string) *Route {
316 r.err = r.addRegexpMatcher(tpl, false, false, false)
317 return r
318 }
319
320 // PathPrefix -----------------------------------------------------------------
321
322 // PathPrefix adds a matcher for the URL path prefix. This matches if the given
323 // template is a prefix of the full URL path. See Route.Path() for details on
324 // the tpl argument.
325 //
326 // Note that it does not treat slashes specially ("/foobar/" will be matched by
327 // the prefix "/foo") so you may want to use a trailing slash here.
328 //
329 // Also note that the setting of Router.StrictSlash() has no effect on routes
330 // with a PathPrefix matcher.
331 func (r *Route) PathPrefix(tpl string) *Route {
332 r.err = r.addRegexpMatcher(tpl, false, true, false)
333 return r
334 }
335
336 // Query ----------------------------------------------------------------------
337
338 // Queries adds a matcher for URL query values.
339 // It accepts a sequence of key/value pairs. Values may define variables.
340 // For example:
341 //
342 // r := mux.NewRouter()
343 // r.Queries("foo", "bar", "id", "{id:[0-9]+}")
344 //
345 // The above route will only match if the URL contains the defined queries
346 // values, e.g.: ?foo=bar&id=42.
347 //
348 // It the value is an empty string, it will match any value if the key is set.
349 //
350 // Variables can define an optional regexp pattern to be matched:
351 //
352 // - {name} matches anything until the next slash.
353 //
354 // - {name:pattern} matches the given regexp pattern.
355 func (r *Route) Queries(pairs ...string) *Route {
356 length := len(pairs)
357 if length%2 != 0 {
358 r.err = fmt.Errorf(
359 "mux: number of parameters must be multiple of 2, got %v", pairs)
360 return nil
361 }
362 for i := 0; i < length; i += 2 {
363 if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil {
364 return r
365 }
366 }
367
368 return r
369 }
370
371 // Schemes --------------------------------------------------------------------
372
373 // schemeMatcher matches the request against URL schemes.
374 type schemeMatcher []string
375
376 func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
377 return matchInArray(m, r.URL.Scheme)
378 }
379
380 // Schemes adds a matcher for URL schemes.
381 // It accepts a sequence of schemes to be matched, e.g.: "http", "https".
382 func (r *Route) Schemes(schemes ...string) *Route {
383 for k, v := range schemes {
384 schemes[k] = strings.ToLower(v)
385 }
386 return r.addMatcher(schemeMatcher(schemes))
387 }
388
389 // BuildVarsFunc --------------------------------------------------------------
390
391 // BuildVarsFunc is the function signature used by custom build variable
392 // functions (which can modify route variables before a route's URL is built).
393 type BuildVarsFunc func(map[string]string) map[string]string
394
395 // BuildVarsFunc adds a custom function to be used to modify build variables
396 // before a route's URL is built.
397 func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
398 r.buildVarsFunc = f
399 return r
400 }
401
402 // Subrouter ------------------------------------------------------------------
403
404 // Subrouter creates a subrouter for the route.
405 //
406 // It will test the inner routes only if the parent route matched. For example:
407 //
408 // r := mux.NewRouter()
409 // s := r.Host("www.example.com").Subrouter()
410 // s.HandleFunc("/products/", ProductsHandler)
411 // s.HandleFunc("/products/{key}", ProductHandler)
412 // s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
413 //
414 // Here, the routes registered in the subrouter won't be tested if the host
415 // doesn't match.
416 func (r *Route) Subrouter() *Router {
417 router := &Router{parent: r, strictSlash: r.strictSlash}
418 r.addMatcher(router)
419 return router
420 }
421
422 // ----------------------------------------------------------------------------
423 // URL building
424 // ----------------------------------------------------------------------------
425
426 // URL builds a URL for the route.
427 //
428 // It accepts a sequence of key/value pairs for the route variables. For
429 // example, given this route:
430 //
431 // r := mux.NewRouter()
432 // r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
433 // Name("article")
434 //
435 // ...a URL for it can be built using:
436 //
437 // url, err := r.Get("article").URL("category", "technology", "id", "42")
438 //
439 // ...which will return an url.URL with the following path:
440 //
441 // "/articles/technology/42"
442 //
443 // This also works for host variables:
444 //
445 // r := mux.NewRouter()
446 // r.Host("{subdomain}.domain.com").
447 // HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
448 // Name("article")
449 //
450 // // url.String() will be "http://news.domain.com/articles/technology/42"
451 // url, err := r.Get("article").URL("subdomain", "news",
452 // "category", "technology",
453 // "id", "42")
454 //
455 // All variables defined in the route are required, and their values must
456 // conform to the corresponding patterns.
457 func (r *Route) URL(pairs ...string) (*url.URL, error) {
458 if r.err != nil {
459 return nil, r.err
460 }
461 if r.regexp == nil {
462 return nil, errors.New("mux: route doesn't have a host or path")
463 }
464 values, err := r.prepareVars(pairs...)
465 if err != nil {
466 return nil, err
467 }
468 var scheme, host, path string
469 if r.regexp.host != nil {
470 // Set a default scheme.
471 scheme = "http"
472 if host, err = r.regexp.host.url(values); err != nil {
473 return nil, err
474 }
475 }
476 if r.regexp.path != nil {
477 if path, err = r.regexp.path.url(values); err != nil {
478 return nil, err
479 }
480 }
481 return &url.URL{
482 Scheme: scheme,
483 Host: host,
484 Path: path,
485 }, nil
486 }
487
488 // URLHost builds the host part of the URL for a route. See Route.URL().
489 //
490 // The route must have a host defined.
491 func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
492 if r.err != nil {
493 return nil, r.err
494 }
495 if r.regexp == nil || r.regexp.host == nil {
496 return nil, errors.New("mux: route doesn't have a host")
497 }
498 values, err := r.prepareVars(pairs...)
499 if err != nil {
500 return nil, err
501 }
502 host, err := r.regexp.host.url(values)
503 if err != nil {
504 return nil, err
505 }
506 return &url.URL{
507 Scheme: "http",
508 Host: host,
509 }, nil
510 }
511
512 // URLPath builds the path part of the URL for a route. See Route.URL().
513 //
514 // The route must have a path defined.
515 func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
516 if r.err != nil {
517 return nil, r.err
518 }
519 if r.regexp == nil || r.regexp.path == nil {
520 return nil, errors.New("mux: route doesn't have a path")
521 }
522 values, err := r.prepareVars(pairs...)
523 if err != nil {
524 return nil, err
525 }
526 path, err := r.regexp.path.url(values)
527 if err != nil {
528 return nil, err
529 }
530 return &url.URL{
531 Path: path,
532 }, nil
533 }
534
535 // prepareVars converts the route variable pairs into a map. If the route has a
536 // BuildVarsFunc, it is invoked.
537 func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
538 m, err := mapFromPairsToString(pairs...)
539 if err != nil {
540 return nil, err
541 }
542 return r.buildVars(m), nil
543 }
544
545 func (r *Route) buildVars(m map[string]string) map[string]string {
546 if r.parent != nil {
547 m = r.parent.buildVars(m)
548 }
549 if r.buildVarsFunc != nil {
550 m = r.buildVarsFunc(m)
551 }
552 return m
553 }
554
555 // ----------------------------------------------------------------------------
556 // parentRoute
557 // ----------------------------------------------------------------------------
558
559 // parentRoute allows routes to know about parent host and path definitions.
560 type parentRoute interface {
561 getNamedRoutes() map[string]*Route
562 getRegexpGroup() *routeRegexpGroup
563 buildVars(map[string]string) map[string]string
564 }
565
566 // getNamedRoutes returns the map where named routes are registered.
567 func (r *Route) getNamedRoutes() map[string]*Route {
568 if r.parent == nil {
569 // During tests router is not always set.
570 r.parent = NewRouter()
571 }
572 return r.parent.getNamedRoutes()
573 }
574
575 // getRegexpGroup returns regexp definitions from this route.
576 func (r *Route) getRegexpGroup() *routeRegexpGroup {
577 if r.regexp == nil {
578 if r.parent == nil {
579 // During tests router is not always set.
580 r.parent = NewRouter()
581 }
582 regexp := r.parent.getRegexpGroup()
583 if regexp == nil {
584 r.regexp = new(routeRegexpGroup)
585 } else {
586 // Copy.
587 r.regexp = &routeRegexpGroup{
588 host: regexp.host,
589 path: regexp.path,
590 queries: regexp.queries,
591 }
592 }
593 }
594 return r.regexp
595 }