123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- package chain
- // This is a modified version of https://github.com/justinas/alice
- // The original code is licensed under the MIT license.
- // It's modified for couple reasons:
- // - Added the Chain interface
- // - Added support for the Chain.Prepend(...) method
- import "net/http"
- type (
- // Chain defines a chain of middleware.
- Chain interface {
- Append(middlewares ...Middleware) Chain
- Prepend(middlewares ...Middleware) Chain
- Then(h http.Handler) http.Handler
- ThenFunc(fn http.HandlerFunc) http.Handler
- }
- // Middleware is an HTTP middleware.
- Middleware func(http.Handler) http.Handler
- // chain acts as a list of http.Handler middlewares.
- // chain is effectively immutable:
- // once created, it will always hold
- // the same set of middlewares in the same order.
- chain struct {
- middlewares []Middleware
- }
- )
- // New creates a new Chain, memorizing the given list of middleware middlewares.
- // New serves no other function, middlewares are only called upon a call to Then() or ThenFunc().
- func New(middlewares ...Middleware) Chain {
- return chain{middlewares: append(([]Middleware)(nil), middlewares...)}
- }
- // Append extends a chain, adding the specified middlewares as the last ones in the request flow.
- //
- // c := chain.New(m1, m2)
- // c.Append(m3, m4)
- // // requests in c go m1 -> m2 -> m3 -> m4
- func (c chain) Append(middlewares ...Middleware) Chain {
- return chain{middlewares: join(c.middlewares, middlewares)}
- }
- // Prepend extends a chain by adding the specified chain as the first one in the request flow.
- //
- // c := chain.New(m3, m4)
- // c1 := chain.New(m1, m2)
- // c.Prepend(c1)
- // // requests in c go m1 -> m2 -> m3 -> m4
- func (c chain) Prepend(middlewares ...Middleware) Chain {
- return chain{middlewares: join(middlewares, c.middlewares)}
- }
- // Then chains the middleware and returns the final http.Handler.
- //
- // New(m1, m2, m3).Then(h)
- //
- // is equivalent to:
- //
- // m1(m2(m3(h)))
- //
- // When the request comes in, it will be passed to m1, then m2, then m3
- // and finally, the given handler
- // (assuming every middleware calls the following one).
- //
- // A chain can be safely reused by calling Then() several times.
- //
- // stdStack := chain.New(ratelimitHandler, csrfHandler)
- // indexPipe = stdStack.Then(indexHandler)
- // authPipe = stdStack.Then(authHandler)
- //
- // Note that middlewares are called on every call to Then() or ThenFunc()
- // and thus several instances of the same middleware will be created
- // when a chain is reused in this way.
- // For proper middleware, this should cause no problems.
- //
- // Then() treats nil as http.DefaultServeMux.
- func (c chain) Then(h http.Handler) http.Handler {
- if h == nil {
- h = http.DefaultServeMux
- }
- for i := range c.middlewares {
- h = c.middlewares[len(c.middlewares)-1-i](h)
- }
- return h
- }
- // ThenFunc works identically to Then, but takes
- // a HandlerFunc instead of a Handler.
- //
- // The following two statements are equivalent:
- //
- // c.Then(http.HandlerFunc(fn))
- // c.ThenFunc(fn)
- //
- // ThenFunc provides all the guarantees of Then.
- func (c chain) ThenFunc(fn http.HandlerFunc) http.Handler {
- // This nil check cannot be removed due to the "nil is not nil" common mistake in Go.
- // Required due to: https://stackoverflow.com/questions/33426977/how-to-golang-check-a-variable-is-nil
- if fn == nil {
- return c.Then(nil)
- }
- return c.Then(fn)
- }
- func join(a, b []Middleware) []Middleware {
- mids := make([]Middleware, 0, len(a)+len(b))
- mids = append(mids, a...)
- mids = append(mids, b...)
- return mids
- }
|