From ea3cb4563a8cbb9f9240e2702286fbe9ab18fbcf Mon Sep 17 00:00:00 2001 From: Mohammad Oweis Date: Wed, 25 Mar 2020 14:39:56 -0700 Subject: [PATCH 01/11] added: Endware functionality --- chain.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 21 deletions(-) diff --git a/chain.go b/chain.go index da0e2b5..fe36cdf 100644 --- a/chain.go +++ b/chain.go @@ -1,7 +1,9 @@ // Package alice provides a convenient way to chain http handlers. package alice -import "net/http" +import ( + "net/http" +) // A constructor for a piece of middleware. // Some middleware use this constructor out of the box, @@ -14,6 +16,7 @@ type Constructor func(http.Handler) http.Handler // the same set of constructors in the same order. type Chain struct { constructors []Constructor + endwares []Endware } // New creates a new chain, @@ -21,25 +24,27 @@ type Chain struct { // 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...)} + return Chain{append(([]Constructor)(nil), constructors...), nil} } -// Then chains the middleware and returns the final http.Handler. -// New(m1, m2, m3).Then(h) +// Then chains the middleware and endwares and returns the final http.Handler. +// New(m1, m2, m3).After(e1, e2, e3).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). +// followed by: +// e1(e2(e3())) +// When the request comes in, it will be passed to m1, then m2, then m3, +// then the given handler (who serves the response), then e1, e2, e3 +// (assuming every middleware/endwares calls the following one). // // A chain can be safely reused by calling Then() several times. -// stdStack := alice.New(ratelimitHandler, csrfHandler) +// stdStack := alice.New(ratelimitHandler, csrfHandler).After(loggingHandler) // 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 +// Note that constructors and endwares are called on every call to Then() +// and thus several instances of the same middleware/endwares will be created // when a chain is reused in this way. -// For proper middleware, this should cause no problems. +// For proper middleware/endwares, this should cause no problems. // // Then() treats nil as http.DefaultServeMux. func (c Chain) Then(h http.Handler) http.Handler { @@ -47,6 +52,14 @@ func (c Chain) Then(h http.Handler) http.Handler { h = http.DefaultServeMux } + h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + h.ServeHTTP(w, r) + + for _, endwareFn := range c.endwares { + endwareFn.ServeHTTP(w, r) + } + }) + for i := range c.constructors { h = c.constructors[len(c.constructors)-1-i](h) } @@ -73,6 +86,7 @@ func (c Chain) ThenFunc(fn http.HandlerFunc) http.Handler { // as the last ones in the request flow. // // Append returns a new chain, leaving the original one untouched. +// The new chain will have the original chain's endwares. // // stdChain := alice.New(m1, m2) // extChain := stdChain.Append(m3, m4) @@ -83,7 +97,7 @@ func (c Chain) Append(constructors ...Constructor) Chain { newCons = append(newCons, c.constructors...) newCons = append(newCons, constructors...) - return Chain{newCons} + return Chain{newCons, c.endwares} } // Extend extends a chain by adding the specified chain @@ -92,21 +106,96 @@ func (c Chain) Append(constructors ...Constructor) Chain { // Extend returns a new chain, leaving the original one untouched. // // stdChain := alice.New(m1, m2) -// ext1Chain := alice.New(m3, m4) +// ext1Chain := alice.New(m3, m4).After(e1, e2) // ext2Chain := stdChain.Extend(ext1Chain) -// // requests in stdChain go m1 -> m2 -// // requests in ext1Chain go m3 -> m4 -// // requests in ext2Chain go m1 -> m2 -> m3 -> m4 +// // requests in stdChain go m1 -> m2 -> handler +// // requests in ext1Chain go m3 -> m4 -> handler -> e1 -> e2 +// // requests in ext2Chain go m1 -> m2 -> m3 -> m4 -> handler -> e1 -> e2 // // Another example: // aHtmlAfterNosurf := alice.New(m2) +// logRequestChain := aHtmlAfterNosurf.After(e1) // aHtml := alice.New(m1, func(h http.Handler) http.Handler { // csrf := nosurf.New(h) -// csrf.SetFailureHandler(aHtmlAfterNosurf.ThenFunc(csrfFail)) +// csrf.SetFailureHandler(logRequestChain.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 +// }).Extend(logRequestChain) +// // requests to aHtml hitting nosurfs success handler go: +// m1 -> nosurf -> m2 -> target-handler -> e1 +// // requests to aHtml hitting nosurfs failure handler go: +// m1 -> nosurf -> m2 -> csrfFail -> e1 func (c Chain) Extend(chain Chain) Chain { - return c.Append(chain.constructors...) + newC := c. + Append(chain.constructors...). + AppendEndware(chain.endwares...) + return newC +} + +// Endware is functionality executed after a the main handler is called +// and response has been sent to the requester. Like middleware, +// values from the request or response can be accessed. This will not +// let you access values from the request or the response that can no longer be used. +// e.g. re-reading a request body, re-setting the response headers, etc. +type Endware http.Handler + +// After creates a new chain with the original chain's +// constructors and endwares, as well as the provided endwares. +// Endwares are executed after both the constructors and +// the Then() handler are called. +func (c Chain) After(endwares ...Endware) Chain { + return Chain{c.constructors, c.endwares}.AppendEndware(endwares...) +} + +// AfterFuncs works identically to After, but takes HandlerFuncs +// instead of Endwares. +// +// The following two statements are equivalent: +// c.After(http.HandlerFunc(fn1), http.HandlerFunc(fn2)) +// c.AfterFuncs(fn1, fn2) +// +// AfterFuncs provides all the guarantees of After. +func (c Chain) AfterFuncs(fns ...func(w http.ResponseWriter, r *http.Request)) Chain { + // convert each http.HandlerFunc into an Endware + endwares := make([]Endware, len(fns)) + for i, fn := range fns { + endwares[i] = http.HandlerFunc(fn) + } + + return Chain{c.constructors, c.endwares}.AppendEndware(endwares...) +} + +// AppendEndware extends a chain, adding the specified endwares +// as the last ones in the request flow. +// +// AppendEndware returns a new chain, leaving the original one untouched. +// The new chain will have the original chain's constructors. +// +// stdChain := alice.New(m1).After(e1, e2) +// extChain := stdChain.AppendEndware(e3, e4) +// // requests in stdHandler go m1 -> handler -> e1 -> e2 +// // requests in extHandler go m1 -> handler -> e1 -> e2 -> e3 -> e4 +func (c Chain) AppendEndware(endwares ...Endware) Chain { + newEnds := make([]Endware, 0, len(c.endwares)+len(endwares)) + newEnds = append(newEnds, c.endwares...) + newEnds = append(newEnds, endwares...) + + return Chain{c.constructors, newEnds} +} + +// AppendEndwareFuncs works identically to AppendEndware, but takes HandlerFuncs +// instead of Endwares. +// +// The following two statements are equivalent: +// c.AppendEndware(http.HandlerFunc(fn1), http.HandlerFunc(fn2)) +// c.AppendEndwareFuncs(fn1, fn2) +// +// AppendEndwareFuncs provides all the guarantees of AppendEndware. +func (c Chain) AppendEndwareFuncs(fns ...func(w http.ResponseWriter, r *http.Request)) Chain { + // convert each http.HandlerFunc into an Endware + endwares := make([]Endware, len(fns)) + for i, fn := range fns { + endwares[i] = http.HandlerFunc(fn) + } + + return Chain{c.constructors, c.endwares}.AppendEndware(endwares...) } From af47edeb703cfcfe80e25dda2e8e42e3e60907e8 Mon Sep 17 00:00:00 2001 From: Mohammad Oweis Date: Wed, 25 Mar 2020 14:45:01 -0700 Subject: [PATCH 02/11] removed: unnecessary variable in Extend --- chain.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chain.go b/chain.go index fe36cdf..a88d73b 100644 --- a/chain.go +++ b/chain.go @@ -125,10 +125,9 @@ func (c Chain) Append(constructors ...Constructor) Chain { // // requests to aHtml hitting nosurfs failure handler go: // m1 -> nosurf -> m2 -> csrfFail -> e1 func (c Chain) Extend(chain Chain) Chain { - newC := c. + return c. Append(chain.constructors...). AppendEndware(chain.endwares...) - return newC } // Endware is functionality executed after a the main handler is called From f7a7a24611c4ca9c2f5186f98bfc3d35bb8e1fde Mon Sep 17 00:00:00 2001 From: Mohammad Oweis Date: Wed, 25 Mar 2020 15:52:31 -0700 Subject: [PATCH 03/11] fixed: overflow from ServeHTTP calls --- chain.go | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/chain.go b/chain.go index a88d73b..8ae5ec4 100644 --- a/chain.go +++ b/chain.go @@ -24,7 +24,7 @@ type Chain struct { // 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...), nil} + return Chain{append(([]Constructor)(nil), constructors...), make([]Endware, 0)} } // Then chains the middleware and endwares and returns the final http.Handler. @@ -52,13 +52,9 @@ func (c Chain) Then(h http.Handler) http.Handler { h = http.DefaultServeMux } - h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - h.ServeHTTP(w, r) - - for _, endwareFn := range c.endwares { - endwareFn.ServeHTTP(w, r) - } - }) + if len(c.endwares) > 0 { + h = endwareHandler{h, c.endwares} + } for i := range c.constructors { h = c.constructors[len(c.constructors)-1-i](h) @@ -67,6 +63,25 @@ func (c Chain) Then(h http.Handler) http.Handler { return h } +// endwareHandler represents a handler that has been modified +// to execute endwares afterwards. This is a helper for Then() +// because if we just wrap it in an anonymous +// http.HandlerFunc(func(w http.ResponseWriter, r *http.Request))) +// there is a stack overflow +type endwareHandler struct { + handler http.Handler + endwares []Endware +} + +// ServeHTTP serves the main endwareHandler's handler as well as +// calling all of the individual endwares afterwards. +func (eh endwareHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + eh.handler.ServeHTTP(w, r) + for _, endware := range eh.endwares { + endware.ServeHTTP(w, r) + } +} + // ThenFunc works identically to Then, but takes // a HandlerFunc instead of a Handler. // From 2cb09e0e844288b8dcc94d8da44ac6b60a257214 Mon Sep 17 00:00:00 2001 From: Mohammad Oweis Date: Wed, 25 Mar 2020 15:52:52 -0700 Subject: [PATCH 04/11] added: tests --- chain_test.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/chain_test.go b/chain_test.go index 6f4316b..e8e4cff 100644 --- a/chain_test.go +++ b/chain_test.go @@ -20,6 +20,12 @@ func tagMiddleware(tag string) Constructor { } } +func tagEndware(tag string) Endware { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(tag)) + }) +} + // Not recommended (https://golang.org/pkg/reflect/#Value.Pointer), // but the best we can do. func funcsEqual(f1, f2 interface{}) bool { @@ -51,12 +57,33 @@ func TestNew(t *testing.T) { } } +func TestAfter(t *testing.T) { + e1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + e2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + slice := []Endware{e1, e2} + + chain := New().After(slice...) + for k := range slice { + if !funcsEqual(chain.endwares[k], slice[k]) { + t.Error("After does not add endwares correctly") + } + } +} + func TestThenWorksWithNoMiddleware(t *testing.T) { if !funcsEqual(New().Then(testApp), testApp) { t.Error("Then does not work with no middleware") } } +func TestThenWorksWithNoEndware(t *testing.T) { + if !funcsEqual(New().After().Then(testApp), testApp) { + t.Error("Then does not work with no endware") + } +} + func TestThenTreatsNilAsDefaultServeMux(t *testing.T) { if New().Then(nil) != http.DefaultServeMux { t.Error("Then does not treat nil as DefaultServeMux") @@ -87,8 +114,11 @@ func TestThenOrdersHandlersCorrectly(t *testing.T) { t1 := tagMiddleware("t1\n") t2 := tagMiddleware("t2\n") t3 := tagMiddleware("t3\n") + e1 := tagEndware("e1\n") + e2 := tagEndware("e2\n") + e3 := tagEndware("e3\n") - chained := New(t1, t2, t3).Then(testApp) + chained := New(t1, t2, t3).After(e1, e2, e3).Then(testApp) w := httptest.NewRecorder() r, err := http.NewRequest("GET", "/", nil) @@ -98,7 +128,7 @@ func TestThenOrdersHandlersCorrectly(t *testing.T) { chained.ServeHTTP(w, r) - if w.Body.String() != "t1\nt2\nt3\napp\n" { + if w.Body.String() != "t1\nt2\nt3\napp\ne1\ne2\ne3\n" { t.Error("Then does not order handlers correctly") } } @@ -134,24 +164,45 @@ func TestAppendRespectsImmutability(t *testing.T) { newChain := chain.Append(tagMiddleware("")) if &chain.constructors[0] == &newChain.constructors[0] { - t.Error("Apppend does not respect immutability") + t.Error("Append does not respect immutability") + } +} + +func TestAppendEndwareRespectsImmutability(t *testing.T) { + chain := New().After(tagEndware("")) + newChain := chain.AppendEndware(tagEndware("")) + + if &chain.endwares[0] == &newChain.endwares[0] { + t.Error("AppendEndware 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")) + chain2 := New(tagMiddleware("t3\n"), tagMiddleware("t4\n")). + After(tagEndware("e1\n"), tagEndware("e2\n")) newChain := chain1.Extend(chain2) if len(chain1.constructors) != 2 { t.Error("chain1 should contain 2 constructors") } + if len(chain1.endwares) != 0 { + t.Error("chain1 should contain 0 endwares") + } + if len(chain2.constructors) != 2 { t.Error("chain2 should contain 2 constructors") } + if len(chain2.endwares) != 2 { + t.Error("chain2 should contain 2 endwares") + } + if len(newChain.constructors) != 4 { t.Error("newChain should contain 4 constructors") } + if len(newChain.endwares) != 2 { + t.Error("newChain should contain 2 endwares") + } chained := newChain.Then(testApp) @@ -163,16 +214,20 @@ func TestExtendAddsHandlersCorrectly(t *testing.T) { chained.ServeHTTP(w, r) - if w.Body.String() != "t1\nt2\nt3\nt4\napp\n" { + if w.Body.String() != "t1\nt2\nt3\nt4\napp\ne1\ne2\n" { t.Error("Extend does not add handlers in correctly") } } func TestExtendRespectsImmutability(t *testing.T) { - chain := New(tagMiddleware("")) + chain := New(tagMiddleware("")).After(tagEndware("")) newChain := chain.Extend(New(tagMiddleware(""))) if &chain.constructors[0] == &newChain.constructors[0] { t.Error("Extend does not respect immutability") } + + if &chain.endwares[0] == &newChain.endwares[0] { + t.Error("Extend does not respect immutability for endwares") + } } From 242520ce8d7ae980e915fb590402a81e57d4aa94 Mon Sep 17 00:00:00 2001 From: Mohammad Oweis Date: Wed, 25 Mar 2020 16:16:23 -0700 Subject: [PATCH 05/11] fixed: immutability issue with tests --- chain.go | 21 ++++++++------- chain_test.go | 73 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 13 deletions(-) diff --git a/chain.go b/chain.go index 8ae5ec4..2a4209d 100644 --- a/chain.go +++ b/chain.go @@ -112,7 +112,7 @@ func (c Chain) Append(constructors ...Constructor) Chain { newCons = append(newCons, c.constructors...) newCons = append(newCons, constructors...) - return Chain{newCons, c.endwares} + return New(newCons...).AppendEndware(c.endwares...) } // Extend extends a chain by adding the specified chain @@ -157,7 +157,13 @@ type Endware http.Handler // Endwares are executed after both the constructors and // the Then() handler are called. func (c Chain) After(endwares ...Endware) Chain { - return Chain{c.constructors, c.endwares}.AppendEndware(endwares...) + newEnds := make([]Endware, 0, len(c.endwares)+len(endwares)) + newEnds = append(newEnds, c.endwares...) + newEnds = append(newEnds, endwares...) + + newC := New(c.constructors...) + newC.endwares = newEnds + return newC } // AfterFuncs works identically to After, but takes HandlerFuncs @@ -175,7 +181,7 @@ func (c Chain) AfterFuncs(fns ...func(w http.ResponseWriter, r *http.Request)) C endwares[i] = http.HandlerFunc(fn) } - return Chain{c.constructors, c.endwares}.AppendEndware(endwares...) + return c.After(endwares...) } // AppendEndware extends a chain, adding the specified endwares @@ -189,11 +195,7 @@ func (c Chain) AfterFuncs(fns ...func(w http.ResponseWriter, r *http.Request)) C // // requests in stdHandler go m1 -> handler -> e1 -> e2 // // requests in extHandler go m1 -> handler -> e1 -> e2 -> e3 -> e4 func (c Chain) AppendEndware(endwares ...Endware) Chain { - newEnds := make([]Endware, 0, len(c.endwares)+len(endwares)) - newEnds = append(newEnds, c.endwares...) - newEnds = append(newEnds, endwares...) - - return Chain{c.constructors, newEnds} + return New(c.constructors...).After(append(c.endwares, endwares...)...) } // AppendEndwareFuncs works identically to AppendEndware, but takes HandlerFuncs @@ -211,5 +213,6 @@ func (c Chain) AppendEndwareFuncs(fns ...func(w http.ResponseWriter, r *http.Req endwares[i] = http.HandlerFunc(fn) } - return Chain{c.constructors, c.endwares}.AppendEndware(endwares...) + return c.AppendEndware(endwares...) + } diff --git a/chain_test.go b/chain_test.go index e8e4cff..acea6a8 100644 --- a/chain_test.go +++ b/chain_test.go @@ -140,9 +140,15 @@ func TestAppendAddsHandlersCorrectly(t *testing.T) { if len(chain.constructors) != 2 { t.Error("chain should have 2 constructors") } + if len(chain.endwares) != 0 { + t.Error("chain should have 0 endwares") + } if len(newChain.constructors) != 4 { t.Error("newChain should have 4 constructors") } + if len(newChain.endwares) != 0 { + t.Error("newChain should have 0 endwares") + } chained := newChain.Then(testApp) @@ -159,21 +165,80 @@ func TestAppendAddsHandlersCorrectly(t *testing.T) { } } +func TestAppendEndwareAddsHandlersCorrectly(t *testing.T) { + chain := New(tagMiddleware("t1\n")).After(tagEndware("e1\n"), tagEndware("e2\n")) + newChain := chain.AppendEndware(tagEndware("e3\n"), tagEndware("e4\n")) + + if len(chain.constructors) != 1 { + t.Error("chain should have 1 constructor") + } + if len(chain.endwares) != 2 { + t.Error("chain should have 2 endwares") + } + if len(newChain.constructors) != 1 { + t.Error("newChain should have 1 constructor") + } + if len(newChain.endwares) != 4 { + t.Error("newChain should have 4 endwares") + } + + 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\napp\ne1\ne2\ne3\ne4\n" { + t.Error("AppendEndware does not add handlers correctly") + } +} + func TestAppendRespectsImmutability(t *testing.T) { - chain := New(tagMiddleware("")) + chain := New(tagMiddleware("")).After(tagEndware("")) newChain := chain.Append(tagMiddleware("")) if &chain.constructors[0] == &newChain.constructors[0] { - t.Error("Append does not respect immutability") + t.Error("Append does not respect constructor immutability") + } + + if &chain.endwares[0] == &newChain.endwares[0] { + t.Error("Append does not respect endware immutability") } } func TestAppendEndwareRespectsImmutability(t *testing.T) { - chain := New().After(tagEndware("")) + chain := New(tagMiddleware("")).After(tagEndware("")) newChain := chain.AppendEndware(tagEndware("")) + if &chain.constructors[0] == &newChain.constructors[0] { + t.Error("AppendEndware does not respect constructor immutability") + } + + if &chain.endwares[0] == &newChain.endwares[0] { + t.Error("AppendEndware does not respect endware immutability") + } +} + +func TestExtendsRespectsImmutability(t *testing.T) { + chain := New(tagMiddleware("")).After(tagEndware("")) + newChain := chain.Extend(New(tagMiddleware(""))) + + // chain.constructors[0] should have the same functionality as + // newChain.constructors[1], but check both anyways + if &chain.constructors[0] == &newChain.constructors[0] { + t.Error("Extends does not respect constructor immutability") + } + + if &chain.constructors[0] == &newChain.constructors[1] { + t.Error("Extends does not respect constructor immutability") + } + if &chain.endwares[0] == &newChain.endwares[0] { - t.Error("AppendEndware does not respect immutability") + t.Error("Extends does not respect endware immutability") } } From b196bc7991f16620312934176c810a62333160ac Mon Sep 17 00:00:00 2001 From: Mohammad Oweis Date: Wed, 25 Mar 2020 16:19:03 -0700 Subject: [PATCH 06/11] updated: better immutability test --- chain_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chain_test.go b/chain_test.go index acea6a8..ab12e2f 100644 --- a/chain_test.go +++ b/chain_test.go @@ -225,7 +225,8 @@ func TestAppendEndwareRespectsImmutability(t *testing.T) { func TestExtendsRespectsImmutability(t *testing.T) { chain := New(tagMiddleware("")).After(tagEndware("")) - newChain := chain.Extend(New(tagMiddleware(""))) + newChain := New(tagMiddleware("")).After(tagEndware("")) + newChain = chain.Extend(newChain) // chain.constructors[0] should have the same functionality as // newChain.constructors[1], but check both anyways @@ -240,6 +241,10 @@ func TestExtendsRespectsImmutability(t *testing.T) { if &chain.endwares[0] == &newChain.endwares[0] { t.Error("Extends does not respect endware immutability") } + + if &chain.endwares[0] == &newChain.endwares[1] { + t.Error("Extends does not respect endware immutability") + } } func TestExtendAddsHandlersCorrectly(t *testing.T) { From 9659d7020cd1658ab60339d239829d2a22619a88 Mon Sep 17 00:00:00 2001 From: Mohammad Oweis Date: Wed, 25 Mar 2020 16:25:20 -0700 Subject: [PATCH 07/11] updated: make([]Endware,0) -> ([]Endware)(nil) --- chain.go | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/chain.go b/chain.go index 2a4209d..2c8059e 100644 --- a/chain.go +++ b/chain.go @@ -24,7 +24,26 @@ type Chain struct { // 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...), make([]Endware, 0)} + return Chain{append(([]Constructor)(nil), constructors...), ([]Endware)(nil)} +} + +// endwareHandler represents a handler that has been modified +// to execute endwares afterwards. This is a helper for Then() +// because if we just wrap it in an anonymous +// http.HandlerFunc(func(w http.ResponseWriter, r *http.Request))) +// there is a stack overflow +type endwareHandler struct { + handler http.Handler + endwares []Endware +} + +// ServeHTTP serves the main endwareHandler's handler as well as +// calling all of the individual endwares afterwards. +func (eh endwareHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + eh.handler.ServeHTTP(w, r) + for _, endware := range eh.endwares { + endware.ServeHTTP(w, r) + } } // Then chains the middleware and endwares and returns the final http.Handler. @@ -63,25 +82,6 @@ func (c Chain) Then(h http.Handler) http.Handler { return h } -// endwareHandler represents a handler that has been modified -// to execute endwares afterwards. This is a helper for Then() -// because if we just wrap it in an anonymous -// http.HandlerFunc(func(w http.ResponseWriter, r *http.Request))) -// there is a stack overflow -type endwareHandler struct { - handler http.Handler - endwares []Endware -} - -// ServeHTTP serves the main endwareHandler's handler as well as -// calling all of the individual endwares afterwards. -func (eh endwareHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - eh.handler.ServeHTTP(w, r) - for _, endware := range eh.endwares { - endware.ServeHTTP(w, r) - } -} - // ThenFunc works identically to Then, but takes // a HandlerFunc instead of a Handler. // From d69ac3b46ebb4b31d5b5bf3d1251a2d85fafedf9 Mon Sep 17 00:00:00 2001 From: Mohammad Oweis Date: Wed, 25 Mar 2020 16:59:14 -0700 Subject: [PATCH 08/11] updated: tests to include After() call if it has a New() --- chain_test.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/chain_test.go b/chain_test.go index ab12e2f..3834a35 100644 --- a/chain_test.go +++ b/chain_test.go @@ -58,9 +58,13 @@ func TestNew(t *testing.T) { } func TestAfter(t *testing.T) { - e1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + e1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("e1\n")) + }) - e2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + e2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("e2\n")) + }) slice := []Endware{e1, e2} @@ -85,13 +89,13 @@ func TestThenWorksWithNoEndware(t *testing.T) { } func TestThenTreatsNilAsDefaultServeMux(t *testing.T) { - if New().Then(nil) != http.DefaultServeMux { + if New().After().Then(nil) != http.DefaultServeMux { t.Error("Then does not treat nil as DefaultServeMux") } } func TestThenFuncTreatsNilAsDefaultServeMux(t *testing.T) { - if New().ThenFunc(nil) != http.DefaultServeMux { + if New().After().ThenFunc(nil) != http.DefaultServeMux { t.Error("ThenFunc does not treat nil as DefaultServeMux") } } @@ -100,7 +104,7 @@ func TestThenFuncConstructsHandlerFunc(t *testing.T) { fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }) - chained := New().ThenFunc(fn) + chained := New().After().ThenFunc(fn) rec := httptest.NewRecorder() chained.ServeHTTP(rec, (*http.Request)(nil)) @@ -291,10 +295,10 @@ func TestExtendAddsHandlersCorrectly(t *testing.T) { func TestExtendRespectsImmutability(t *testing.T) { chain := New(tagMiddleware("")).After(tagEndware("")) - newChain := chain.Extend(New(tagMiddleware(""))) + newChain := chain.Extend(New()) if &chain.constructors[0] == &newChain.constructors[0] { - t.Error("Extend does not respect immutability") + t.Error("Extend does not respect immutability for constructors") } if &chain.endwares[0] == &newChain.endwares[0] { From 561f6e5904688709e9cfc306afe97d4218e7537c Mon Sep 17 00:00:00 2001 From: Mohammad Oweis Date: Wed, 25 Mar 2020 17:07:39 -0700 Subject: [PATCH 09/11] update: After -> FinishWith --- chain.go | 28 ++++++++++++++-------------- chain_test.go | 28 ++++++++++++++-------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/chain.go b/chain.go index 2c8059e..76fa38b 100644 --- a/chain.go +++ b/chain.go @@ -47,7 +47,7 @@ func (eh endwareHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Then chains the middleware and endwares and returns the final http.Handler. -// New(m1, m2, m3).After(e1, e2, e3).Then(h) +// New(m1, m2, m3).FinishWith(e1, e2, e3).Then(h) // is equivalent to: // m1(m2(m3(h))) // followed by: @@ -57,7 +57,7 @@ func (eh endwareHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // (assuming every middleware/endwares calls the following one). // // A chain can be safely reused by calling Then() several times. -// stdStack := alice.New(ratelimitHandler, csrfHandler).After(loggingHandler) +// stdStack := alice.New(ratelimitHandler, csrfHandler).FinishWith(loggingHandler) // indexPipe = stdStack.Then(indexHandler) // authPipe = stdStack.Then(authHandler) // Note that constructors and endwares are called on every call to Then() @@ -121,7 +121,7 @@ func (c Chain) Append(constructors ...Constructor) Chain { // Extend returns a new chain, leaving the original one untouched. // // stdChain := alice.New(m1, m2) -// ext1Chain := alice.New(m3, m4).After(e1, e2) +// ext1Chain := alice.New(m3, m4).FinishWith(e1, e2) // ext2Chain := stdChain.Extend(ext1Chain) // // requests in stdChain go m1 -> m2 -> handler // // requests in ext1Chain go m3 -> m4 -> handler -> e1 -> e2 @@ -129,7 +129,7 @@ func (c Chain) Append(constructors ...Constructor) Chain { // // Another example: // aHtmlAfterNosurf := alice.New(m2) -// logRequestChain := aHtmlAfterNosurf.After(e1) +// logRequestChain := aHtmlAfterNosurf.FinishWith(e1) // aHtml := alice.New(m1, func(h http.Handler) http.Handler { // csrf := nosurf.New(h) // csrf.SetFailureHandler(logRequestChain.ThenFunc(csrfFail)) @@ -152,11 +152,11 @@ func (c Chain) Extend(chain Chain) Chain { // e.g. re-reading a request body, re-setting the response headers, etc. type Endware http.Handler -// After creates a new chain with the original chain's +// FinishWith creates a new chain with the original chain's // constructors and endwares, as well as the provided endwares. // Endwares are executed after both the constructors and // the Then() handler are called. -func (c Chain) After(endwares ...Endware) Chain { +func (c Chain) FinishWith(endwares ...Endware) Chain { newEnds := make([]Endware, 0, len(c.endwares)+len(endwares)) newEnds = append(newEnds, c.endwares...) newEnds = append(newEnds, endwares...) @@ -166,22 +166,22 @@ func (c Chain) After(endwares ...Endware) Chain { return newC } -// AfterFuncs works identically to After, but takes HandlerFuncs +// FinishWithFuncs works identically to FinishWith, but takes HandlerFuncs // instead of Endwares. // // The following two statements are equivalent: -// c.After(http.HandlerFunc(fn1), http.HandlerFunc(fn2)) -// c.AfterFuncs(fn1, fn2) +// c.FinishWith(http.HandlerFunc(fn1), http.HandlerFunc(fn2)) +// c.FinishWithFuncs(fn1, fn2) // -// AfterFuncs provides all the guarantees of After. -func (c Chain) AfterFuncs(fns ...func(w http.ResponseWriter, r *http.Request)) Chain { +// FinishWithFuncs provides all the guarantees of FinishWith. +func (c Chain) FinishWithFuncs(fns ...func(w http.ResponseWriter, r *http.Request)) Chain { // convert each http.HandlerFunc into an Endware endwares := make([]Endware, len(fns)) for i, fn := range fns { endwares[i] = http.HandlerFunc(fn) } - return c.After(endwares...) + return c.FinishWith(endwares...) } // AppendEndware extends a chain, adding the specified endwares @@ -190,12 +190,12 @@ func (c Chain) AfterFuncs(fns ...func(w http.ResponseWriter, r *http.Request)) C // AppendEndware returns a new chain, leaving the original one untouched. // The new chain will have the original chain's constructors. // -// stdChain := alice.New(m1).After(e1, e2) +// stdChain := alice.New(m1).FinishWith(e1, e2) // extChain := stdChain.AppendEndware(e3, e4) // // requests in stdHandler go m1 -> handler -> e1 -> e2 // // requests in extHandler go m1 -> handler -> e1 -> e2 -> e3 -> e4 func (c Chain) AppendEndware(endwares ...Endware) Chain { - return New(c.constructors...).After(append(c.endwares, endwares...)...) + return New(c.constructors...).FinishWith(append(c.endwares, endwares...)...) } // AppendEndwareFuncs works identically to AppendEndware, but takes HandlerFuncs diff --git a/chain_test.go b/chain_test.go index 3834a35..bf7e101 100644 --- a/chain_test.go +++ b/chain_test.go @@ -68,10 +68,10 @@ func TestAfter(t *testing.T) { slice := []Endware{e1, e2} - chain := New().After(slice...) + chain := New().FinishWith(slice...) for k := range slice { if !funcsEqual(chain.endwares[k], slice[k]) { - t.Error("After does not add endwares correctly") + t.Error("FinishWith does not add endwares correctly") } } } @@ -83,19 +83,19 @@ func TestThenWorksWithNoMiddleware(t *testing.T) { } func TestThenWorksWithNoEndware(t *testing.T) { - if !funcsEqual(New().After().Then(testApp), testApp) { + if !funcsEqual(New().FinishWith().Then(testApp), testApp) { t.Error("Then does not work with no endware") } } func TestThenTreatsNilAsDefaultServeMux(t *testing.T) { - if New().After().Then(nil) != http.DefaultServeMux { + if New().FinishWith().Then(nil) != http.DefaultServeMux { t.Error("Then does not treat nil as DefaultServeMux") } } func TestThenFuncTreatsNilAsDefaultServeMux(t *testing.T) { - if New().After().ThenFunc(nil) != http.DefaultServeMux { + if New().FinishWith().ThenFunc(nil) != http.DefaultServeMux { t.Error("ThenFunc does not treat nil as DefaultServeMux") } } @@ -104,7 +104,7 @@ func TestThenFuncConstructsHandlerFunc(t *testing.T) { fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }) - chained := New().After().ThenFunc(fn) + chained := New().FinishWith().ThenFunc(fn) rec := httptest.NewRecorder() chained.ServeHTTP(rec, (*http.Request)(nil)) @@ -122,7 +122,7 @@ func TestThenOrdersHandlersCorrectly(t *testing.T) { e2 := tagEndware("e2\n") e3 := tagEndware("e3\n") - chained := New(t1, t2, t3).After(e1, e2, e3).Then(testApp) + chained := New(t1, t2, t3).FinishWith(e1, e2, e3).Then(testApp) w := httptest.NewRecorder() r, err := http.NewRequest("GET", "/", nil) @@ -170,7 +170,7 @@ func TestAppendAddsHandlersCorrectly(t *testing.T) { } func TestAppendEndwareAddsHandlersCorrectly(t *testing.T) { - chain := New(tagMiddleware("t1\n")).After(tagEndware("e1\n"), tagEndware("e2\n")) + chain := New(tagMiddleware("t1\n")).FinishWith(tagEndware("e1\n"), tagEndware("e2\n")) newChain := chain.AppendEndware(tagEndware("e3\n"), tagEndware("e4\n")) if len(chain.constructors) != 1 { @@ -202,7 +202,7 @@ func TestAppendEndwareAddsHandlersCorrectly(t *testing.T) { } func TestAppendRespectsImmutability(t *testing.T) { - chain := New(tagMiddleware("")).After(tagEndware("")) + chain := New(tagMiddleware("")).FinishWith(tagEndware("")) newChain := chain.Append(tagMiddleware("")) if &chain.constructors[0] == &newChain.constructors[0] { @@ -215,7 +215,7 @@ func TestAppendRespectsImmutability(t *testing.T) { } func TestAppendEndwareRespectsImmutability(t *testing.T) { - chain := New(tagMiddleware("")).After(tagEndware("")) + chain := New(tagMiddleware("")).FinishWith(tagEndware("")) newChain := chain.AppendEndware(tagEndware("")) if &chain.constructors[0] == &newChain.constructors[0] { @@ -228,8 +228,8 @@ func TestAppendEndwareRespectsImmutability(t *testing.T) { } func TestExtendsRespectsImmutability(t *testing.T) { - chain := New(tagMiddleware("")).After(tagEndware("")) - newChain := New(tagMiddleware("")).After(tagEndware("")) + chain := New(tagMiddleware("")).FinishWith(tagEndware("")) + newChain := New(tagMiddleware("")).FinishWith(tagEndware("")) newChain = chain.Extend(newChain) // chain.constructors[0] should have the same functionality as @@ -254,7 +254,7 @@ func TestExtendsRespectsImmutability(t *testing.T) { func TestExtendAddsHandlersCorrectly(t *testing.T) { chain1 := New(tagMiddleware("t1\n"), tagMiddleware("t2\n")) chain2 := New(tagMiddleware("t3\n"), tagMiddleware("t4\n")). - After(tagEndware("e1\n"), tagEndware("e2\n")) + FinishWith(tagEndware("e1\n"), tagEndware("e2\n")) newChain := chain1.Extend(chain2) if len(chain1.constructors) != 2 { @@ -294,7 +294,7 @@ func TestExtendAddsHandlersCorrectly(t *testing.T) { } func TestExtendRespectsImmutability(t *testing.T) { - chain := New(tagMiddleware("")).After(tagEndware("")) + chain := New(tagMiddleware("")).FinishWith(tagEndware("")) newChain := chain.Extend(New()) if &chain.constructors[0] == &newChain.constructors[0] { From 4e0d17f7bbd6e672c44caab845baf08e88560dd2 Mon Sep 17 00:00:00 2001 From: Mohammad Oweis Date: Wed, 25 Mar 2020 17:09:42 -0700 Subject: [PATCH 10/11] update: FinishWith -> Finally --- chain.go | 28 ++++++++++++++-------------- chain_test.go | 28 ++++++++++++++-------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/chain.go b/chain.go index 76fa38b..7d2393e 100644 --- a/chain.go +++ b/chain.go @@ -47,7 +47,7 @@ func (eh endwareHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Then chains the middleware and endwares and returns the final http.Handler. -// New(m1, m2, m3).FinishWith(e1, e2, e3).Then(h) +// New(m1, m2, m3).Finally(e1, e2, e3).Then(h) // is equivalent to: // m1(m2(m3(h))) // followed by: @@ -57,7 +57,7 @@ func (eh endwareHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // (assuming every middleware/endwares calls the following one). // // A chain can be safely reused by calling Then() several times. -// stdStack := alice.New(ratelimitHandler, csrfHandler).FinishWith(loggingHandler) +// stdStack := alice.New(ratelimitHandler, csrfHandler).Finally(loggingHandler) // indexPipe = stdStack.Then(indexHandler) // authPipe = stdStack.Then(authHandler) // Note that constructors and endwares are called on every call to Then() @@ -121,7 +121,7 @@ func (c Chain) Append(constructors ...Constructor) Chain { // Extend returns a new chain, leaving the original one untouched. // // stdChain := alice.New(m1, m2) -// ext1Chain := alice.New(m3, m4).FinishWith(e1, e2) +// ext1Chain := alice.New(m3, m4).Finally(e1, e2) // ext2Chain := stdChain.Extend(ext1Chain) // // requests in stdChain go m1 -> m2 -> handler // // requests in ext1Chain go m3 -> m4 -> handler -> e1 -> e2 @@ -129,7 +129,7 @@ func (c Chain) Append(constructors ...Constructor) Chain { // // Another example: // aHtmlAfterNosurf := alice.New(m2) -// logRequestChain := aHtmlAfterNosurf.FinishWith(e1) +// logRequestChain := aHtmlAfterNosurf.Finally(e1) // aHtml := alice.New(m1, func(h http.Handler) http.Handler { // csrf := nosurf.New(h) // csrf.SetFailureHandler(logRequestChain.ThenFunc(csrfFail)) @@ -152,11 +152,11 @@ func (c Chain) Extend(chain Chain) Chain { // e.g. re-reading a request body, re-setting the response headers, etc. type Endware http.Handler -// FinishWith creates a new chain with the original chain's +// Finally creates a new chain with the original chain's // constructors and endwares, as well as the provided endwares. // Endwares are executed after both the constructors and // the Then() handler are called. -func (c Chain) FinishWith(endwares ...Endware) Chain { +func (c Chain) Finally(endwares ...Endware) Chain { newEnds := make([]Endware, 0, len(c.endwares)+len(endwares)) newEnds = append(newEnds, c.endwares...) newEnds = append(newEnds, endwares...) @@ -166,22 +166,22 @@ func (c Chain) FinishWith(endwares ...Endware) Chain { return newC } -// FinishWithFuncs works identically to FinishWith, but takes HandlerFuncs +// FinallyFuncs works identically to Finally, but takes HandlerFuncs // instead of Endwares. // // The following two statements are equivalent: -// c.FinishWith(http.HandlerFunc(fn1), http.HandlerFunc(fn2)) -// c.FinishWithFuncs(fn1, fn2) +// c.Finally(http.HandlerFunc(fn1), http.HandlerFunc(fn2)) +// c.FinallyFuncs(fn1, fn2) // -// FinishWithFuncs provides all the guarantees of FinishWith. -func (c Chain) FinishWithFuncs(fns ...func(w http.ResponseWriter, r *http.Request)) Chain { +// FinallyFuncs provides all the guarantees of Finally. +func (c Chain) FinallyFuncs(fns ...func(w http.ResponseWriter, r *http.Request)) Chain { // convert each http.HandlerFunc into an Endware endwares := make([]Endware, len(fns)) for i, fn := range fns { endwares[i] = http.HandlerFunc(fn) } - return c.FinishWith(endwares...) + return c.Finally(endwares...) } // AppendEndware extends a chain, adding the specified endwares @@ -190,12 +190,12 @@ func (c Chain) FinishWithFuncs(fns ...func(w http.ResponseWriter, r *http.Reques // AppendEndware returns a new chain, leaving the original one untouched. // The new chain will have the original chain's constructors. // -// stdChain := alice.New(m1).FinishWith(e1, e2) +// stdChain := alice.New(m1).Finally(e1, e2) // extChain := stdChain.AppendEndware(e3, e4) // // requests in stdHandler go m1 -> handler -> e1 -> e2 // // requests in extHandler go m1 -> handler -> e1 -> e2 -> e3 -> e4 func (c Chain) AppendEndware(endwares ...Endware) Chain { - return New(c.constructors...).FinishWith(append(c.endwares, endwares...)...) + return New(c.constructors...).Finally(append(c.endwares, endwares...)...) } // AppendEndwareFuncs works identically to AppendEndware, but takes HandlerFuncs diff --git a/chain_test.go b/chain_test.go index bf7e101..0d1959b 100644 --- a/chain_test.go +++ b/chain_test.go @@ -68,10 +68,10 @@ func TestAfter(t *testing.T) { slice := []Endware{e1, e2} - chain := New().FinishWith(slice...) + chain := New().Finally(slice...) for k := range slice { if !funcsEqual(chain.endwares[k], slice[k]) { - t.Error("FinishWith does not add endwares correctly") + t.Error("Finally does not add endwares correctly") } } } @@ -83,19 +83,19 @@ func TestThenWorksWithNoMiddleware(t *testing.T) { } func TestThenWorksWithNoEndware(t *testing.T) { - if !funcsEqual(New().FinishWith().Then(testApp), testApp) { + if !funcsEqual(New().Finally().Then(testApp), testApp) { t.Error("Then does not work with no endware") } } func TestThenTreatsNilAsDefaultServeMux(t *testing.T) { - if New().FinishWith().Then(nil) != http.DefaultServeMux { + if New().Finally().Then(nil) != http.DefaultServeMux { t.Error("Then does not treat nil as DefaultServeMux") } } func TestThenFuncTreatsNilAsDefaultServeMux(t *testing.T) { - if New().FinishWith().ThenFunc(nil) != http.DefaultServeMux { + if New().Finally().ThenFunc(nil) != http.DefaultServeMux { t.Error("ThenFunc does not treat nil as DefaultServeMux") } } @@ -104,7 +104,7 @@ func TestThenFuncConstructsHandlerFunc(t *testing.T) { fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }) - chained := New().FinishWith().ThenFunc(fn) + chained := New().Finally().ThenFunc(fn) rec := httptest.NewRecorder() chained.ServeHTTP(rec, (*http.Request)(nil)) @@ -122,7 +122,7 @@ func TestThenOrdersHandlersCorrectly(t *testing.T) { e2 := tagEndware("e2\n") e3 := tagEndware("e3\n") - chained := New(t1, t2, t3).FinishWith(e1, e2, e3).Then(testApp) + chained := New(t1, t2, t3).Finally(e1, e2, e3).Then(testApp) w := httptest.NewRecorder() r, err := http.NewRequest("GET", "/", nil) @@ -170,7 +170,7 @@ func TestAppendAddsHandlersCorrectly(t *testing.T) { } func TestAppendEndwareAddsHandlersCorrectly(t *testing.T) { - chain := New(tagMiddleware("t1\n")).FinishWith(tagEndware("e1\n"), tagEndware("e2\n")) + chain := New(tagMiddleware("t1\n")).Finally(tagEndware("e1\n"), tagEndware("e2\n")) newChain := chain.AppendEndware(tagEndware("e3\n"), tagEndware("e4\n")) if len(chain.constructors) != 1 { @@ -202,7 +202,7 @@ func TestAppendEndwareAddsHandlersCorrectly(t *testing.T) { } func TestAppendRespectsImmutability(t *testing.T) { - chain := New(tagMiddleware("")).FinishWith(tagEndware("")) + chain := New(tagMiddleware("")).Finally(tagEndware("")) newChain := chain.Append(tagMiddleware("")) if &chain.constructors[0] == &newChain.constructors[0] { @@ -215,7 +215,7 @@ func TestAppendRespectsImmutability(t *testing.T) { } func TestAppendEndwareRespectsImmutability(t *testing.T) { - chain := New(tagMiddleware("")).FinishWith(tagEndware("")) + chain := New(tagMiddleware("")).Finally(tagEndware("")) newChain := chain.AppendEndware(tagEndware("")) if &chain.constructors[0] == &newChain.constructors[0] { @@ -228,8 +228,8 @@ func TestAppendEndwareRespectsImmutability(t *testing.T) { } func TestExtendsRespectsImmutability(t *testing.T) { - chain := New(tagMiddleware("")).FinishWith(tagEndware("")) - newChain := New(tagMiddleware("")).FinishWith(tagEndware("")) + chain := New(tagMiddleware("")).Finally(tagEndware("")) + newChain := New(tagMiddleware("")).Finally(tagEndware("")) newChain = chain.Extend(newChain) // chain.constructors[0] should have the same functionality as @@ -254,7 +254,7 @@ func TestExtendsRespectsImmutability(t *testing.T) { func TestExtendAddsHandlersCorrectly(t *testing.T) { chain1 := New(tagMiddleware("t1\n"), tagMiddleware("t2\n")) chain2 := New(tagMiddleware("t3\n"), tagMiddleware("t4\n")). - FinishWith(tagEndware("e1\n"), tagEndware("e2\n")) + Finally(tagEndware("e1\n"), tagEndware("e2\n")) newChain := chain1.Extend(chain2) if len(chain1.constructors) != 2 { @@ -294,7 +294,7 @@ func TestExtendAddsHandlersCorrectly(t *testing.T) { } func TestExtendRespectsImmutability(t *testing.T) { - chain := New(tagMiddleware("")).FinishWith(tagEndware("")) + chain := New(tagMiddleware("")).Finally(tagEndware("")) newChain := chain.Extend(New()) if &chain.constructors[0] == &newChain.constructors[0] { From 28c31d979c4fa6eca9d5413c4d1bceb620d57bbe Mon Sep 17 00:00:00 2001 From: Mohammad Oweis Date: Wed, 25 Mar 2020 17:12:25 -0700 Subject: [PATCH 11/11] refactor: After -> Finally in tests --- chain_test.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/chain_test.go b/chain_test.go index 0d1959b..53af6b4 100644 --- a/chain_test.go +++ b/chain_test.go @@ -57,14 +57,9 @@ func TestNew(t *testing.T) { } } -func TestAfter(t *testing.T) { - e1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("e1\n")) - }) - - e2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("e2\n")) - }) +func TestFinally(t *testing.T) { + e1 := tagEndware("e1\n") + e2 := tagEndware("e2\n") slice := []Endware{e1, e2}