route.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  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, matchQuery bool) error {
  121. if r.err != nil {
  122. return r.err
  123. }
  124. r.regexp = r.getRegexpGroup()
  125. if !matchHost && !matchQuery {
  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, matchQuery, r.strictSlash)
  134. if err != nil {
  135. return err
  136. }
  137. for _, q := range r.regexp.queries {
  138. if err = uniqueVars(rr.varsN, q.varsN); err != nil {
  139. return err
  140. }
  141. }
  142. if matchHost {
  143. if r.regexp.path != nil {
  144. if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
  145. return err
  146. }
  147. }
  148. r.regexp.host = rr
  149. } else {
  150. if r.regexp.host != nil {
  151. if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
  152. return err
  153. }
  154. }
  155. if matchQuery {
  156. r.regexp.queries = append(r.regexp.queries, rr)
  157. } else {
  158. r.regexp.path = rr
  159. }
  160. }
  161. r.addMatcher(rr)
  162. return nil
  163. }
  164. // Headers --------------------------------------------------------------------
  165. // headerMatcher matches the request against header values.
  166. type headerMatcher map[string]string
  167. func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
  168. return matchMap(m, r.Header, true)
  169. }
  170. // Headers adds a matcher for request header values.
  171. // It accepts a sequence of key/value pairs to be matched. For example:
  172. //
  173. // r := mux.NewRouter()
  174. // r.Headers("Content-Type", "application/json",
  175. // "X-Requested-With", "XMLHttpRequest")
  176. //
  177. // The above route will only match if both request header values match.
  178. //
  179. // It the value is an empty string, it will match any value if the key is set.
  180. func (r *Route) Headers(pairs ...string) *Route {
  181. if r.err == nil {
  182. var headers map[string]string
  183. headers, r.err = mapFromPairs(pairs...)
  184. return r.addMatcher(headerMatcher(headers))
  185. }
  186. return r
  187. }
  188. // Host -----------------------------------------------------------------------
  189. // Host adds a matcher for the URL host.
  190. // It accepts a template with zero or more URL variables enclosed by {}.
  191. // Variables can define an optional regexp pattern to me matched:
  192. //
  193. // - {name} matches anything until the next dot.
  194. //
  195. // - {name:pattern} matches the given regexp pattern.
  196. //
  197. // For example:
  198. //
  199. // r := mux.NewRouter()
  200. // r.Host("www.domain.com")
  201. // r.Host("{subdomain}.domain.com")
  202. // r.Host("{subdomain:[a-z]+}.domain.com")
  203. //
  204. // Variable names must be unique in a given route. They can be retrieved
  205. // calling mux.Vars(request).
  206. func (r *Route) Host(tpl string) *Route {
  207. r.err = r.addRegexpMatcher(tpl, true, false, false)
  208. return r
  209. }
  210. // MatcherFunc ----------------------------------------------------------------
  211. // MatcherFunc is the function signature used by custom matchers.
  212. type MatcherFunc func(*http.Request, *RouteMatch) bool
  213. func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
  214. return m(r, match)
  215. }
  216. // MatcherFunc adds a custom function to be used as request matcher.
  217. func (r *Route) MatcherFunc(f MatcherFunc) *Route {
  218. return r.addMatcher(f)
  219. }
  220. // Methods --------------------------------------------------------------------
  221. // methodMatcher matches the request against HTTP methods.
  222. type methodMatcher []string
  223. func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
  224. return matchInArray(m, r.Method)
  225. }
  226. // Methods adds a matcher for HTTP methods.
  227. // It accepts a sequence of one or more methods to be matched, e.g.:
  228. // "GET", "POST", "PUT".
  229. func (r *Route) Methods(methods ...string) *Route {
  230. for k, v := range methods {
  231. methods[k] = strings.ToUpper(v)
  232. }
  233. return r.addMatcher(methodMatcher(methods))
  234. }
  235. // Path -----------------------------------------------------------------------
  236. // Path adds a matcher for the URL path.
  237. // It accepts a template with zero or more URL variables enclosed by {}. The
  238. // template must start with a "/".
  239. // Variables can define an optional regexp pattern to me matched:
  240. //
  241. // - {name} matches anything until the next slash.
  242. //
  243. // - {name:pattern} matches the given regexp pattern.
  244. //
  245. // For example:
  246. //
  247. // r := mux.NewRouter()
  248. // r.Path("/products/").Handler(ProductsHandler)
  249. // r.Path("/products/{key}").Handler(ProductsHandler)
  250. // r.Path("/articles/{category}/{id:[0-9]+}").
  251. // Handler(ArticleHandler)
  252. //
  253. // Variable names must be unique in a given route. They can be retrieved
  254. // calling mux.Vars(request).
  255. func (r *Route) Path(tpl string) *Route {
  256. r.err = r.addRegexpMatcher(tpl, false, false, false)
  257. return r
  258. }
  259. // PathPrefix -----------------------------------------------------------------
  260. // PathPrefix adds a matcher for the URL path prefix. This matches if the given
  261. // template is a prefix of the full URL path. See Route.Path() for details on
  262. // the tpl argument.
  263. //
  264. // Note that it does not treat slashes specially ("/foobar/" will be matched by
  265. // the prefix "/foo") so you may want to use a trailing slash here.
  266. //
  267. // Also note that the setting of Router.StrictSlash() has no effect on routes
  268. // with a PathPrefix matcher.
  269. func (r *Route) PathPrefix(tpl string) *Route {
  270. r.err = r.addRegexpMatcher(tpl, false, true, false)
  271. return r
  272. }
  273. // Query ----------------------------------------------------------------------
  274. // Queries adds a matcher for URL query values.
  275. // It accepts a sequence of key/value pairs. Values may define variables.
  276. // For example:
  277. //
  278. // r := mux.NewRouter()
  279. // r.Queries("foo", "bar", "id", "{id:[0-9]+}")
  280. //
  281. // The above route will only match if the URL contains the defined queries
  282. // values, e.g.: ?foo=bar&id=42.
  283. //
  284. // It the value is an empty string, it will match any value if the key is set.
  285. //
  286. // Variables can define an optional regexp pattern to me matched:
  287. //
  288. // - {name} matches anything until the next slash.
  289. //
  290. // - {name:pattern} matches the given regexp pattern.
  291. func (r *Route) Queries(pairs ...string) *Route {
  292. length := len(pairs)
  293. if length%2 != 0 {
  294. r.err = fmt.Errorf(
  295. "mux: number of parameters must be multiple of 2, got %v", pairs)
  296. return nil
  297. }
  298. for i := 0; i < length; i += 2 {
  299. if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, true, true); r.err != nil {
  300. return r
  301. }
  302. }
  303. return r
  304. }
  305. // Schemes --------------------------------------------------------------------
  306. // schemeMatcher matches the request against URL schemes.
  307. type schemeMatcher []string
  308. func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
  309. return matchInArray(m, r.URL.Scheme)
  310. }
  311. // Schemes adds a matcher for URL schemes.
  312. // It accepts a sequence of schemes to be matched, e.g.: "http", "https".
  313. func (r *Route) Schemes(schemes ...string) *Route {
  314. for k, v := range schemes {
  315. schemes[k] = strings.ToLower(v)
  316. }
  317. return r.addMatcher(schemeMatcher(schemes))
  318. }
  319. // Subrouter ------------------------------------------------------------------
  320. // Subrouter creates a subrouter for the route.
  321. //
  322. // It will test the inner routes only if the parent route matched. For example:
  323. //
  324. // r := mux.NewRouter()
  325. // s := r.Host("www.domain.com").Subrouter()
  326. // s.HandleFunc("/products/", ProductsHandler)
  327. // s.HandleFunc("/products/{key}", ProductHandler)
  328. // s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
  329. //
  330. // Here, the routes registered in the subrouter won't be tested if the host
  331. // doesn't match.
  332. func (r *Route) Subrouter() *Router {
  333. router := &Router{parent: r, strictSlash: r.strictSlash}
  334. r.addMatcher(router)
  335. return router
  336. }
  337. // ----------------------------------------------------------------------------
  338. // URL building
  339. // ----------------------------------------------------------------------------
  340. // URL builds a URL for the route.
  341. //
  342. // It accepts a sequence of key/value pairs for the route variables. For
  343. // example, given this route:
  344. //
  345. // r := mux.NewRouter()
  346. // r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
  347. // Name("article")
  348. //
  349. // ...a URL for it can be built using:
  350. //
  351. // url, err := r.Get("article").URL("category", "technology", "id", "42")
  352. //
  353. // ...which will return an url.URL with the following path:
  354. //
  355. // "/articles/technology/42"
  356. //
  357. // This also works for host variables:
  358. //
  359. // r := mux.NewRouter()
  360. // r.Host("{subdomain}.domain.com").
  361. // HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
  362. // Name("article")
  363. //
  364. // // url.String() will be "http://news.domain.com/articles/technology/42"
  365. // url, err := r.Get("article").URL("subdomain", "news",
  366. // "category", "technology",
  367. // "id", "42")
  368. //
  369. // All variables defined in the route are required, and their values must
  370. // conform to the corresponding patterns.
  371. func (r *Route) URL(pairs ...string) (*url.URL, error) {
  372. if r.err != nil {
  373. return nil, r.err
  374. }
  375. if r.regexp == nil {
  376. return nil, errors.New("mux: route doesn't have a host or path")
  377. }
  378. var scheme, host, path string
  379. var err error
  380. if r.regexp.host != nil {
  381. // Set a default scheme.
  382. scheme = "http"
  383. if host, err = r.regexp.host.url(pairs...); err != nil {
  384. return nil, err
  385. }
  386. }
  387. if r.regexp.path != nil {
  388. if path, err = r.regexp.path.url(pairs...); err != nil {
  389. return nil, err
  390. }
  391. }
  392. return &url.URL{
  393. Scheme: scheme,
  394. Host: host,
  395. Path: path,
  396. }, nil
  397. }
  398. // URLHost builds the host part of the URL for a route. See Route.URL().
  399. //
  400. // The route must have a host defined.
  401. func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
  402. if r.err != nil {
  403. return nil, r.err
  404. }
  405. if r.regexp == nil || r.regexp.host == nil {
  406. return nil, errors.New("mux: route doesn't have a host")
  407. }
  408. host, err := r.regexp.host.url(pairs...)
  409. if err != nil {
  410. return nil, err
  411. }
  412. return &url.URL{
  413. Scheme: "http",
  414. Host: host,
  415. }, nil
  416. }
  417. // URLPath builds the path part of the URL for a route. See Route.URL().
  418. //
  419. // The route must have a path defined.
  420. func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
  421. if r.err != nil {
  422. return nil, r.err
  423. }
  424. if r.regexp == nil || r.regexp.path == nil {
  425. return nil, errors.New("mux: route doesn't have a path")
  426. }
  427. path, err := r.regexp.path.url(pairs...)
  428. if err != nil {
  429. return nil, err
  430. }
  431. return &url.URL{
  432. Path: path,
  433. }, nil
  434. }
  435. // ----------------------------------------------------------------------------
  436. // parentRoute
  437. // ----------------------------------------------------------------------------
  438. // parentRoute allows routes to know about parent host and path definitions.
  439. type parentRoute interface {
  440. getNamedRoutes() map[string]*Route
  441. getRegexpGroup() *routeRegexpGroup
  442. }
  443. // getNamedRoutes returns the map where named routes are registered.
  444. func (r *Route) getNamedRoutes() map[string]*Route {
  445. if r.parent == nil {
  446. // During tests router is not always set.
  447. r.parent = NewRouter()
  448. }
  449. return r.parent.getNamedRoutes()
  450. }
  451. // getRegexpGroup returns regexp definitions from this route.
  452. func (r *Route) getRegexpGroup() *routeRegexpGroup {
  453. if r.regexp == nil {
  454. if r.parent == nil {
  455. // During tests router is not always set.
  456. r.parent = NewRouter()
  457. }
  458. regexp := r.parent.getRegexpGroup()
  459. if regexp == nil {
  460. r.regexp = new(routeRegexpGroup)
  461. } else {
  462. // Copy.
  463. r.regexp = &routeRegexpGroup{
  464. host: regexp.host,
  465. path: regexp.path,
  466. queries: regexp.queries,
  467. }
  468. }
  469. }
  470. return r.regexp
  471. }