123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763 |
- package mux
- import (
- "errors"
- "fmt"
- "net/http"
- "net/url"
- "regexp"
- "strings"
- )
- type Route struct {
-
- parent parentRoute
-
- handler http.Handler
-
- matchers []matcher
-
- regexp *routeRegexpGroup
-
-
- strictSlash bool
-
-
- skipClean bool
-
- useEncodedPath bool
-
- buildScheme string
-
- buildOnly bool
-
- name string
-
- err error
- buildVarsFunc BuildVarsFunc
- }
- func (r *Route) SkipClean() bool {
- return r.skipClean
- }
- func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
- if r.buildOnly || r.err != nil {
- return false
- }
- var matchErr error
-
- for _, m := range r.matchers {
- if matched := m.Match(req, match); !matched {
- if _, ok := m.(methodMatcher); ok {
- matchErr = ErrMethodMismatch
- continue
- }
- matchErr = nil
- return false
- }
- }
- if matchErr != nil {
- match.MatchErr = matchErr
- return false
- }
- if match.MatchErr == ErrMethodMismatch {
-
- match.MatchErr = nil
-
- match.Handler = r.handler
- }
-
- if match.Route == nil {
- match.Route = r
- }
- if match.Handler == nil {
- match.Handler = r.handler
- }
- if match.Vars == nil {
- match.Vars = make(map[string]string)
- }
-
- if r.regexp != nil {
- r.regexp.setMatch(req, match, r)
- }
- return true
- }
- func (r *Route) GetError() error {
- return r.err
- }
- func (r *Route) BuildOnly() *Route {
- r.buildOnly = true
- return r
- }
- func (r *Route) Handler(handler http.Handler) *Route {
- if r.err == nil {
- r.handler = handler
- }
- return r
- }
- func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
- return r.Handler(http.HandlerFunc(f))
- }
- func (r *Route) GetHandler() http.Handler {
- return r.handler
- }
- func (r *Route) Name(name string) *Route {
- if r.name != "" {
- r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
- r.name, name)
- }
- if r.err == nil {
- r.name = name
- r.getNamedRoutes()[name] = r
- }
- return r
- }
- func (r *Route) GetName() string {
- return r.name
- }
- type matcher interface {
- Match(*http.Request, *RouteMatch) bool
- }
- func (r *Route) addMatcher(m matcher) *Route {
- if r.err == nil {
- r.matchers = append(r.matchers, m)
- }
- return r
- }
- func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error {
- if r.err != nil {
- return r.err
- }
- r.regexp = r.getRegexpGroup()
- if typ == regexpTypePath || typ == regexpTypePrefix {
- if len(tpl) > 0 && tpl[0] != '/' {
- return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
- }
- if r.regexp.path != nil {
- tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
- }
- }
- rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{
- strictSlash: r.strictSlash,
- useEncodedPath: r.useEncodedPath,
- })
- if err != nil {
- return err
- }
- for _, q := range r.regexp.queries {
- if err = uniqueVars(rr.varsN, q.varsN); err != nil {
- return err
- }
- }
- if typ == regexpTypeHost {
- if r.regexp.path != nil {
- if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
- return err
- }
- }
- r.regexp.host = rr
- } else {
- if r.regexp.host != nil {
- if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
- return err
- }
- }
- if typ == regexpTypeQuery {
- r.regexp.queries = append(r.regexp.queries, rr)
- } else {
- r.regexp.path = rr
- }
- }
- r.addMatcher(rr)
- return nil
- }
- type headerMatcher map[string]string
- func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
- return matchMapWithString(m, r.Header, true)
- }
- func (r *Route) Headers(pairs ...string) *Route {
- if r.err == nil {
- var headers map[string]string
- headers, r.err = mapFromPairsToString(pairs...)
- return r.addMatcher(headerMatcher(headers))
- }
- return r
- }
- type headerRegexMatcher map[string]*regexp.Regexp
- func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
- return matchMapWithRegex(m, r.Header, true)
- }
- func (r *Route) HeadersRegexp(pairs ...string) *Route {
- if r.err == nil {
- var headers map[string]*regexp.Regexp
- headers, r.err = mapFromPairsToRegex(pairs...)
- return r.addMatcher(headerRegexMatcher(headers))
- }
- return r
- }
- func (r *Route) Host(tpl string) *Route {
- r.err = r.addRegexpMatcher(tpl, regexpTypeHost)
- return r
- }
- type MatcherFunc func(*http.Request, *RouteMatch) bool
- func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
- return m(r, match)
- }
- func (r *Route) MatcherFunc(f MatcherFunc) *Route {
- return r.addMatcher(f)
- }
- type methodMatcher []string
- func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
- return matchInArray(m, r.Method)
- }
- func (r *Route) Methods(methods ...string) *Route {
- for k, v := range methods {
- methods[k] = strings.ToUpper(v)
- }
- return r.addMatcher(methodMatcher(methods))
- }
- func (r *Route) Path(tpl string) *Route {
- r.err = r.addRegexpMatcher(tpl, regexpTypePath)
- return r
- }
- func (r *Route) PathPrefix(tpl string) *Route {
- r.err = r.addRegexpMatcher(tpl, regexpTypePrefix)
- return r
- }
- func (r *Route) Queries(pairs ...string) *Route {
- length := len(pairs)
- if length%2 != 0 {
- r.err = fmt.Errorf(
- "mux: number of parameters must be multiple of 2, got %v", pairs)
- return nil
- }
- for i := 0; i < length; i += 2 {
- if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], regexpTypeQuery); r.err != nil {
- return r
- }
- }
- return r
- }
- type schemeMatcher []string
- func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
- return matchInArray(m, r.URL.Scheme)
- }
- func (r *Route) Schemes(schemes ...string) *Route {
- for k, v := range schemes {
- schemes[k] = strings.ToLower(v)
- }
- if r.buildScheme == "" && len(schemes) > 0 {
- r.buildScheme = schemes[0]
- }
- return r.addMatcher(schemeMatcher(schemes))
- }
- type BuildVarsFunc func(map[string]string) map[string]string
- func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
- r.buildVarsFunc = f
- return r
- }
- func (r *Route) Subrouter() *Router {
- router := &Router{parent: r, strictSlash: r.strictSlash}
- r.addMatcher(router)
- return router
- }
- func (r *Route) URL(pairs ...string) (*url.URL, error) {
- if r.err != nil {
- return nil, r.err
- }
- if r.regexp == nil {
- return nil, errors.New("mux: route doesn't have a host or path")
- }
- values, err := r.prepareVars(pairs...)
- if err != nil {
- return nil, err
- }
- var scheme, host, path string
- queries := make([]string, 0, len(r.regexp.queries))
- if r.regexp.host != nil {
- if host, err = r.regexp.host.url(values); err != nil {
- return nil, err
- }
- scheme = "http"
- if s := r.getBuildScheme(); s != "" {
- scheme = s
- }
- }
- if r.regexp.path != nil {
- if path, err = r.regexp.path.url(values); err != nil {
- return nil, err
- }
- }
- for _, q := range r.regexp.queries {
- var query string
- if query, err = q.url(values); err != nil {
- return nil, err
- }
- queries = append(queries, query)
- }
- return &url.URL{
- Scheme: scheme,
- Host: host,
- Path: path,
- RawQuery: strings.Join(queries, "&"),
- }, nil
- }
- func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
- if r.err != nil {
- return nil, r.err
- }
- if r.regexp == nil || r.regexp.host == nil {
- return nil, errors.New("mux: route doesn't have a host")
- }
- values, err := r.prepareVars(pairs...)
- if err != nil {
- return nil, err
- }
- host, err := r.regexp.host.url(values)
- if err != nil {
- return nil, err
- }
- u := &url.URL{
- Scheme: "http",
- Host: host,
- }
- if s := r.getBuildScheme(); s != "" {
- u.Scheme = s
- }
- return u, nil
- }
- func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
- if r.err != nil {
- return nil, r.err
- }
- if r.regexp == nil || r.regexp.path == nil {
- return nil, errors.New("mux: route doesn't have a path")
- }
- values, err := r.prepareVars(pairs...)
- if err != nil {
- return nil, err
- }
- path, err := r.regexp.path.url(values)
- if err != nil {
- return nil, err
- }
- return &url.URL{
- Path: path,
- }, nil
- }
- func (r *Route) GetPathTemplate() (string, error) {
- if r.err != nil {
- return "", r.err
- }
- if r.regexp == nil || r.regexp.path == nil {
- return "", errors.New("mux: route doesn't have a path")
- }
- return r.regexp.path.template, nil
- }
- func (r *Route) GetPathRegexp() (string, error) {
- if r.err != nil {
- return "", r.err
- }
- if r.regexp == nil || r.regexp.path == nil {
- return "", errors.New("mux: route does not have a path")
- }
- return r.regexp.path.regexp.String(), nil
- }
- func (r *Route) GetQueriesRegexp() ([]string, error) {
- if r.err != nil {
- return nil, r.err
- }
- if r.regexp == nil || r.regexp.queries == nil {
- return nil, errors.New("mux: route doesn't have queries")
- }
- var queries []string
- for _, query := range r.regexp.queries {
- queries = append(queries, query.regexp.String())
- }
- return queries, nil
- }
- func (r *Route) GetQueriesTemplates() ([]string, error) {
- if r.err != nil {
- return nil, r.err
- }
- if r.regexp == nil || r.regexp.queries == nil {
- return nil, errors.New("mux: route doesn't have queries")
- }
- var queries []string
- for _, query := range r.regexp.queries {
- queries = append(queries, query.template)
- }
- return queries, nil
- }
- func (r *Route) GetMethods() ([]string, error) {
- if r.err != nil {
- return nil, r.err
- }
- for _, m := range r.matchers {
- if methods, ok := m.(methodMatcher); ok {
- return []string(methods), nil
- }
- }
- return nil, errors.New("mux: route doesn't have methods")
- }
- func (r *Route) GetHostTemplate() (string, error) {
- if r.err != nil {
- return "", r.err
- }
- if r.regexp == nil || r.regexp.host == nil {
- return "", errors.New("mux: route doesn't have a host")
- }
- return r.regexp.host.template, nil
- }
- func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
- m, err := mapFromPairsToString(pairs...)
- if err != nil {
- return nil, err
- }
- return r.buildVars(m), nil
- }
- func (r *Route) buildVars(m map[string]string) map[string]string {
- if r.parent != nil {
- m = r.parent.buildVars(m)
- }
- if r.buildVarsFunc != nil {
- m = r.buildVarsFunc(m)
- }
- return m
- }
- type parentRoute interface {
- getBuildScheme() string
- getNamedRoutes() map[string]*Route
- getRegexpGroup() *routeRegexpGroup
- buildVars(map[string]string) map[string]string
- }
- func (r *Route) getBuildScheme() string {
- if r.buildScheme != "" {
- return r.buildScheme
- }
- if r.parent != nil {
- return r.parent.getBuildScheme()
- }
- return ""
- }
- func (r *Route) getNamedRoutes() map[string]*Route {
- if r.parent == nil {
-
- r.parent = NewRouter()
- }
- return r.parent.getNamedRoutes()
- }
- func (r *Route) getRegexpGroup() *routeRegexpGroup {
- if r.regexp == nil {
- if r.parent == nil {
-
- r.parent = NewRouter()
- }
- regexp := r.parent.getRegexpGroup()
- if regexp == nil {
- r.regexp = new(routeRegexpGroup)
- } else {
-
- r.regexp = &routeRegexpGroup{
- host: regexp.host,
- path: regexp.path,
- queries: regexp.queries,
- }
- }
- }
- return r.regexp
- }
|