mux.go (17782B)
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 "context" 9 "errors" 10 "fmt" 11 "net/http" 12 "path" 13 "regexp" 14 ) 15 16 var ( 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. 25 func 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 // 34 // var router = mux.NewRouter() 35 // 36 // func main() { 37 // http.Handle("/", router) 38 // } 39 // 40 // Or, for Google App Engine, register it in a init() function: 41 // 42 // func init() { 43 // http.Handle("/", router) 44 // } 45 // 46 // This will send all incoming requests to the router. 47 type Router struct { 48 // Configurable Handler to be used when no route matches. 49 // This can be used to render your own 404 Not Found errors. 50 NotFoundHandler http.Handler 51 52 // Configurable Handler to be used when the request method does not match the route. 53 // This can be used to render your own 405 Method Not Allowed errors. 54 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` 75 type 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` 100 func 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 122 func 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. 138 func (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). 175 func (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. 216 func (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. 222 func (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. 245 func (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 258 func (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" 269 func (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. 279 func (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(). 288 func (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(). 294 func (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(). 300 func (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(). 307 func (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(). 313 func (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(). 319 func (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(). 325 func (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(). 331 func (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(). 337 func (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(). 343 func (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(). 349 func (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. 355 func (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. 362 func (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. 368 var 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. 373 type WalkFunc func(route *Route, router *Router, ancestors []*Route) error 374 375 func (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. 411 type 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 422 type contextKey int 423 424 const ( 425 varsKey contextKey = iota 426 routeKey 427 ) 428 429 // Vars returns the route variables for the current request, if any. 430 func 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. 441 func CurrentRoute(r *http.Request) *Route { 442 if rv := r.Context().Value(routeKey); rv != nil { 443 return rv.(*Route) 444 } 445 return nil 446 } 447 448 func 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 453 func 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. 464 func 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. 482 func 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. 495 func 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. 506 func 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. 520 func 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. 537 func 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. 547 func 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 575 func 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. 602 func 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. 608 func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) }