...

Source file src/github.com/gorilla/mux/mux.go

Documentation: github.com/gorilla/mux

     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  
     5  package mux
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"net/http"
    12  	"path"
    13  	"regexp"
    14  )
    15  
    16  var (
    17  	// ErrMethodMismatch is returned when the method in the request does not match
    18  	// the method defined against the route.
    19  	ErrMethodMismatch = errors.New("method is not allowed")
    20  	// ErrNotFound is returned when no route match is found.
    21  	ErrNotFound = errors.New("no matching route was found")
    22  )
    23  
    24  // NewRouter returns a new router instance.
    25  func NewRouter() *Router {
    26  	return &Router{namedRoutes: make(map[string]*Route)}
    27  }
    28  
    29  // Router registers routes to be matched and dispatches a handler.
    30  //
    31  // It implements the http.Handler interface, so it can be registered to serve
    32  // requests:
    33  //
    34  //	var router = mux.NewRouter()
    35  //
    36  //	func main() {
    37  //	    http.Handle("/", router)
    38  //	}
    39  //
    40  // Or, for Google App Engine, register it in a init() function:
    41  //
    42  //	func init() {
    43  //	    http.Handle("/", router)
    44  //	}
    45  //
    46  // This will send all incoming requests to the router.
    47  type Router struct {
    48  	// Configurable Handler to be used when no route matches.
    49  	// This can be used to render your own 404 Not Found errors.
    50  	NotFoundHandler http.Handler
    51  
    52  	// Configurable Handler to be used when the request method does not match the route.
    53  	// This can be used to render your own 405 Method Not Allowed errors.
    54  	MethodNotAllowedHandler http.Handler
    55  
    56  	// Routes to be matched, in order.
    57  	routes []*Route
    58  
    59  	// Routes by name for URL building.
    60  	namedRoutes map[string]*Route
    61  
    62  	// If true, do not clear the request context after handling the request.
    63  	//
    64  	// Deprecated: No effect, since the context is stored on the request itself.
    65  	KeepContext bool
    66  
    67  	// Slice of middlewares to be called after a match is found
    68  	middlewares []middleware
    69  
    70  	// configuration shared with `Route`
    71  	routeConf
    72  }
    73  
    74  // common route configuration shared between `Router` and `Route`
    75  type routeConf struct {
    76  	// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
    77  	useEncodedPath bool
    78  
    79  	// If true, when the path pattern is "/path/", accessing "/path" will
    80  	// redirect to the former and vice versa.
    81  	strictSlash bool
    82  
    83  	// If true, when the path pattern is "/path//to", accessing "/path//to"
    84  	// will not redirect
    85  	skipClean bool
    86  
    87  	// Manager for the variables from host and path.
    88  	regexp routeRegexpGroup
    89  
    90  	// List of matchers.
    91  	matchers []matcher
    92  
    93  	// The scheme used when building URLs.
    94  	buildScheme string
    95  
    96  	buildVarsFunc BuildVarsFunc
    97  }
    98  
    99  // returns an effective deep copy of `routeConf`
   100  func copyRouteConf(r routeConf) routeConf {
   101  	c := r
   102  
   103  	if r.regexp.path != nil {
   104  		c.regexp.path = copyRouteRegexp(r.regexp.path)
   105  	}
   106  
   107  	if r.regexp.host != nil {
   108  		c.regexp.host = copyRouteRegexp(r.regexp.host)
   109  	}
   110  
   111  	c.regexp.queries = make([]*routeRegexp, 0, len(r.regexp.queries))
   112  	for _, q := range r.regexp.queries {
   113  		c.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q))
   114  	}
   115  
   116  	c.matchers = make([]matcher, len(r.matchers))
   117  	copy(c.matchers, r.matchers)
   118  
   119  	return c
   120  }
   121  
   122  func copyRouteRegexp(r *routeRegexp) *routeRegexp {
   123  	c := *r
   124  	return &c
   125  }
   126  
   127  // Match attempts to match the given request against the router's registered routes.
   128  //
   129  // If the request matches a route of this router or one of its subrouters the Route,
   130  // Handler, and Vars fields of the the match argument are filled and this function
   131  // returns true.
   132  //
   133  // If the request does not match any of this router's or its subrouters' routes
   134  // then this function returns false. If available, a reason for the match failure
   135  // will be filled in the match argument's MatchErr field. If the match failure type
   136  // (eg: not found) has a registered handler, the handler is assigned to the Handler
   137  // field of the match argument.
   138  func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
   139  	for _, route := range r.routes {
   140  		if route.Match(req, match) {
   141  			// Build middleware chain if no error was found
   142  			if match.MatchErr == nil {
   143  				for i := len(r.middlewares) - 1; i >= 0; i-- {
   144  					match.Handler = r.middlewares[i].Middleware(match.Handler)
   145  				}
   146  			}
   147  			return true
   148  		}
   149  	}
   150  
   151  	if match.MatchErr == ErrMethodMismatch {
   152  		if r.MethodNotAllowedHandler != nil {
   153  			match.Handler = r.MethodNotAllowedHandler
   154  			return true
   155  		}
   156  
   157  		return false
   158  	}
   159  
   160  	// Closest match for a router (includes sub-routers)
   161  	if r.NotFoundHandler != nil {
   162  		match.Handler = r.NotFoundHandler
   163  		match.MatchErr = ErrNotFound
   164  		return true
   165  	}
   166  
   167  	match.MatchErr = ErrNotFound
   168  	return false
   169  }
   170  
   171  // ServeHTTP dispatches the handler registered in the matched route.
   172  //
   173  // When there is a match, the route variables can be retrieved calling
   174  // mux.Vars(request).
   175  func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   176  	if !r.skipClean {
   177  		path := req.URL.Path
   178  		if r.useEncodedPath {
   179  			path = req.URL.EscapedPath()
   180  		}
   181  		// Clean path to canonical form and redirect.
   182  		if p := cleanPath(path); p != path {
   183  
   184  			// Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
   185  			// This matches with fix in go 1.2 r.c. 4 for same problem.  Go Issue:
   186  			// http://code.google.com/p/go/issues/detail?id=5252
   187  			url := *req.URL
   188  			url.Path = p
   189  			p = url.String()
   190  
   191  			w.Header().Set("Location", p)
   192  			w.WriteHeader(http.StatusMovedPermanently)
   193  			return
   194  		}
   195  	}
   196  	var match RouteMatch
   197  	var handler http.Handler
   198  	if r.Match(req, &match) {
   199  		handler = match.Handler
   200  		req = requestWithVars(req, match.Vars)
   201  		req = requestWithRoute(req, match.Route)
   202  	}
   203  
   204  	if handler == nil && match.MatchErr == ErrMethodMismatch {
   205  		handler = methodNotAllowedHandler()
   206  	}
   207  
   208  	if handler == nil {
   209  		handler = http.NotFoundHandler()
   210  	}
   211  
   212  	handler.ServeHTTP(w, req)
   213  }
   214  
   215  // Get returns a route registered with the given name.
   216  func (r *Router) Get(name string) *Route {
   217  	return r.namedRoutes[name]
   218  }
   219  
   220  // GetRoute returns a route registered with the given name. This method
   221  // was renamed to Get() and remains here for backwards compatibility.
   222  func (r *Router) GetRoute(name string) *Route {
   223  	return r.namedRoutes[name]
   224  }
   225  
   226  // StrictSlash defines the trailing slash behavior for new routes. The initial
   227  // value is false.
   228  //
   229  // When true, if the route path is "/path/", accessing "/path" will perform a redirect
   230  // to the former and vice versa. In other words, your application will always
   231  // see the path as specified in the route.
   232  //
   233  // When false, if the route path is "/path", accessing "/path/" will not match
   234  // this route and vice versa.
   235  //
   236  // The re-direct is a HTTP 301 (Moved Permanently). Note that when this is set for
   237  // routes with a non-idempotent method (e.g. POST, PUT), the subsequent re-directed
   238  // request will be made as a GET by most clients. Use middleware or client settings
   239  // to modify this behaviour as needed.
   240  //
   241  // Special case: when a route sets a path prefix using the PathPrefix() method,
   242  // strict slash is ignored for that route because the redirect behavior can't
   243  // be determined from a prefix alone. However, any subrouters created from that
   244  // route inherit the original StrictSlash setting.
   245  func (r *Router) StrictSlash(value bool) *Router {
   246  	r.strictSlash = value
   247  	return r
   248  }
   249  
   250  // SkipClean defines the path cleaning behaviour for new routes. The initial
   251  // value is false. Users should be careful about which routes are not cleaned
   252  //
   253  // When true, if the route path is "/path//to", it will remain with the double
   254  // slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
   255  //
   256  // When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will
   257  // become /fetch/http/xkcd.com/534
   258  func (r *Router) SkipClean(value bool) *Router {
   259  	r.skipClean = value
   260  	return r
   261  }
   262  
   263  // UseEncodedPath tells the router to match the encoded original path
   264  // to the routes.
   265  // For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to".
   266  //
   267  // If not called, the router will match the unencoded path to the routes.
   268  // For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to"
   269  func (r *Router) UseEncodedPath() *Router {
   270  	r.useEncodedPath = true
   271  	return r
   272  }
   273  
   274  // ----------------------------------------------------------------------------
   275  // Route factories
   276  // ----------------------------------------------------------------------------
   277  
   278  // NewRoute registers an empty route.
   279  func (r *Router) NewRoute() *Route {
   280  	// initialize a route with a copy of the parent router's configuration
   281  	route := &Route{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes}
   282  	r.routes = append(r.routes, route)
   283  	return route
   284  }
   285  
   286  // Name registers a new route with a name.
   287  // See Route.Name().
   288  func (r *Router) Name(name string) *Route {
   289  	return r.NewRoute().Name(name)
   290  }
   291  
   292  // Handle registers a new route with a matcher for the URL path.
   293  // See Route.Path() and Route.Handler().
   294  func (r *Router) Handle(path string, handler http.Handler) *Route {
   295  	return r.NewRoute().Path(path).Handler(handler)
   296  }
   297  
   298  // HandleFunc registers a new route with a matcher for the URL path.
   299  // See Route.Path() and Route.HandlerFunc().
   300  func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
   301  	*http.Request)) *Route {
   302  	return r.NewRoute().Path(path).HandlerFunc(f)
   303  }
   304  
   305  // Headers registers a new route with a matcher for request header values.
   306  // See Route.Headers().
   307  func (r *Router) Headers(pairs ...string) *Route {
   308  	return r.NewRoute().Headers(pairs...)
   309  }
   310  
   311  // Host registers a new route with a matcher for the URL host.
   312  // See Route.Host().
   313  func (r *Router) Host(tpl string) *Route {
   314  	return r.NewRoute().Host(tpl)
   315  }
   316  
   317  // MatcherFunc registers a new route with a custom matcher function.
   318  // See Route.MatcherFunc().
   319  func (r *Router) MatcherFunc(f MatcherFunc) *Route {
   320  	return r.NewRoute().MatcherFunc(f)
   321  }
   322  
   323  // Methods registers a new route with a matcher for HTTP methods.
   324  // See Route.Methods().
   325  func (r *Router) Methods(methods ...string) *Route {
   326  	return r.NewRoute().Methods(methods...)
   327  }
   328  
   329  // Path registers a new route with a matcher for the URL path.
   330  // See Route.Path().
   331  func (r *Router) Path(tpl string) *Route {
   332  	return r.NewRoute().Path(tpl)
   333  }
   334  
   335  // PathPrefix registers a new route with a matcher for the URL path prefix.
   336  // See Route.PathPrefix().
   337  func (r *Router) PathPrefix(tpl string) *Route {
   338  	return r.NewRoute().PathPrefix(tpl)
   339  }
   340  
   341  // Queries registers a new route with a matcher for URL query values.
   342  // See Route.Queries().
   343  func (r *Router) Queries(pairs ...string) *Route {
   344  	return r.NewRoute().Queries(pairs...)
   345  }
   346  
   347  // Schemes registers a new route with a matcher for URL schemes.
   348  // See Route.Schemes().
   349  func (r *Router) Schemes(schemes ...string) *Route {
   350  	return r.NewRoute().Schemes(schemes...)
   351  }
   352  
   353  // BuildVarsFunc registers a new route with a custom function for modifying
   354  // route variables before building a URL.
   355  func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route {
   356  	return r.NewRoute().BuildVarsFunc(f)
   357  }
   358  
   359  // Walk walks the router and all its sub-routers, calling walkFn for each route
   360  // in the tree. The routes are walked in the order they were added. Sub-routers
   361  // are explored depth-first.
   362  func (r *Router) Walk(walkFn WalkFunc) error {
   363  	return r.walk(walkFn, []*Route{})
   364  }
   365  
   366  // SkipRouter is used as a return value from WalkFuncs to indicate that the
   367  // router that walk is about to descend down to should be skipped.
   368  var SkipRouter = errors.New("skip this router")
   369  
   370  // WalkFunc is the type of the function called for each route visited by Walk.
   371  // At every invocation, it is given the current route, and the current router,
   372  // and a list of ancestor routes that lead to the current route.
   373  type WalkFunc func(route *Route, router *Router, ancestors []*Route) error
   374  
   375  func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
   376  	for _, t := range r.routes {
   377  		err := walkFn(t, r, ancestors)
   378  		if err == SkipRouter {
   379  			continue
   380  		}
   381  		if err != nil {
   382  			return err
   383  		}
   384  		for _, sr := range t.matchers {
   385  			if h, ok := sr.(*Router); ok {
   386  				ancestors = append(ancestors, t)
   387  				err := h.walk(walkFn, ancestors)
   388  				if err != nil {
   389  					return err
   390  				}
   391  				ancestors = ancestors[:len(ancestors)-1]
   392  			}
   393  		}
   394  		if h, ok := t.handler.(*Router); ok {
   395  			ancestors = append(ancestors, t)
   396  			err := h.walk(walkFn, ancestors)
   397  			if err != nil {
   398  				return err
   399  			}
   400  			ancestors = ancestors[:len(ancestors)-1]
   401  		}
   402  	}
   403  	return nil
   404  }
   405  
   406  // ----------------------------------------------------------------------------
   407  // Context
   408  // ----------------------------------------------------------------------------
   409  
   410  // RouteMatch stores information about a matched route.
   411  type RouteMatch struct {
   412  	Route   *Route
   413  	Handler http.Handler
   414  	Vars    map[string]string
   415  
   416  	// MatchErr is set to appropriate matching error
   417  	// It is set to ErrMethodMismatch if there is a mismatch in
   418  	// the request method and route method
   419  	MatchErr error
   420  }
   421  
   422  type contextKey int
   423  
   424  const (
   425  	varsKey contextKey = iota
   426  	routeKey
   427  )
   428  
   429  // Vars returns the route variables for the current request, if any.
   430  func Vars(r *http.Request) map[string]string {
   431  	if rv := r.Context().Value(varsKey); rv != nil {
   432  		return rv.(map[string]string)
   433  	}
   434  	return nil
   435  }
   436  
   437  // CurrentRoute returns the matched route for the current request, if any.
   438  // This only works when called inside the handler of the matched route
   439  // because the matched route is stored in the request context which is cleared
   440  // after the handler returns.
   441  func CurrentRoute(r *http.Request) *Route {
   442  	if rv := r.Context().Value(routeKey); rv != nil {
   443  		return rv.(*Route)
   444  	}
   445  	return nil
   446  }
   447  
   448  func requestWithVars(r *http.Request, vars map[string]string) *http.Request {
   449  	ctx := context.WithValue(r.Context(), varsKey, vars)
   450  	return r.WithContext(ctx)
   451  }
   452  
   453  func requestWithRoute(r *http.Request, route *Route) *http.Request {
   454  	ctx := context.WithValue(r.Context(), routeKey, route)
   455  	return r.WithContext(ctx)
   456  }
   457  
   458  // ----------------------------------------------------------------------------
   459  // Helpers
   460  // ----------------------------------------------------------------------------
   461  
   462  // cleanPath returns the canonical path for p, eliminating . and .. elements.
   463  // Borrowed from the net/http package.
   464  func cleanPath(p string) string {
   465  	if p == "" {
   466  		return "/"
   467  	}
   468  	if p[0] != '/' {
   469  		p = "/" + p
   470  	}
   471  	np := path.Clean(p)
   472  	// path.Clean removes trailing slash except for root;
   473  	// put the trailing slash back if necessary.
   474  	if p[len(p)-1] == '/' && np != "/" {
   475  		np += "/"
   476  	}
   477  
   478  	return np
   479  }
   480  
   481  // uniqueVars returns an error if two slices contain duplicated strings.
   482  func uniqueVars(s1, s2 []string) error {
   483  	for _, v1 := range s1 {
   484  		for _, v2 := range s2 {
   485  			if v1 == v2 {
   486  				return fmt.Errorf("mux: duplicated route variable %q", v2)
   487  			}
   488  		}
   489  	}
   490  	return nil
   491  }
   492  
   493  // checkPairs returns the count of strings passed in, and an error if
   494  // the count is not an even number.
   495  func checkPairs(pairs ...string) (int, error) {
   496  	length := len(pairs)
   497  	if length%2 != 0 {
   498  		return length, fmt.Errorf(
   499  			"mux: number of parameters must be multiple of 2, got %v", pairs)
   500  	}
   501  	return length, nil
   502  }
   503  
   504  // mapFromPairsToString converts variadic string parameters to a
   505  // string to string map.
   506  func mapFromPairsToString(pairs ...string) (map[string]string, error) {
   507  	length, err := checkPairs(pairs...)
   508  	if err != nil {
   509  		return nil, err
   510  	}
   511  	m := make(map[string]string, length/2)
   512  	for i := 0; i < length; i += 2 {
   513  		m[pairs[i]] = pairs[i+1]
   514  	}
   515  	return m, nil
   516  }
   517  
   518  // mapFromPairsToRegex converts variadic string parameters to a
   519  // string to regex map.
   520  func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) {
   521  	length, err := checkPairs(pairs...)
   522  	if err != nil {
   523  		return nil, err
   524  	}
   525  	m := make(map[string]*regexp.Regexp, length/2)
   526  	for i := 0; i < length; i += 2 {
   527  		regex, err := regexp.Compile(pairs[i+1])
   528  		if err != nil {
   529  			return nil, err
   530  		}
   531  		m[pairs[i]] = regex
   532  	}
   533  	return m, nil
   534  }
   535  
   536  // matchInArray returns true if the given string value is in the array.
   537  func matchInArray(arr []string, value string) bool {
   538  	for _, v := range arr {
   539  		if v == value {
   540  			return true
   541  		}
   542  	}
   543  	return false
   544  }
   545  
   546  // matchMapWithString returns true if the given key/value pairs exist in a given map.
   547  func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool {
   548  	for k, v := range toCheck {
   549  		// Check if key exists.
   550  		if canonicalKey {
   551  			k = http.CanonicalHeaderKey(k)
   552  		}
   553  		if values := toMatch[k]; values == nil {
   554  			return false
   555  		} else if v != "" {
   556  			// If value was defined as an empty string we only check that the
   557  			// key exists. Otherwise we also check for equality.
   558  			valueExists := false
   559  			for _, value := range values {
   560  				if v == value {
   561  					valueExists = true
   562  					break
   563  				}
   564  			}
   565  			if !valueExists {
   566  				return false
   567  			}
   568  		}
   569  	}
   570  	return true
   571  }
   572  
   573  // matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against
   574  // the given regex
   575  func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool {
   576  	for k, v := range toCheck {
   577  		// Check if key exists.
   578  		if canonicalKey {
   579  			k = http.CanonicalHeaderKey(k)
   580  		}
   581  		if values := toMatch[k]; values == nil {
   582  			return false
   583  		} else if v != nil {
   584  			// If value was defined as an empty string we only check that the
   585  			// key exists. Otherwise we also check for equality.
   586  			valueExists := false
   587  			for _, value := range values {
   588  				if v.MatchString(value) {
   589  					valueExists = true
   590  					break
   591  				}
   592  			}
   593  			if !valueExists {
   594  				return false
   595  			}
   596  		}
   597  	}
   598  	return true
   599  }
   600  
   601  // methodNotAllowed replies to the request with an HTTP status code 405.
   602  func methodNotAllowed(w http.ResponseWriter, r *http.Request) {
   603  	w.WriteHeader(http.StatusMethodNotAllowed)
   604  }
   605  
   606  // methodNotAllowedHandler returns a simple request handler
   607  // that replies to each request with a status code 405.
   608  func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) }
   609  

View as plain text