route.go 21 KB

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