本站源代码
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.

589 lines
17KB

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