// Package alice provides a convenient way to chain http handlers. package alice import "net/http" // Constructor 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, error) // 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{constructors: 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, error) { if h == nil { h = http.DefaultServeMux } for i := range c.constructors { handler, err := c.constructors[len(c.constructors)-1-i](h) if err != nil { return nil, err } h = handler } return h, nil } // 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, error) { 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...) }