No Description
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 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. package phi
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strings"
  6. "sync"
  7. )
  8. var _ Router = (*Mux)(nil)
  9. // Mux is a simple HTTP route multiplexer that parses a request path,
  10. // records any URL params, and executes an end handler. It implements
  11. // the http.Handler interface and is friendly with the standard library.
  12. //
  13. // Mux is designed to be fast, minimal and offer a powerful API for building
  14. // modular and composable HTTP services with a large set of handlers. It's
  15. // particularly useful for writing large REST API services that break a handler
  16. // into many smaller parts composed of middlewares and end handlers.
  17. type Mux struct {
  18. // The radix trie router
  19. tree *node
  20. // The middleware stack
  21. middlewares []Middleware
  22. // Controls the behaviour of middleware chain generation when a mux
  23. // is registered as an inline group inside another mux.
  24. inline bool
  25. // The computed mux handler made of the chained middleware stack and
  26. // the tree router
  27. handler Handler
  28. // Routing context pool
  29. pool sync.Pool
  30. // Custom route not found handler
  31. notFoundHandler HandlerFunc
  32. }
  33. // NewMux returns a newly initialized Mux object that implements the Router
  34. // interface.
  35. func NewMux() *Mux {
  36. mux := &Mux{tree: &node{}}
  37. mux.pool.New = func() interface{} {
  38. return NewRoutingContext()
  39. }
  40. return mux
  41. }
  42. // Serve is the single method of the Handler interface.
  43. func (mx *Mux) Serve(ctx Context) {
  44. // Ensure the mux has some routes defined on the mux
  45. if mx.handler == nil {
  46. panic("phi: attempting to route to a mux with no handlers.")
  47. }
  48. mx.handler.Serve(ctx)
  49. }
  50. // ServeHTTP is the single method of the http.Handler interface that makes
  51. // Mux interoperable with the standard library. It uses a sync.Pool to get and
  52. // reuse routing contexts for each request.
  53. func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  54. // Ensure the mux has some routes defined on the mux
  55. if mx.handler == nil {
  56. panic("phi: attempting to route to a mux with no handlers.")
  57. }
  58. // Check if a routing context already exists from a parent router.
  59. ctx, _ := r.Context().Value(RouteCtxKey).(*RoutingContext)
  60. if ctx != nil {
  61. ctx.ResponseWriter = w
  62. ctx.request = r
  63. mx.Serve(ctx)
  64. return
  65. }
  66. // Fetch a RouteContext object from the sync pool, and call the computed
  67. // mx.handler that is comprised of mx.middlewares + mx.routeHTTP.
  68. // Once the request is finished, reset the routing context and put it back
  69. // into the pool for reuse from another request.
  70. ctx = mx.pool.Get().(*RoutingContext)
  71. ctx.reset(w, r)
  72. mx.handler.Serve(ctx)
  73. mx.pool.Put(ctx)
  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 ...Middleware) {
  82. if mx.handler != nil {
  83. panic("phi: 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` Handler.
  89. func (mx *Mux) Handle(pattern string, handler Handler) {
  90. mx.handle(mALL, pattern, handler)
  91. }
  92. // HandleFunc adds the route `pattern` that matches any http method to
  93. // execute the `handlerFn` HandlerFunc.
  94. func (mx *Mux) HandleFunc(pattern string, handlerFn HandlerFunc) {
  95. mx.handle(mALL, pattern, handlerFn)
  96. }
  97. // HTTPHandle adds the route `pattern` that matches any http method to
  98. // execute the `handler` http.Handler.
  99. func (mx *Mux) HTTPHandle(pattern string, handler http.Handler) {
  100. mx.handle(mALL, pattern, HandlerFunc(func(ctx Context) { handler.ServeHTTP(ctx, ctx.Request()) }))
  101. }
  102. // HTTPHandleFunc adds the route `pattern` that matches any http method to
  103. // execute the `handlerFn` http.HandlerFunc.
  104. func (mx *Mux) HTTPHandleFunc(pattern string, handlerFn http.HandlerFunc) {
  105. mx.handle(mALL, pattern, HandlerFunc(func(ctx Context) { handlerFn.ServeHTTP(ctx, ctx.Request()) }))
  106. }
  107. // Connect adds the route `pattern` that matches a CONNECT http method to
  108. // execute the `handlerFn` http.HandlerFunc.
  109. func (mx *Mux) Connect(pattern string, handlerFn HandlerFunc) {
  110. mx.handle(mCONNECT, pattern, handlerFn)
  111. }
  112. // Delete adds the route `pattern` that matches a DELETE http method to
  113. // execute the `handlerFn` http.HandlerFunc.
  114. func (mx *Mux) Delete(pattern string, handlerFn HandlerFunc) {
  115. mx.handle(mDELETE, pattern, handlerFn)
  116. }
  117. // Get adds the route `pattern` that matches a GET http method to
  118. // execute the `handlerFn` http.HandlerFunc.
  119. func (mx *Mux) Get(pattern string, handlerFn HandlerFunc) {
  120. mx.handle(mGET, pattern, handlerFn)
  121. }
  122. // Head adds the route `pattern` that matches a HEAD http method to
  123. // execute the `handlerFn` http.HandlerFunc.
  124. func (mx *Mux) Head(pattern string, handlerFn HandlerFunc) {
  125. mx.handle(mHEAD, pattern, handlerFn)
  126. }
  127. // Options adds the route `pattern` that matches a OPTIONS http method to
  128. // execute the `handlerFn` http.HandlerFunc.
  129. func (mx *Mux) Options(pattern string, handlerFn HandlerFunc) {
  130. mx.handle(mOPTIONS, pattern, handlerFn)
  131. }
  132. // Patch adds the route `pattern` that matches a PATCH http method to
  133. // execute the `handlerFn` http.HandlerFunc.
  134. func (mx *Mux) Patch(pattern string, handlerFn HandlerFunc) {
  135. mx.handle(mPATCH, pattern, handlerFn)
  136. }
  137. // Post adds the route `pattern` that matches a POST http method to
  138. // execute the `handlerFn` http.HandlerFunc.
  139. func (mx *Mux) Post(pattern string, handlerFn HandlerFunc) {
  140. mx.handle(mPOST, pattern, handlerFn)
  141. }
  142. // Put adds the route `pattern` that matches a PUT http method to
  143. // execute the `handlerFn` http.HandlerFunc.
  144. func (mx *Mux) Put(pattern string, handlerFn HandlerFunc) {
  145. mx.handle(mPUT, pattern, handlerFn)
  146. }
  147. // Trace adds the route `pattern` that matches a TRACE http method to
  148. // execute the `handlerFn` http.HandlerFunc.
  149. func (mx *Mux) Trace(pattern string, handlerFn HandlerFunc) {
  150. mx.handle(mTRACE, pattern, handlerFn)
  151. }
  152. // NotFound sets a custom http.HandlerFunc for routing paths that could
  153. // not be found. The default 404 handler is `http.NotFound`.
  154. func (mx *Mux) NotFound(handlerFn HandlerFunc) {
  155. mx.notFoundHandler = handlerFn
  156. }
  157. // With adds inline middlewares for an endpoint handler.
  158. func (mx *Mux) With(middlewares ...Middleware) Router {
  159. // Similarly as in handle(), we must build the mux handler once further
  160. // middleware registration isn't allowed for this stack, like now.
  161. if !mx.inline && mx.handler == nil {
  162. mx.buildRouteHandler()
  163. }
  164. // Copy middlewares from parent inline muxs
  165. var mws Middlewares
  166. if mx.inline {
  167. mws = make(Middlewares, len(mx.middlewares))
  168. copy(mws, mx.middlewares)
  169. }
  170. mws = append(mws, middlewares...)
  171. im := &Mux{inline: true, tree: mx.tree, middlewares: mws}
  172. return im
  173. }
  174. // Group creates a new inline-Mux with a fresh middleware stack. It's useful
  175. // for a group of handlers along the same routing path that use an additional
  176. // set of middlewares. See _examples/.
  177. func (mx *Mux) Group(fn func(r Router)) Router {
  178. im := mx.With().(*Mux)
  179. if fn != nil {
  180. fn(im)
  181. }
  182. return im
  183. }
  184. // Route creates a new Mux with a fresh middleware stack and mounts it
  185. // along the `pattern` as a subrouter. Effectively, this is a short-hand
  186. // call to Mount. See _examples/.
  187. func (mx *Mux) Route(pattern string, fn func(r Router)) Router {
  188. subRouter := NewRouter()
  189. if fn != nil {
  190. fn(subRouter)
  191. }
  192. mx.Mount(pattern, subRouter)
  193. return subRouter
  194. }
  195. // Mount attaches another http.Handler or phi Router as a subrouter along a routing
  196. // path. It's very useful to split up a large API as many independent routers and
  197. // compose them as a single service using Mount. See _examples/.
  198. //
  199. // Note that Mount() simply sets a wildcard along the `pattern` that will continue
  200. // routing at the `handler`, which in most cases is another phi.Router. As a result,
  201. // if you define two Mount() routes on the exact same pattern the mount will panic.
  202. func (mx *Mux) Mount(pattern string, handler Handler) {
  203. // Provide runtime safety for ensuring a pattern isn't mounted on an existing
  204. // routing pattern.
  205. if mx.tree.findPattern(pattern+"*") != nil || mx.tree.findPattern(pattern+"/*") != nil {
  206. panic(fmt.Sprintf("phi: attempting to Mount() a handler on an existing path, '%s'", pattern))
  207. }
  208. // Assign sub-Router's with the parent not found handler if not specified.
  209. subr, ok := handler.(*Mux)
  210. if ok && subr.notFoundHandler == nil && mx.notFoundHandler != nil {
  211. subr.NotFound(mx.notFoundHandler)
  212. }
  213. // Wrap the sub-router in a handlerFunc to scope the request path for routing.
  214. subHandler := HandlerFunc(func(ctx Context) {
  215. rctx := ctx.Value(RouteCtxKey).(*RoutingContext)
  216. rctx.routePath = "/" + rctx.URLParams.Del("*")
  217. handler.Serve(rctx)
  218. })
  219. if pattern == "" || pattern[len(pattern)-1] != '/' {
  220. mx.handle(mALL|mSTUB, pattern, subHandler)
  221. mx.handle(mALL|mSTUB, pattern+"/", mx.NotFoundHandler())
  222. pattern += "/"
  223. }
  224. method := mALL
  225. subroutes, _ := handler.(Routes)
  226. if subroutes != nil {
  227. method |= mSTUB
  228. }
  229. n := mx.handle(method, pattern+"*", subHandler)
  230. if subroutes != nil {
  231. n.subroutes = subroutes
  232. }
  233. }
  234. // Middlewares returns all installed middlewares.
  235. func (mx *Mux) Middlewares() Middlewares {
  236. return mx.middlewares
  237. }
  238. // Routes returns all installed routes.
  239. func (mx *Mux) Routes() []Route {
  240. return mx.tree.routes()
  241. }
  242. // FileServer conveniently sets up a http.FileServer handler to serve
  243. // static files from a http.FileSystem.
  244. func (mx *Mux) FileServer(path string, root http.FileSystem) {
  245. if strings.ContainsAny(path, ":*") {
  246. panic("phi: FileServer does not permit URL parameters.")
  247. }
  248. fs := http.StripPrefix(path, http.FileServer(root))
  249. if path != "/" && path[len(path)-1] != '/' {
  250. mx.Get(path, func(ctx Context) {
  251. ctx.Redirect(path+"/", http.StatusMovedPermanently)
  252. })
  253. path += "/"
  254. }
  255. path += "*"
  256. mx.Get(path, func(ctx Context) {
  257. fs.ServeHTTP(ctx, ctx.Request())
  258. })
  259. }
  260. // NotFoundHandler returns the default Mux 404 responder whenever a route
  261. // cannot be found.
  262. func (mx *Mux) NotFoundHandler() HandlerFunc {
  263. if mx.notFoundHandler != nil {
  264. return mx.notFoundHandler
  265. }
  266. return func(ctx Context) {
  267. ctx.Error(nil, 404)
  268. }
  269. }
  270. // buildRouteHandler builds the single mux handler that is a chain of the middleware
  271. // stack, as defined by calls to Use(), and the tree router (Mux) itself. After this
  272. // point, no other middlewares can be registered on this Mux's stack. But you can still
  273. // compose additional middlewares via Group()'s or using a chained middleware handler.
  274. func (mx *Mux) buildRouteHandler() {
  275. mx.handler = chain(mx.middlewares, HandlerFunc(mx.routeContext))
  276. }
  277. // handle registers a http.Handler in the routing tree for a particular http method
  278. // and routing pattern.
  279. func (mx *Mux) handle(method methodTyp, pattern string, handler Handler) *node {
  280. if len(pattern) == 0 || pattern[0] != '/' {
  281. panic(fmt.Sprintf("phi: routing pattern must begin with '/' in '%s'", pattern))
  282. }
  283. // Build the final routing handler for this Mux.
  284. if !mx.inline && mx.handler == nil {
  285. mx.buildRouteHandler()
  286. }
  287. // Build endpoint handler with inline middlewares for the route
  288. var h Handler
  289. if mx.inline {
  290. mx.handler = HandlerFunc(mx.routeContext)
  291. h = Chain(mx.middlewares...).Handler(handler)
  292. } else {
  293. h = handler
  294. }
  295. // Add the endpoint to the tree and return the node
  296. return mx.tree.InsertRoute(method, pattern, h)
  297. }
  298. // routeContext routes a *Context through the Mux routing tree to serve
  299. // the matching handler for a particular http method.
  300. func (mx *Mux) routeContext(ctx Context) {
  301. rctx, _ := ctx.(*RoutingContext)
  302. if rctx == nil {
  303. panic("RoutingContext is nil")
  304. }
  305. if rctx.request == nil {
  306. panic("RoutingContext.request is nil")
  307. }
  308. // The request routing path
  309. routePath := rctx.routePath
  310. if routePath == "" {
  311. routePath = rctx.request.URL.Path
  312. }
  313. // Check if method is supported by phi
  314. method, ok := methodMap[rctx.request.Method]
  315. if !ok {
  316. methodNotAllowedHandler(rctx)
  317. return
  318. }
  319. // Find the route
  320. hs := mx.tree.FindRoute(rctx, routePath)
  321. if hs == nil {
  322. mx.NotFoundHandler().Serve(ctx)
  323. return
  324. }
  325. h, ok := hs[method]
  326. if !ok {
  327. methodNotAllowedHandler(rctx)
  328. return
  329. }
  330. // Serve it up
  331. h.Serve(ctx)
  332. }
  333. // methodNotAllowedHandler is a helper function to respond with a 405,
  334. // method not allowed.
  335. func methodNotAllowedHandler(ctx Context) {
  336. ctx.WriteHeader(405)
  337. ctx.Write(nil)
  338. }