github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/go-chi/chi/mux.go (about)

     1  package chi
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/hellobchain/newcryptosm/http"
    10  )
    11  
    12  var _ Router = &Mux{}
    13  
    14  // Mux is a simple HTTP route multiplexer that parses a request path,
    15  // records any URL params, and executes an end handler. It implements
    16  // the http.Handler interface and is friendly with the standard library.
    17  //
    18  // Mux is designed to be fast, minimal and offer a powerful API for building
    19  // modular and composable HTTP services with a large set of handlers. It's
    20  // particularly useful for writing large REST API services that break a handler
    21  // into many smaller parts composed of middlewares and end handlers.
    22  type Mux struct {
    23  	// The radix trie router
    24  	tree *node
    25  
    26  	// The middleware stack
    27  	middlewares []func(http.Handler) http.Handler
    28  
    29  	// Controls the behaviour of middleware chain generation when a mux
    30  	// is registered as an inline group inside another mux.
    31  	inline bool
    32  	parent *Mux
    33  
    34  	// The computed mux handler made of the chained middleware stack and
    35  	// the tree router
    36  	handler http.Handler
    37  
    38  	// Routing context pool
    39  	pool *sync.Pool
    40  
    41  	// Custom route not found handler
    42  	notFoundHandler http.HandlerFunc
    43  
    44  	// Custom method not allowed handler
    45  	methodNotAllowedHandler http.HandlerFunc
    46  }
    47  
    48  // NewMux returns a newly initialized Mux object that implements the Router
    49  // interface.
    50  func NewMux() *Mux {
    51  	mux := &Mux{tree: &node{}, pool: &sync.Pool{}}
    52  	mux.pool.New = func() interface{} {
    53  		return NewRouteContext()
    54  	}
    55  	return mux
    56  }
    57  
    58  // ServeHTTP is the single method of the http.Handler interface that makes
    59  // Mux interoperable with the standard library. It uses a sync.Pool to get and
    60  // reuse routing contexts for each request.
    61  func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    62  	// Ensure the mux has some routes defined on the mux
    63  	if mx.handler == nil {
    64  		panic("chi: attempting to route to a mux with no handlers.")
    65  	}
    66  
    67  	// Check if a routing context already exists from a parent router.
    68  	rctx, _ := r.Context().Value(RouteCtxKey).(*Context)
    69  	if rctx != nil {
    70  		mx.handler.ServeHTTP(w, r)
    71  		return
    72  	}
    73  
    74  	// Fetch a RouteContext object from the sync pool, and call the computed
    75  	// mx.handler that is comprised of mx.middlewares + mx.routeHTTP.
    76  	// Once the request is finished, reset the routing context and put it back
    77  	// into the pool for reuse from another request.
    78  	rctx = mx.pool.Get().(*Context)
    79  	rctx.Reset()
    80  	rctx.Routes = mx
    81  	r = r.WithContext(context.WithValue(r.Context(), RouteCtxKey, rctx))
    82  	mx.handler.ServeHTTP(w, r)
    83  	mx.pool.Put(rctx)
    84  }
    85  
    86  // Use appends a middleware handler to the Mux middleware stack.
    87  //
    88  // The middleware stack for any Mux will execute before searching for a matching
    89  // route to a specific handler, which provides opportunity to respond early,
    90  // change the course of the request execution, or set request-scoped values for
    91  // the next http.Handler.
    92  func (mx *Mux) Use(middlewares ...func(http.Handler) http.Handler) {
    93  	if mx.handler != nil {
    94  		panic("chi: all middlewares must be defined before routes on a mux")
    95  	}
    96  	mx.middlewares = append(mx.middlewares, middlewares...)
    97  }
    98  
    99  // Handle adds the route `pattern` that matches any http method to
   100  // execute the `handler` http.Handler.
   101  func (mx *Mux) Handle(pattern string, handler http.Handler) {
   102  	mx.handle(mALL, pattern, handler)
   103  }
   104  
   105  // HandleFunc adds the route `pattern` that matches any http method to
   106  // execute the `handlerFn` http.HandlerFunc.
   107  func (mx *Mux) HandleFunc(pattern string, handlerFn http.HandlerFunc) {
   108  	mx.handle(mALL, pattern, handlerFn)
   109  }
   110  
   111  // Method adds the route `pattern` that matches `method` http method to
   112  // execute the `handler` http.Handler.
   113  func (mx *Mux) Method(method, pattern string, handler http.Handler) {
   114  	m, ok := methodMap[strings.ToUpper(method)]
   115  	if !ok {
   116  		panic(fmt.Sprintf("chi: '%s' http method is not supported.", method))
   117  	}
   118  	mx.handle(m, pattern, handler)
   119  }
   120  
   121  // MethodFunc adds the route `pattern` that matches `method` http method to
   122  // execute the `handlerFn` http.HandlerFunc.
   123  func (mx *Mux) MethodFunc(method, pattern string, handlerFn http.HandlerFunc) {
   124  	mx.Method(method, pattern, handlerFn)
   125  }
   126  
   127  // Connect adds the route `pattern` that matches a CONNECT http method to
   128  // execute the `handlerFn` http.HandlerFunc.
   129  func (mx *Mux) Connect(pattern string, handlerFn http.HandlerFunc) {
   130  	mx.handle(mCONNECT, pattern, handlerFn)
   131  }
   132  
   133  // Delete adds the route `pattern` that matches a DELETE http method to
   134  // execute the `handlerFn` http.HandlerFunc.
   135  func (mx *Mux) Delete(pattern string, handlerFn http.HandlerFunc) {
   136  	mx.handle(mDELETE, pattern, handlerFn)
   137  }
   138  
   139  // Get adds the route `pattern` that matches a GET http method to
   140  // execute the `handlerFn` http.HandlerFunc.
   141  func (mx *Mux) Get(pattern string, handlerFn http.HandlerFunc) {
   142  	mx.handle(mGET, pattern, handlerFn)
   143  }
   144  
   145  // Head adds the route `pattern` that matches a HEAD http method to
   146  // execute the `handlerFn` http.HandlerFunc.
   147  func (mx *Mux) Head(pattern string, handlerFn http.HandlerFunc) {
   148  	mx.handle(mHEAD, pattern, handlerFn)
   149  }
   150  
   151  // Options adds the route `pattern` that matches a OPTIONS http method to
   152  // execute the `handlerFn` http.HandlerFunc.
   153  func (mx *Mux) Options(pattern string, handlerFn http.HandlerFunc) {
   154  	mx.handle(mOPTIONS, pattern, handlerFn)
   155  }
   156  
   157  // Patch adds the route `pattern` that matches a PATCH http method to
   158  // execute the `handlerFn` http.HandlerFunc.
   159  func (mx *Mux) Patch(pattern string, handlerFn http.HandlerFunc) {
   160  	mx.handle(mPATCH, pattern, handlerFn)
   161  }
   162  
   163  // Post adds the route `pattern` that matches a POST http method to
   164  // execute the `handlerFn` http.HandlerFunc.
   165  func (mx *Mux) Post(pattern string, handlerFn http.HandlerFunc) {
   166  	mx.handle(mPOST, pattern, handlerFn)
   167  }
   168  
   169  // Put adds the route `pattern` that matches a PUT http method to
   170  // execute the `handlerFn` http.HandlerFunc.
   171  func (mx *Mux) Put(pattern string, handlerFn http.HandlerFunc) {
   172  	mx.handle(mPUT, pattern, handlerFn)
   173  }
   174  
   175  // Trace adds the route `pattern` that matches a TRACE http method to
   176  // execute the `handlerFn` http.HandlerFunc.
   177  func (mx *Mux) Trace(pattern string, handlerFn http.HandlerFunc) {
   178  	mx.handle(mTRACE, pattern, handlerFn)
   179  }
   180  
   181  // NotFound sets a custom http.HandlerFunc for routing paths that could
   182  // not be found. The default 404 handler is `http.NotFound`.
   183  func (mx *Mux) NotFound(handlerFn http.HandlerFunc) {
   184  	// Build NotFound handler chain
   185  	m := mx
   186  	hFn := handlerFn
   187  	if mx.inline && mx.parent != nil {
   188  		m = mx.parent
   189  		hFn = Chain(mx.middlewares...).HandlerFunc(hFn).ServeHTTP
   190  	}
   191  
   192  	// Update the notFoundHandler from this point forward
   193  	m.notFoundHandler = hFn
   194  	m.updateSubRoutes(func(subMux *Mux) {
   195  		if subMux.notFoundHandler == nil {
   196  			subMux.NotFound(hFn)
   197  		}
   198  	})
   199  }
   200  
   201  // MethodNotAllowed sets a custom http.HandlerFunc for routing paths where the
   202  // method is unresolved. The default handler returns a 405 with an empty body.
   203  func (mx *Mux) MethodNotAllowed(handlerFn http.HandlerFunc) {
   204  	// Build MethodNotAllowed handler chain
   205  	m := mx
   206  	hFn := handlerFn
   207  	if mx.inline && mx.parent != nil {
   208  		m = mx.parent
   209  		hFn = Chain(mx.middlewares...).HandlerFunc(hFn).ServeHTTP
   210  	}
   211  
   212  	// Update the methodNotAllowedHandler from this point forward
   213  	m.methodNotAllowedHandler = hFn
   214  	m.updateSubRoutes(func(subMux *Mux) {
   215  		if subMux.methodNotAllowedHandler == nil {
   216  			subMux.MethodNotAllowed(hFn)
   217  		}
   218  	})
   219  }
   220  
   221  // With adds inline middlewares for an endpoint handler.
   222  func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router {
   223  	// Similarly as in handle(), we must build the mux handler once further
   224  	// middleware registration isn't allowed for this stack, like now.
   225  	if !mx.inline && mx.handler == nil {
   226  		mx.buildRouteHandler()
   227  	}
   228  
   229  	// Copy middlewares from parent inline muxs
   230  	var mws Middlewares
   231  	if mx.inline {
   232  		mws = make(Middlewares, len(mx.middlewares))
   233  		copy(mws, mx.middlewares)
   234  	}
   235  	mws = append(mws, middlewares...)
   236  
   237  	im := &Mux{pool: mx.pool, inline: true, parent: mx, tree: mx.tree, middlewares: mws}
   238  
   239  	return im
   240  }
   241  
   242  // Group creates a new inline-Mux with a fresh middleware stack. It's useful
   243  // for a group of handlers along the same routing path that use an additional
   244  // set of middlewares. See _examples/.
   245  func (mx *Mux) Group(fn func(r Router)) Router {
   246  	im := mx.With().(*Mux)
   247  	if fn != nil {
   248  		fn(im)
   249  	}
   250  	return im
   251  }
   252  
   253  // Route creates a new Mux with a fresh middleware stack and mounts it
   254  // along the `pattern` as a subrouter. Effectively, this is a short-hand
   255  // call to Mount. See _examples/.
   256  func (mx *Mux) Route(pattern string, fn func(r Router)) Router {
   257  	subRouter := NewRouter()
   258  	if fn != nil {
   259  		fn(subRouter)
   260  	}
   261  	mx.Mount(pattern, subRouter)
   262  	return subRouter
   263  }
   264  
   265  // Mount attaches another http.Handler or chi Router as a subrouter along a routing
   266  // path. It's very useful to split up a large API as many independent routers and
   267  // compose them as a single service using Mount. See _examples/.
   268  //
   269  // Note that Mount() simply sets a wildcard along the `pattern` that will continue
   270  // routing at the `handler`, which in most cases is another chi.Router. As a result,
   271  // if you define two Mount() routes on the exact same pattern the mount will panic.
   272  func (mx *Mux) Mount(pattern string, handler http.Handler) {
   273  	// Provide runtime safety for ensuring a pattern isn't mounted on an existing
   274  	// routing pattern.
   275  	if mx.tree.findPattern(pattern+"*") || mx.tree.findPattern(pattern+"/*") {
   276  		panic(fmt.Sprintf("chi: attempting to Mount() a handler on an existing path, '%s'", pattern))
   277  	}
   278  
   279  	// Assign sub-Router's with the parent not found & method not allowed handler if not specified.
   280  	subr, ok := handler.(*Mux)
   281  	if ok && subr.notFoundHandler == nil && mx.notFoundHandler != nil {
   282  		subr.NotFound(mx.notFoundHandler)
   283  	}
   284  	if ok && subr.methodNotAllowedHandler == nil && mx.methodNotAllowedHandler != nil {
   285  		subr.MethodNotAllowed(mx.methodNotAllowedHandler)
   286  	}
   287  
   288  	// Wrap the sub-router in a handlerFunc to scope the request path for routing.
   289  	mountHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   290  		rctx := RouteContext(r.Context())
   291  		rctx.RoutePath = mx.nextRoutePath(rctx)
   292  		handler.ServeHTTP(w, r)
   293  	})
   294  
   295  	if pattern == "" || pattern[len(pattern)-1] != '/' {
   296  		mx.handle(mALL|mSTUB, pattern, mountHandler)
   297  		mx.handle(mALL|mSTUB, pattern+"/", mountHandler)
   298  		pattern += "/"
   299  	}
   300  
   301  	method := mALL
   302  	subroutes, _ := handler.(Routes)
   303  	if subroutes != nil {
   304  		method |= mSTUB
   305  	}
   306  	n := mx.handle(method, pattern+"*", mountHandler)
   307  
   308  	if subroutes != nil {
   309  		n.subroutes = subroutes
   310  	}
   311  }
   312  
   313  // Routes returns a slice of routing information from the tree,
   314  // useful for traversing available routes of a router.
   315  func (mx *Mux) Routes() []Route {
   316  	return mx.tree.routes()
   317  }
   318  
   319  // Middlewares returns a slice of middleware handler functions.
   320  func (mx *Mux) Middlewares() Middlewares {
   321  	return mx.middlewares
   322  }
   323  
   324  // Match searches the routing tree for a handler that matches the method/path.
   325  // It's similar to routing a http request, but without executing the handler
   326  // thereafter.
   327  //
   328  // Note: the *Context state is updated during execution, so manage
   329  // the state carefully or make a NewRouteContext().
   330  func (mx *Mux) Match(rctx *Context, method, path string) bool {
   331  	m, ok := methodMap[method]
   332  	if !ok {
   333  		return false
   334  	}
   335  
   336  	node, _, h := mx.tree.FindRoute(rctx, m, path)
   337  
   338  	if node != nil && node.subroutes != nil {
   339  		rctx.RoutePath = mx.nextRoutePath(rctx)
   340  		return node.subroutes.Match(rctx, method, rctx.RoutePath)
   341  	}
   342  
   343  	return h != nil
   344  }
   345  
   346  // NotFoundHandler returns the default Mux 404 responder whenever a route
   347  // cannot be found.
   348  func (mx *Mux) NotFoundHandler() http.HandlerFunc {
   349  	if mx.notFoundHandler != nil {
   350  		return mx.notFoundHandler
   351  	}
   352  	return http.NotFound
   353  }
   354  
   355  // MethodNotAllowedHandler returns the default Mux 405 responder whenever
   356  // a method cannot be resolved for a route.
   357  func (mx *Mux) MethodNotAllowedHandler() http.HandlerFunc {
   358  	if mx.methodNotAllowedHandler != nil {
   359  		return mx.methodNotAllowedHandler
   360  	}
   361  	return methodNotAllowedHandler
   362  }
   363  
   364  // buildRouteHandler builds the single mux handler that is a chain of the middleware
   365  // stack, as defined by calls to Use(), and the tree router (Mux) itself. After this
   366  // point, no other middlewares can be registered on this Mux's stack. But you can still
   367  // compose additional middlewares via Group()'s or using a chained middleware handler.
   368  func (mx *Mux) buildRouteHandler() {
   369  	mx.handler = chain(mx.middlewares, http.HandlerFunc(mx.routeHTTP))
   370  }
   371  
   372  // handle registers a http.Handler in the routing tree for a particular http method
   373  // and routing pattern.
   374  func (mx *Mux) handle(method methodTyp, pattern string, handler http.Handler) *node {
   375  	if len(pattern) == 0 || pattern[0] != '/' {
   376  		panic(fmt.Sprintf("chi: routing pattern must begin with '/' in '%s'", pattern))
   377  	}
   378  
   379  	// Build the final routing handler for this Mux.
   380  	if !mx.inline && mx.handler == nil {
   381  		mx.buildRouteHandler()
   382  	}
   383  
   384  	// Build endpoint handler with inline middlewares for the route
   385  	var h http.Handler
   386  	if mx.inline {
   387  		mx.handler = http.HandlerFunc(mx.routeHTTP)
   388  		h = Chain(mx.middlewares...).Handler(handler)
   389  	} else {
   390  		h = handler
   391  	}
   392  
   393  	// Add the endpoint to the tree and return the node
   394  	return mx.tree.InsertRoute(method, pattern, h)
   395  }
   396  
   397  // routeHTTP routes a http.Request through the Mux routing tree to serve
   398  // the matching handler for a particular http method.
   399  func (mx *Mux) routeHTTP(w http.ResponseWriter, r *http.Request) {
   400  	// Grab the route context object
   401  	rctx := r.Context().Value(RouteCtxKey).(*Context)
   402  
   403  	// The request routing path
   404  	routePath := rctx.RoutePath
   405  	if routePath == "" {
   406  		if r.URL.RawPath != "" {
   407  			routePath = r.URL.RawPath
   408  		} else {
   409  			routePath = r.URL.Path
   410  		}
   411  	}
   412  
   413  	// Check if method is supported by chi
   414  	if rctx.RouteMethod == "" {
   415  		rctx.RouteMethod = r.Method
   416  	}
   417  	method, ok := methodMap[rctx.RouteMethod]
   418  	if !ok {
   419  		mx.MethodNotAllowedHandler().ServeHTTP(w, r)
   420  		return
   421  	}
   422  
   423  	// Find the route
   424  	if _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil {
   425  		h.ServeHTTP(w, r)
   426  		return
   427  	}
   428  	if rctx.methodNotAllowed {
   429  		mx.MethodNotAllowedHandler().ServeHTTP(w, r)
   430  	} else {
   431  		mx.NotFoundHandler().ServeHTTP(w, r)
   432  	}
   433  }
   434  
   435  func (mx *Mux) nextRoutePath(rctx *Context) string {
   436  	routePath := "/"
   437  	nx := len(rctx.routeParams.Keys) - 1 // index of last param in list
   438  	if nx >= 0 && rctx.routeParams.Keys[nx] == "*" && len(rctx.routeParams.Values) > nx {
   439  		routePath += rctx.routeParams.Values[nx]
   440  	}
   441  	return routePath
   442  }
   443  
   444  // Recursively update data on child routers.
   445  func (mx *Mux) updateSubRoutes(fn func(subMux *Mux)) {
   446  	for _, r := range mx.tree.routes() {
   447  		subMux, ok := r.SubRoutes.(*Mux)
   448  		if !ok {
   449  			continue
   450  		}
   451  		fn(subMux)
   452  	}
   453  }
   454  
   455  // methodNotAllowedHandler is a helper function to respond with a 405,
   456  // method not allowed.
   457  func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) {
   458  	w.WriteHeader(405)
   459  	w.Write(nil)
   460  }