You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

543 lines
16 KiB

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