gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/mux/mux.go (about)

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