You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

737 lines
21 KiB

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