| // Package alice provides a convenient way to chain http handlers. |
| package alice |
| |
| import "net/http" |
| |
| // A constructor for a piece of middleware. |
| // Some middleware use this constructor out of the box, |
| // so in most cases you can just pass somepackage.New |
| type Constructor func(http.Handler) http.Handler |
| |
| // Chain acts as a list of http.Handler constructors. |
| // Chain is effectively immutable: |
| // once created, it will always hold |
| // the same set of constructors in the same order. |
| type Chain struct { |
| constructors []Constructor |
| } |
| |
| // New creates a new chain, |
| // memorizing the given list of middleware constructors. |
| // New serves no other function, |
| // constructors are only called upon a call to Then(). |
| func New(constructors ...Constructor) Chain { |
| return Chain{append(([]Constructor)(nil), constructors...)} |
| } |
| |
| // 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 := alice.New(ratelimitHandler, csrfHandler) |
| // indexPipe = stdStack.Then(indexHandler) |
| // authPipe = stdStack.Then(authHandler) |
| // Note that constructors are called on every call to Then() |
| // 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.constructors { |
| h = c.constructors[len(c.constructors)-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 { |
| if fn == nil { |
| return c.Then(nil) |
| } |
| return c.Then(fn) |
| } |
| |
| // Append extends a chain, adding the specified constructors |
| // as the last ones in the request flow. |
| // |
| // Append returns a new chain, leaving the original one untouched. |
| // |
| // stdChain := alice.New(m1, m2) |
| // extChain := stdChain.Append(m3, m4) |
| // // requests in stdChain go m1 -> m2 |
| // // requests in extChain go m1 -> m2 -> m3 -> m4 |
| func (c Chain) Append(constructors ...Constructor) Chain { |
| newCons := make([]Constructor, 0, len(c.constructors)+len(constructors)) |
| newCons = append(newCons, c.constructors...) |
| newCons = append(newCons, constructors...) |
| |
| return Chain{newCons} |
| } |
| |
| // Extend extends a chain by adding the specified chain |
| // as the last one in the request flow. |
| // |
| // Extend returns a new chain, leaving the original one untouched. |
| // |
| // stdChain := alice.New(m1, m2) |
| // ext1Chain := alice.New(m3, m4) |
| // ext2Chain := stdChain.Extend(ext1Chain) |
| // // requests in stdChain go m1 -> m2 |
| // // requests in ext1Chain go m3 -> m4 |
| // // requests in ext2Chain go m1 -> m2 -> m3 -> m4 |
| // |
| // Another example: |
| // aHtmlAfterNosurf := alice.New(m2) |
| // aHtml := alice.New(m1, func(h http.Handler) http.Handler { |
| // csrf := nosurf.New(h) |
| // csrf.SetFailureHandler(aHtmlAfterNosurf.ThenFunc(csrfFail)) |
| // return csrf |
| // }).Extend(aHtmlAfterNosurf) |
| // // requests to aHtml hitting nosurfs success handler go m1 -> nosurf -> m2 -> target-handler |
| // // requests to aHtml hitting nosurfs failure handler go m1 -> nosurf -> m2 -> csrfFail |
| func (c Chain) Extend(chain Chain) Chain { |
| return c.Append(chain.constructors...) |
| } |