route.go 21 KB

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