route.go 18 KB

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