pax_global_header00006660000000000000000000000064131733077470014526gustar00rootroot0000000000000052 comment=03f45bd4b7dad4734bc4620e46a35789349abb20 golang-github-justinas-alice-0.0~git20171023.03f45bd/000077500000000000000000000000001317330774700216155ustar00rootroot00000000000000golang-github-justinas-alice-0.0~git20171023.03f45bd/.travis.yml000066400000000000000000000003571317330774700237330ustar00rootroot00000000000000language: go matrix: include: - go: 1.0.x - go: 1.1.x - go: 1.2.x - go: 1.3.x - go: 1.4.x - go: 1.5.x - go: 1.6.x - go: 1.7.x - go: 1.8.x - go: 1.9.x - go: tip allow_failures: - go: tip golang-github-justinas-alice-0.0~git20171023.03f45bd/LICENSE000066400000000000000000000021001317330774700226130ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Justinas Stankevicius Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-justinas-alice-0.0~git20171023.03f45bd/README.md000066400000000000000000000051111317330774700230720ustar00rootroot00000000000000# Alice [![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](http://godoc.org/github.com/justinas/alice) [![Build Status](https://travis-ci.org/justinas/alice.svg?branch=master)](https://travis-ci.org/justinas/alice) [![Coverage](http://gocover.io/_badge/github.com/justinas/alice)](http://gocover.io/github.com/justinas/alice) Alice provides a convenient way to chain your HTTP middleware functions and the app handler. In short, it transforms ```go Middleware1(Middleware2(Middleware3(App))) ``` to ```go alice.New(Middleware1, Middleware2, Middleware3).Then(App) ``` ### Why? None of the other middleware chaining solutions behaves exactly like Alice. Alice is as minimal as it gets: in essence, it's just a for loop that does the wrapping for you. Check out [this blog post](http://justinas.org/alice-painless-middleware-chaining-for-go/) for explanation how Alice is different from other chaining solutions. ### Usage Your middleware constructors should have the form of ```go func (http.Handler) http.Handler ``` Some middleware provide this out of the box. For ones that don't, it's trivial to write one yourself. ```go func myStripPrefix(h http.Handler) http.Handler { return http.StripPrefix("/old", h) } ``` This complete example shows the full power of Alice. ```go package main import ( "net/http" "time" "github.com/throttled/throttled" "github.com/justinas/alice" "github.com/justinas/nosurf" ) func timeoutHandler(h http.Handler) http.Handler { return http.TimeoutHandler(h, 1*time.Second, "timed out") } func myApp(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello world!")) } func main() { th := throttled.Interval(throttled.PerSec(10), 1, &throttled.VaryBy{Path: true}, 50) myHandler := http.HandlerFunc(myApp) chain := alice.New(th.Throttle, timeoutHandler, nosurf.NewPure).Then(myHandler) http.ListenAndServe(":8000", chain) } ``` Here, the request will pass [throttled](https://github.com/PuerkitoBio/throttled) first, then an http.TimeoutHandler we've set up, then [nosurf](https://github.com/justinas/nosurf) and will finally reach our handler. Note that Alice makes **no guarantees** for how one or another piece of middleware will behave. Once it passes the execution to the outer layer of middleware, it has no saying in whether middleware will execute the inner handlers. This is intentional behavior. Alice works with Go 1.0 and higher. ### Contributing 0. Find an issue that bugs you / open a new one. 1. Discuss. 2. Branch off, commit, test. 3. Make a pull request / attach the commits to the issue. golang-github-justinas-alice-0.0~git20171023.03f45bd/chain.go000066400000000000000000000072531317330774700232350ustar00rootroot00000000000000// 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...) } golang-github-justinas-alice-0.0~git20171023.03f45bd/chain_test.go000066400000000000000000000105441317330774700242710ustar00rootroot00000000000000// Package alice implements a middleware chaining solution. package alice import ( "net/http" "net/http/httptest" "reflect" "testing" ) // A constructor for middleware // that writes its own "tag" into the RW and does nothing else. // Useful in checking if a chain is behaving in the right order. func tagMiddleware(tag string) Constructor { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(tag)) h.ServeHTTP(w, r) }) } } // Not recommended (https://golang.org/pkg/reflect/#Value.Pointer), // but the best we can do. func funcsEqual(f1, f2 interface{}) bool { val1 := reflect.ValueOf(f1) val2 := reflect.ValueOf(f2) return val1.Pointer() == val2.Pointer() } var testApp = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("app\n")) }) func TestNew(t *testing.T) { c1 := func(h http.Handler) http.Handler { return nil } c2 := func(h http.Handler) http.Handler { return http.StripPrefix("potato", nil) } slice := []Constructor{c1, c2} chain := New(slice...) for k := range slice { if !funcsEqual(chain.constructors[k], slice[k]) { t.Error("New does not add constructors correctly") } } } func TestThenWorksWithNoMiddleware(t *testing.T) { if !funcsEqual(New().Then(testApp), testApp) { t.Error("Then does not work with no middleware") } } func TestThenTreatsNilAsDefaultServeMux(t *testing.T) { if New().Then(nil) != http.DefaultServeMux { t.Error("Then does not treat nil as DefaultServeMux") } } func TestThenFuncTreatsNilAsDefaultServeMux(t *testing.T) { if New().ThenFunc(nil) != http.DefaultServeMux { t.Error("ThenFunc does not treat nil as DefaultServeMux") } } func TestThenFuncConstructsHandlerFunc(t *testing.T) { fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }) chained := New().ThenFunc(fn) rec := httptest.NewRecorder() chained.ServeHTTP(rec, (*http.Request)(nil)) if reflect.TypeOf(chained) != reflect.TypeOf((http.HandlerFunc)(nil)) { t.Error("ThenFunc does not construct HandlerFunc") } } func TestThenOrdersHandlersCorrectly(t *testing.T) { t1 := tagMiddleware("t1\n") t2 := tagMiddleware("t2\n") t3 := tagMiddleware("t3\n") chained := New(t1, t2, t3).Then(testApp) w := httptest.NewRecorder() r, err := http.NewRequest("GET", "/", nil) if err != nil { t.Fatal(err) } chained.ServeHTTP(w, r) if w.Body.String() != "t1\nt2\nt3\napp\n" { t.Error("Then does not order handlers correctly") } } func TestAppendAddsHandlersCorrectly(t *testing.T) { chain := New(tagMiddleware("t1\n"), tagMiddleware("t2\n")) newChain := chain.Append(tagMiddleware("t3\n"), tagMiddleware("t4\n")) if len(chain.constructors) != 2 { t.Error("chain should have 2 constructors") } if len(newChain.constructors) != 4 { t.Error("newChain should have 4 constructors") } chained := newChain.Then(testApp) w := httptest.NewRecorder() r, err := http.NewRequest("GET", "/", nil) if err != nil { t.Fatal(err) } chained.ServeHTTP(w, r) if w.Body.String() != "t1\nt2\nt3\nt4\napp\n" { t.Error("Append does not add handlers correctly") } } func TestAppendRespectsImmutability(t *testing.T) { chain := New(tagMiddleware("")) newChain := chain.Append(tagMiddleware("")) if &chain.constructors[0] == &newChain.constructors[0] { t.Error("Apppend does not respect immutability") } } func TestExtendAddsHandlersCorrectly(t *testing.T) { chain1 := New(tagMiddleware("t1\n"), tagMiddleware("t2\n")) chain2 := New(tagMiddleware("t3\n"), tagMiddleware("t4\n")) newChain := chain1.Extend(chain2) if len(chain1.constructors) != 2 { t.Error("chain1 should contain 2 constructors") } if len(chain2.constructors) != 2 { t.Error("chain2 should contain 2 constructors") } if len(newChain.constructors) != 4 { t.Error("newChain should contain 4 constructors") } chained := newChain.Then(testApp) w := httptest.NewRecorder() r, err := http.NewRequest("GET", "/", nil) if err != nil { t.Fatal(err) } chained.ServeHTTP(w, r) if w.Body.String() != "t1\nt2\nt3\nt4\napp\n" { t.Error("Extend does not add handlers in correctly") } } func TestExtendRespectsImmutability(t *testing.T) { chain := New(tagMiddleware("")) newChain := chain.Extend(New(tagMiddleware(""))) if &chain.constructors[0] == &newChain.constructors[0] { t.Error("Extend does not respect immutability") } }