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 }