]>
Commit | Line | Data |
---|---|---|
9b12e4fe JC |
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 | "path" | |
12 | "regexp" | |
13 | ||
14 | "github.com/fsouza/go-dockerclient/external/github.com/gorilla/context" | |
15 | ) | |
16 | ||
17 | // NewRouter returns a new router instance. | |
18 | func NewRouter() *Router { | |
19 | return &Router{namedRoutes: make(map[string]*Route), KeepContext: false} | |
20 | } | |
21 | ||
22 | // Router registers routes to be matched and dispatches a handler. | |
23 | // | |
24 | // It implements the http.Handler interface, so it can be registered to serve | |
25 | // requests: | |
26 | // | |
27 | // var router = mux.NewRouter() | |
28 | // | |
29 | // func main() { | |
30 | // http.Handle("/", router) | |
31 | // } | |
32 | // | |
33 | // Or, for Google App Engine, register it in a init() function: | |
34 | // | |
35 | // func init() { | |
36 | // http.Handle("/", router) | |
37 | // } | |
38 | // | |
39 | // This will send all incoming requests to the router. | |
40 | type Router struct { | |
41 | // Configurable Handler to be used when no route matches. | |
42 | NotFoundHandler http.Handler | |
43 | // Parent route, if this is a subrouter. | |
44 | parent parentRoute | |
45 | // Routes to be matched, in order. | |
46 | routes []*Route | |
47 | // Routes by name for URL building. | |
48 | namedRoutes map[string]*Route | |
49 | // See Router.StrictSlash(). This defines the flag for new routes. | |
50 | strictSlash bool | |
51 | // If true, do not clear the request context after handling the request | |
52 | KeepContext bool | |
53 | } | |
54 | ||
55 | // Match matches registered routes against the request. | |
56 | func (r *Router) Match(req *http.Request, match *RouteMatch) bool { | |
57 | for _, route := range r.routes { | |
58 | if route.Match(req, match) { | |
59 | return true | |
60 | } | |
61 | } | |
62 | ||
63 | // Closest match for a router (includes sub-routers) | |
64 | if r.NotFoundHandler != nil { | |
65 | match.Handler = r.NotFoundHandler | |
66 | return true | |
67 | } | |
68 | return false | |
69 | } | |
70 | ||
71 | // ServeHTTP dispatches the handler registered in the matched route. | |
72 | // | |
73 | // When there is a match, the route variables can be retrieved calling | |
74 | // mux.Vars(request). | |
75 | func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { | |
76 | // Clean path to canonical form and redirect. | |
77 | if p := cleanPath(req.URL.Path); p != req.URL.Path { | |
78 | ||
79 | // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query. | |
80 | // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue: | |
81 | // http://code.google.com/p/go/issues/detail?id=5252 | |
82 | url := *req.URL | |
83 | url.Path = p | |
84 | p = url.String() | |
85 | ||
86 | w.Header().Set("Location", p) | |
87 | w.WriteHeader(http.StatusMovedPermanently) | |
88 | return | |
89 | } | |
90 | var match RouteMatch | |
91 | var handler http.Handler | |
92 | if r.Match(req, &match) { | |
93 | handler = match.Handler | |
94 | setVars(req, match.Vars) | |
95 | setCurrentRoute(req, match.Route) | |
96 | } | |
97 | if handler == nil { | |
98 | handler = http.NotFoundHandler() | |
99 | } | |
100 | if !r.KeepContext { | |
101 | defer context.Clear(req) | |
102 | } | |
103 | handler.ServeHTTP(w, req) | |
104 | } | |
105 | ||
106 | // Get returns a route registered with the given name. | |
107 | func (r *Router) Get(name string) *Route { | |
108 | return r.getNamedRoutes()[name] | |
109 | } | |
110 | ||
111 | // GetRoute returns a route registered with the given name. This method | |
112 | // was renamed to Get() and remains here for backwards compatibility. | |
113 | func (r *Router) GetRoute(name string) *Route { | |
114 | return r.getNamedRoutes()[name] | |
115 | } | |
116 | ||
117 | // StrictSlash defines the trailing slash behavior for new routes. The initial | |
118 | // value is false. | |
119 | // | |
120 | // When true, if the route path is "/path/", accessing "/path" will redirect | |
121 | // to the former and vice versa. In other words, your application will always | |
122 | // see the path as specified in the route. | |
123 | // | |
124 | // When false, if the route path is "/path", accessing "/path/" will not match | |
125 | // this route and vice versa. | |
126 | // | |
127 | // Special case: when a route sets a path prefix using the PathPrefix() method, | |
128 | // strict slash is ignored for that route because the redirect behavior can't | |
129 | // be determined from a prefix alone. However, any subrouters created from that | |
130 | // route inherit the original StrictSlash setting. | |
131 | func (r *Router) StrictSlash(value bool) *Router { | |
132 | r.strictSlash = value | |
133 | return r | |
134 | } | |
135 | ||
136 | // ---------------------------------------------------------------------------- | |
137 | // parentRoute | |
138 | // ---------------------------------------------------------------------------- | |
139 | ||
140 | // getNamedRoutes returns the map where named routes are registered. | |
141 | func (r *Router) getNamedRoutes() map[string]*Route { | |
142 | if r.namedRoutes == nil { | |
143 | if r.parent != nil { | |
144 | r.namedRoutes = r.parent.getNamedRoutes() | |
145 | } else { | |
146 | r.namedRoutes = make(map[string]*Route) | |
147 | } | |
148 | } | |
149 | return r.namedRoutes | |
150 | } | |
151 | ||
152 | // getRegexpGroup returns regexp definitions from the parent route, if any. | |
153 | func (r *Router) getRegexpGroup() *routeRegexpGroup { | |
154 | if r.parent != nil { | |
155 | return r.parent.getRegexpGroup() | |
156 | } | |
157 | return nil | |
158 | } | |
159 | ||
160 | func (r *Router) buildVars(m map[string]string) map[string]string { | |
161 | if r.parent != nil { | |
162 | m = r.parent.buildVars(m) | |
163 | } | |
164 | return m | |
165 | } | |
166 | ||
167 | // ---------------------------------------------------------------------------- | |
168 | // Route factories | |
169 | // ---------------------------------------------------------------------------- | |
170 | ||
171 | // NewRoute registers an empty route. | |
172 | func (r *Router) NewRoute() *Route { | |
173 | route := &Route{parent: r, strictSlash: r.strictSlash} | |
174 | r.routes = append(r.routes, route) | |
175 | return route | |
176 | } | |
177 | ||
178 | // Handle registers a new route with a matcher for the URL path. | |
179 | // See Route.Path() and Route.Handler(). | |
180 | func (r *Router) Handle(path string, handler http.Handler) *Route { | |
181 | return r.NewRoute().Path(path).Handler(handler) | |
182 | } | |
183 | ||
184 | // HandleFunc registers a new route with a matcher for the URL path. | |
185 | // See Route.Path() and Route.HandlerFunc(). | |
186 | func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, | |
187 | *http.Request)) *Route { | |
188 | return r.NewRoute().Path(path).HandlerFunc(f) | |
189 | } | |
190 | ||
191 | // Headers registers a new route with a matcher for request header values. | |
192 | // See Route.Headers(). | |
193 | func (r *Router) Headers(pairs ...string) *Route { | |
194 | return r.NewRoute().Headers(pairs...) | |
195 | } | |
196 | ||
197 | // Host registers a new route with a matcher for the URL host. | |
198 | // See Route.Host(). | |
199 | func (r *Router) Host(tpl string) *Route { | |
200 | return r.NewRoute().Host(tpl) | |
201 | } | |
202 | ||
203 | // MatcherFunc registers a new route with a custom matcher function. | |
204 | // See Route.MatcherFunc(). | |
205 | func (r *Router) MatcherFunc(f MatcherFunc) *Route { | |
206 | return r.NewRoute().MatcherFunc(f) | |
207 | } | |
208 | ||
209 | // Methods registers a new route with a matcher for HTTP methods. | |
210 | // See Route.Methods(). | |
211 | func (r *Router) Methods(methods ...string) *Route { | |
212 | return r.NewRoute().Methods(methods...) | |
213 | } | |
214 | ||
215 | // Path registers a new route with a matcher for the URL path. | |
216 | // See Route.Path(). | |
217 | func (r *Router) Path(tpl string) *Route { | |
218 | return r.NewRoute().Path(tpl) | |
219 | } | |
220 | ||
221 | // PathPrefix registers a new route with a matcher for the URL path prefix. | |
222 | // See Route.PathPrefix(). | |
223 | func (r *Router) PathPrefix(tpl string) *Route { | |
224 | return r.NewRoute().PathPrefix(tpl) | |
225 | } | |
226 | ||
227 | // Queries registers a new route with a matcher for URL query values. | |
228 | // See Route.Queries(). | |
229 | func (r *Router) Queries(pairs ...string) *Route { | |
230 | return r.NewRoute().Queries(pairs...) | |
231 | } | |
232 | ||
233 | // Schemes registers a new route with a matcher for URL schemes. | |
234 | // See Route.Schemes(). | |
235 | func (r *Router) Schemes(schemes ...string) *Route { | |
236 | return r.NewRoute().Schemes(schemes...) | |
237 | } | |
238 | ||
239 | // BuildVars registers a new route with a custom function for modifying | |
240 | // route variables before building a URL. | |
241 | func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route { | |
242 | return r.NewRoute().BuildVarsFunc(f) | |
243 | } | |
244 | ||
245 | // Walk walks the router and all its sub-routers, calling walkFn for each route | |
246 | // in the tree. The routes are walked in the order they were added. Sub-routers | |
247 | // are explored depth-first. | |
248 | func (r *Router) Walk(walkFn WalkFunc) error { | |
249 | return r.walk(walkFn, []*Route{}) | |
250 | } | |
251 | ||
252 | // SkipRouter is used as a return value from WalkFuncs to indicate that the | |
253 | // router that walk is about to descend down to should be skipped. | |
254 | var SkipRouter = errors.New("skip this router") | |
255 | ||
256 | // WalkFunc is the type of the function called for each route visited by Walk. | |
257 | // At every invocation, it is given the current route, and the current router, | |
258 | // and a list of ancestor routes that lead to the current route. | |
259 | type WalkFunc func(route *Route, router *Router, ancestors []*Route) error | |
260 | ||
261 | func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error { | |
262 | for _, t := range r.routes { | |
263 | if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" { | |
264 | continue | |
265 | } | |
266 | ||
267 | err := walkFn(t, r, ancestors) | |
268 | if err == SkipRouter { | |
269 | continue | |
270 | } | |
271 | for _, sr := range t.matchers { | |
272 | if h, ok := sr.(*Router); ok { | |
273 | err := h.walk(walkFn, ancestors) | |
274 | if err != nil { | |
275 | return err | |
276 | } | |
277 | } | |
278 | } | |
279 | if h, ok := t.handler.(*Router); ok { | |
280 | ancestors = append(ancestors, t) | |
281 | err := h.walk(walkFn, ancestors) | |
282 | if err != nil { | |
283 | return err | |
284 | } | |
285 | ancestors = ancestors[:len(ancestors)-1] | |
286 | } | |
287 | } | |
288 | return nil | |
289 | } | |
290 | ||
291 | // ---------------------------------------------------------------------------- | |
292 | // Context | |
293 | // ---------------------------------------------------------------------------- | |
294 | ||
295 | // RouteMatch stores information about a matched route. | |
296 | type RouteMatch struct { | |
297 | Route *Route | |
298 | Handler http.Handler | |
299 | Vars map[string]string | |
300 | } | |
301 | ||
302 | type contextKey int | |
303 | ||
304 | const ( | |
305 | varsKey contextKey = iota | |
306 | routeKey | |
307 | ) | |
308 | ||
309 | // Vars returns the route variables for the current request, if any. | |
310 | func Vars(r *http.Request) map[string]string { | |
311 | if rv := context.Get(r, varsKey); rv != nil { | |
312 | return rv.(map[string]string) | |
313 | } | |
314 | return nil | |
315 | } | |
316 | ||
317 | // CurrentRoute returns the matched route for the current request, if any. | |
318 | // This only works when called inside the handler of the matched route | |
319 | // because the matched route is stored in the request context which is cleared | |
320 | // after the handler returns, unless the KeepContext option is set on the | |
321 | // Router. | |
322 | func CurrentRoute(r *http.Request) *Route { | |
323 | if rv := context.Get(r, routeKey); rv != nil { | |
324 | return rv.(*Route) | |
325 | } | |
326 | return nil | |
327 | } | |
328 | ||
329 | func setVars(r *http.Request, val interface{}) { | |
330 | if val != nil { | |
331 | context.Set(r, varsKey, val) | |
332 | } | |
333 | } | |
334 | ||
335 | func setCurrentRoute(r *http.Request, val interface{}) { | |
336 | if val != nil { | |
337 | context.Set(r, routeKey, val) | |
338 | } | |
339 | } | |
340 | ||
341 | // ---------------------------------------------------------------------------- | |
342 | // Helpers | |
343 | // ---------------------------------------------------------------------------- | |
344 | ||
345 | // cleanPath returns the canonical path for p, eliminating . and .. elements. | |
346 | // Borrowed from the net/http package. | |
347 | func cleanPath(p string) string { | |
348 | if p == "" { | |
349 | return "/" | |
350 | } | |
351 | if p[0] != '/' { | |
352 | p = "/" + p | |
353 | } | |
354 | np := path.Clean(p) | |
355 | // path.Clean removes trailing slash except for root; | |
356 | // put the trailing slash back if necessary. | |
357 | if p[len(p)-1] == '/' && np != "/" { | |
358 | np += "/" | |
359 | } | |
360 | return np | |
361 | } | |
362 | ||
363 | // uniqueVars returns an error if two slices contain duplicated strings. | |
364 | func uniqueVars(s1, s2 []string) error { | |
365 | for _, v1 := range s1 { | |
366 | for _, v2 := range s2 { | |
367 | if v1 == v2 { | |
368 | return fmt.Errorf("mux: duplicated route variable %q", v2) | |
369 | } | |
370 | } | |
371 | } | |
372 | return nil | |
373 | } | |
374 | ||
375 | // checkPairs returns the count of strings passed in, and an error if | |
376 | // the count is not an even number. | |
377 | func checkPairs(pairs ...string) (int, error) { | |
378 | length := len(pairs) | |
379 | if length%2 != 0 { | |
380 | return length, fmt.Errorf( | |
381 | "mux: number of parameters must be multiple of 2, got %v", pairs) | |
382 | } | |
383 | return length, nil | |
384 | } | |
385 | ||
386 | // mapFromPairsToString converts variadic string parameters to a | |
387 | // string to string map. | |
388 | func mapFromPairsToString(pairs ...string) (map[string]string, error) { | |
389 | length, err := checkPairs(pairs...) | |
390 | if err != nil { | |
391 | return nil, err | |
392 | } | |
393 | m := make(map[string]string, length/2) | |
394 | for i := 0; i < length; i += 2 { | |
395 | m[pairs[i]] = pairs[i+1] | |
396 | } | |
397 | return m, nil | |
398 | } | |
399 | ||
400 | // mapFromPairsToRegex converts variadic string paramers to a | |
401 | // string to regex map. | |
402 | func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) { | |
403 | length, err := checkPairs(pairs...) | |
404 | if err != nil { | |
405 | return nil, err | |
406 | } | |
407 | m := make(map[string]*regexp.Regexp, length/2) | |
408 | for i := 0; i < length; i += 2 { | |
409 | regex, err := regexp.Compile(pairs[i+1]) | |
410 | if err != nil { | |
411 | return nil, err | |
412 | } | |
413 | m[pairs[i]] = regex | |
414 | } | |
415 | return m, nil | |
416 | } | |
417 | ||
418 | // matchInArray returns true if the given string value is in the array. | |
419 | func matchInArray(arr []string, value string) bool { | |
420 | for _, v := range arr { | |
421 | if v == value { | |
422 | return true | |
423 | } | |
424 | } | |
425 | return false | |
426 | } | |
427 | ||
428 | // matchMapWithString returns true if the given key/value pairs exist in a given map. | |
429 | func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool { | |
430 | for k, v := range toCheck { | |
431 | // Check if key exists. | |
432 | if canonicalKey { | |
433 | k = http.CanonicalHeaderKey(k) | |
434 | } | |
435 | if values := toMatch[k]; values == nil { | |
436 | return false | |
437 | } else if v != "" { | |
438 | // If value was defined as an empty string we only check that the | |
439 | // key exists. Otherwise we also check for equality. | |
440 | valueExists := false | |
441 | for _, value := range values { | |
442 | if v == value { | |
443 | valueExists = true | |
444 | break | |
445 | } | |
446 | } | |
447 | if !valueExists { | |
448 | return false | |
449 | } | |
450 | } | |
451 | } | |
452 | return true | |
453 | } | |
454 | ||
455 | // matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against | |
456 | // the given regex | |
457 | func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool { | |
458 | for k, v := range toCheck { | |
459 | // Check if key exists. | |
460 | if canonicalKey { | |
461 | k = http.CanonicalHeaderKey(k) | |
462 | } | |
463 | if values := toMatch[k]; values == nil { | |
464 | return false | |
465 | } else if v != nil { | |
466 | // If value was defined as an empty string we only check that the | |
467 | // key exists. Otherwise we also check for equality. | |
468 | valueExists := false | |
469 | for _, value := range values { | |
470 | if v.MatchString(value) { | |
471 | valueExists = true | |
472 | break | |
473 | } | |
474 | } | |
475 | if !valueExists { | |
476 | return false | |
477 | } | |
478 | } | |
479 | } | |
480 | return true | |
481 | } |