You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mux.go 15KB

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