route.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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. "strings"
  11. )
  12. // Route stores information to match a request and build URLs.
  13. type Route struct {
  14. // Parent where the route was registered (a Router).
  15. parent parentRoute
  16. // Request handler for the route.
  17. handler http.Handler
  18. // List of matchers.
  19. matchers []matcher
  20. // Manager for the variables from host and path.
  21. regexp *routeRegexpGroup
  22. // If true, when the path pattern is "/path/", accessing "/path" will
  23. // redirect to the former and vice versa.
  24. strictSlash bool
  25. // If true, this route never matches: it is only used to build URLs.
  26. buildOnly bool
  27. // The name used to build URLs.
  28. name string
  29. // Error resulted from building a route.
  30. err error
  31. }
  32. // Match matches the route against the request.
  33. func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
  34. if r.buildOnly || r.err != nil {
  35. return false
  36. }
  37. // Match everything.
  38. for _, m := range r.matchers {
  39. if matched := m.Match(req, match); !matched {
  40. return false
  41. }
  42. }
  43. // Yay, we have a match. Let's collect some info about it.
  44. if match.Route == nil {
  45. match.Route = r
  46. }
  47. if match.Handler == nil {
  48. match.Handler = r.handler
  49. }
  50. if match.Vars == nil {
  51. match.Vars = make(map[string]string)
  52. }
  53. // Set variables.
  54. if r.regexp != nil {
  55. r.regexp.setMatch(req, match, r)
  56. }
  57. return true
  58. }
  59. // ----------------------------------------------------------------------------
  60. // Route attributes
  61. // ----------------------------------------------------------------------------
  62. // GetError returns an error resulted from building the route, if any.
  63. func (r *Route) GetError() error {
  64. return r.err
  65. }
  66. // BuildOnly sets the route to never match: it is only used to build URLs.
  67. func (r *Route) BuildOnly() *Route {
  68. r.buildOnly = true
  69. return r
  70. }
  71. // Handler --------------------------------------------------------------------
  72. // Handler sets a handler for the route.
  73. func (r *Route) Handler(handler http.Handler) *Route {
  74. if r.err == nil {
  75. r.handler = handler
  76. }
  77. return r
  78. }
  79. // HandlerFunc sets a handler function for the route.
  80. func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
  81. return r.Handler(http.HandlerFunc(f))
  82. }
  83. // GetHandler returns the handler for the route, if any.
  84. func (r *Route) GetHandler() http.Handler {
  85. return r.handler
  86. }
  87. // Name -----------------------------------------------------------------------
  88. // Name sets the name for the route, used to build URLs.
  89. // If the name was registered already it will be overwritten.
  90. func (r *Route) Name(name string) *Route {
  91. if r.name != "" {
  92. r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
  93. r.name, name)
  94. }
  95. if r.err == nil {
  96. r.name = name
  97. r.getNamedRoutes()[name] = r
  98. }
  99. return r
  100. }
  101. // GetName returns the name for the route, if any.
  102. func (r *Route) GetName() string {
  103. return r.name
  104. }
  105. // ----------------------------------------------------------------------------
  106. // Matchers
  107. // ----------------------------------------------------------------------------
  108. // matcher types try to match a request.
  109. type matcher interface {
  110. Match(*http.Request, *RouteMatch) bool
  111. }
  112. // addMatcher adds a matcher to the route.
  113. func (r *Route) addMatcher(m matcher) *Route {
  114. if r.err == nil {
  115. r.matchers = append(r.matchers, m)
  116. }
  117. return r
  118. }
  119. // addRegexpMatcher adds a host or path matcher and builder to a route.
  120. func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix bool) error {
  121. if r.err != nil {
  122. return r.err
  123. }
  124. r.regexp = r.getRegexpGroup()
  125. if !matchHost {
  126. if len(tpl) == 0 || tpl[0] != '/' {
  127. return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
  128. }
  129. if r.regexp.path != nil {
  130. tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
  131. }
  132. }
  133. rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, r.strictSlash)
  134. if err != nil {
  135. return err
  136. }
  137. if matchHost {
  138. if r.regexp.path != nil {
  139. if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
  140. return err
  141. }
  142. }
  143. r.regexp.host = rr
  144. } else {
  145. if r.regexp.host != nil {
  146. if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
  147. return err
  148. }
  149. }
  150. r.regexp.path = rr
  151. }
  152. r.addMatcher(rr)
  153. return nil
  154. }
  155. // Headers --------------------------------------------------------------------
  156. // headerMatcher matches the request against header values.
  157. type headerMatcher map[string]string
  158. func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
  159. return matchMap(m, r.Header, true)
  160. }
  161. // Headers adds a matcher for request header values.
  162. // It accepts a sequence of key/value pairs to be matched. For example:
  163. //
  164. // r := mux.NewRouter()
  165. // r.Headers("Content-Type", "application/json",
  166. // "X-Requested-With", "XMLHttpRequest")
  167. //
  168. // The above route will only match if both request header values match.
  169. //
  170. // It the value is an empty string, it will match any value if the key is set.
  171. func (r *Route) Headers(pairs ...string) *Route {
  172. if r.err == nil {
  173. var headers map[string]string
  174. headers, r.err = mapFromPairs(pairs...)
  175. return r.addMatcher(headerMatcher(headers))
  176. }
  177. return r
  178. }
  179. // Host -----------------------------------------------------------------------
  180. // Host adds a matcher for the URL host.
  181. // It accepts a template with zero or more URL variables enclosed by {}.
  182. // Variables can define an optional regexp pattern to me matched:
  183. //
  184. // - {name} matches anything until the next dot.
  185. //
  186. // - {name:pattern} matches the given regexp pattern.
  187. //
  188. // For example:
  189. //
  190. // r := mux.NewRouter()
  191. // r.Host("www.domain.com")
  192. // r.Host("{subdomain}.domain.com")
  193. // r.Host("{subdomain:[a-z]+}.domain.com")
  194. //
  195. // Variable names must be unique in a given route. They can be retrieved
  196. // calling mux.Vars(request).
  197. func (r *Route) Host(tpl string) *Route {
  198. r.err = r.addRegexpMatcher(tpl, true, false)
  199. return r
  200. }
  201. // MatcherFunc ----------------------------------------------------------------
  202. // MatcherFunc is the function signature used by custom matchers.
  203. type MatcherFunc func(*http.Request, *RouteMatch) bool
  204. func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
  205. return m(r, match)
  206. }
  207. // MatcherFunc adds a custom function to be used as request matcher.
  208. func (r *Route) MatcherFunc(f MatcherFunc) *Route {
  209. return r.addMatcher(f)
  210. }
  211. // Methods --------------------------------------------------------------------
  212. // methodMatcher matches the request against HTTP methods.
  213. type methodMatcher []string
  214. func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
  215. return matchInArray(m, r.Method)
  216. }
  217. // Methods adds a matcher for HTTP methods.
  218. // It accepts a sequence of one or more methods to be matched, e.g.:
  219. // "GET", "POST", "PUT".
  220. func (r *Route) Methods(methods ...string) *Route {
  221. for k, v := range methods {
  222. methods[k] = strings.ToUpper(v)
  223. }
  224. return r.addMatcher(methodMatcher(methods))
  225. }
  226. // Path -----------------------------------------------------------------------
  227. // Path adds a matcher for the URL path.
  228. // It accepts a template with zero or more URL variables enclosed by {}.
  229. // Variables can define an optional regexp pattern to me matched:
  230. //
  231. // - {name} matches anything until the next slash.
  232. //
  233. // - {name:pattern} matches the given regexp pattern.
  234. //
  235. // For example:
  236. //
  237. // r := mux.NewRouter()
  238. // r.Path("/products/").Handler(ProductsHandler)
  239. // r.Path("/products/{key}").Handler(ProductsHandler)
  240. // r.Path("/articles/{category}/{id:[0-9]+}").
  241. // Handler(ArticleHandler)
  242. //
  243. // Variable names must be unique in a given route. They can be retrieved
  244. // calling mux.Vars(request).
  245. func (r *Route) Path(tpl string) *Route {
  246. r.err = r.addRegexpMatcher(tpl, false, false)
  247. return r
  248. }
  249. // PathPrefix -----------------------------------------------------------------
  250. // PathPrefix adds a matcher for the URL path prefix.
  251. func (r *Route) PathPrefix(tpl string) *Route {
  252. r.strictSlash = false
  253. r.err = r.addRegexpMatcher(tpl, false, true)
  254. return r
  255. }
  256. // Query ----------------------------------------------------------------------
  257. // queryMatcher matches the request against URL queries.
  258. type queryMatcher map[string]string
  259. func (m queryMatcher) Match(r *http.Request, match *RouteMatch) bool {
  260. return matchMap(m, r.URL.Query(), false)
  261. }
  262. // Queries adds a matcher for URL query values.
  263. // It accepts a sequence of key/value pairs. For example:
  264. //
  265. // r := mux.NewRouter()
  266. // r.Queries("foo", "bar", "baz", "ding")
  267. //
  268. // The above route will only match if the URL contains the defined queries
  269. // values, e.g.: ?foo=bar&baz=ding.
  270. //
  271. // It the value is an empty string, it will match any value if the key is set.
  272. func (r *Route) Queries(pairs ...string) *Route {
  273. if r.err == nil {
  274. var queries map[string]string
  275. queries, r.err = mapFromPairs(pairs...)
  276. return r.addMatcher(queryMatcher(queries))
  277. }
  278. return r
  279. }
  280. // Schemes --------------------------------------------------------------------
  281. // schemeMatcher matches the request against URL schemes.
  282. type schemeMatcher []string
  283. func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
  284. return matchInArray(m, r.URL.Scheme)
  285. }
  286. // Schemes adds a matcher for URL schemes.
  287. // It accepts a sequence of schemes to be matched, e.g.: "http", "https".
  288. func (r *Route) Schemes(schemes ...string) *Route {
  289. for k, v := range schemes {
  290. schemes[k] = strings.ToLower(v)
  291. }
  292. return r.addMatcher(schemeMatcher(schemes))
  293. }
  294. // Subrouter ------------------------------------------------------------------
  295. // Subrouter creates a subrouter for the route.
  296. //
  297. // It will test the inner routes only if the parent route matched. For example:
  298. //
  299. // r := mux.NewRouter()
  300. // s := r.Host("www.domain.com").Subrouter()
  301. // s.HandleFunc("/products/", ProductsHandler)
  302. // s.HandleFunc("/products/{key}", ProductHandler)
  303. // s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
  304. //
  305. // Here, the routes registered in the subrouter won't be tested if the host
  306. // doesn't match.
  307. func (r *Route) Subrouter() *Router {
  308. router := &Router{parent: r, strictSlash: r.strictSlash}
  309. r.addMatcher(router)
  310. return router
  311. }
  312. // ----------------------------------------------------------------------------
  313. // URL building
  314. // ----------------------------------------------------------------------------
  315. // URL builds a URL for the route.
  316. //
  317. // It accepts a sequence of key/value pairs for the route variables. For
  318. // example, given this route:
  319. //
  320. // r := mux.NewRouter()
  321. // r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
  322. // Name("article")
  323. //
  324. // ...a URL for it can be built using:
  325. //
  326. // url, err := r.Get("article").URL("category", "technology", "id", "42")
  327. //
  328. // ...which will return an url.URL with the following path:
  329. //
  330. // "/articles/technology/42"
  331. //
  332. // This also works for host variables:
  333. //
  334. // r := mux.NewRouter()
  335. // r.Host("{subdomain}.domain.com").
  336. // HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
  337. // Name("article")
  338. //
  339. // // url.String() will be "http://news.domain.com/articles/technology/42"
  340. // url, err := r.Get("article").URL("subdomain", "news",
  341. // "category", "technology",
  342. // "id", "42")
  343. //
  344. // All variables defined in the route are required, and their values must
  345. // conform to the corresponding patterns.
  346. func (r *Route) URL(pairs ...string) (*url.URL, error) {
  347. if r.err != nil {
  348. return nil, r.err
  349. }
  350. if r.regexp == nil {
  351. return nil, errors.New("mux: route doesn't have a host or path")
  352. }
  353. var scheme, host, path string
  354. var err error
  355. if r.regexp.host != nil {
  356. // Set a default scheme.
  357. scheme = "http"
  358. if host, err = r.regexp.host.url(pairs...); err != nil {
  359. return nil, err
  360. }
  361. }
  362. if r.regexp.path != nil {
  363. if path, err = r.regexp.path.url(pairs...); err != nil {
  364. return nil, err
  365. }
  366. }
  367. return &url.URL{
  368. Scheme: scheme,
  369. Host: host,
  370. Path: path,
  371. }, nil
  372. }
  373. // URLHost builds the host part of the URL for a route. See Route.URL().
  374. //
  375. // The route must have a host defined.
  376. func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
  377. if r.err != nil {
  378. return nil, r.err
  379. }
  380. if r.regexp == nil || r.regexp.host == nil {
  381. return nil, errors.New("mux: route doesn't have a host")
  382. }
  383. host, err := r.regexp.host.url(pairs...)
  384. if err != nil {
  385. return nil, err
  386. }
  387. return &url.URL{
  388. Scheme: "http",
  389. Host: host,
  390. }, nil
  391. }
  392. // URLPath builds the path part of the URL for a route. See Route.URL().
  393. //
  394. // The route must have a path defined.
  395. func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
  396. if r.err != nil {
  397. return nil, r.err
  398. }
  399. if r.regexp == nil || r.regexp.path == nil {
  400. return nil, errors.New("mux: route doesn't have a path")
  401. }
  402. path, err := r.regexp.path.url(pairs...)
  403. if err != nil {
  404. return nil, err
  405. }
  406. return &url.URL{
  407. Path: path,
  408. }, nil
  409. }
  410. // ----------------------------------------------------------------------------
  411. // parentRoute
  412. // ----------------------------------------------------------------------------
  413. // parentRoute allows routes to know about parent host and path definitions.
  414. type parentRoute interface {
  415. getNamedRoutes() map[string]*Route
  416. getRegexpGroup() *routeRegexpGroup
  417. }
  418. // getNamedRoutes returns the map where named routes are registered.
  419. func (r *Route) getNamedRoutes() map[string]*Route {
  420. if r.parent == nil {
  421. // During tests router is not always set.
  422. r.parent = NewRouter()
  423. }
  424. return r.parent.getNamedRoutes()
  425. }
  426. // getRegexpGroup returns regexp definitions from this route.
  427. func (r *Route) getRegexpGroup() *routeRegexpGroup {
  428. if r.regexp == nil {
  429. if r.parent == nil {
  430. // During tests router is not always set.
  431. r.parent = NewRouter()
  432. }
  433. regexp := r.parent.getRegexpGroup()
  434. if regexp == nil {
  435. r.regexp = new(routeRegexpGroup)
  436. } else {
  437. // Copy.
  438. r.regexp = &routeRegexpGroup{
  439. host: regexp.host,
  440. path: regexp.path,
  441. }
  442. }
  443. }
  444. return r.regexp
  445. }