forked from docbobo/madhatter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmadhatter.go
149 lines (124 loc) · 4.32 KB
/
madhatter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package madhatter
import (
"net/http"
"golang.org/x/net/context"
)
// Handler provides an interface similar to http.Handler but supporting
// pass-through of context
type Handler interface {
ServeHTTP(context.Context, http.ResponseWriter, *http.Request)
}
// The HandlerFunc type is an adapter to allow the use of ordinary functions
// as HTTP handlers supporting context.
type HandlerFunc func(context.Context, http.ResponseWriter, *http.Request)
// ServeHTTP calls f(ctx, w, r).
func (f HandlerFunc) ServeHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request) {
f(ctx, w, r)
}
// A Constructor for a piece of middleware.
type Constructor func(Handler) Handler
// Chain acts as list of Handlers.
// Chain is effectively immutable: once created, it will always hold
// the same set of constructors in the same order.
type Chain struct {
constructors []Constructor
finalize func(Handler) http.Handler
}
type NegroniHandler interface {
ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
// The NegroniHandlerFunc should match the Negroni handler signature
type NegroniHandlerFunc func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
// ServeHTTP calls f(ctx, w, r)
func (f NegroniHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
f(w, r, next)
}
// Adapt can be used to adapt existing Negroni handlers so that they can be properly integrated
// into the middleware chain. They can appear anywhere in the chain and context will be passed
// properly.
func Adapt(constructorHandler func() NegroniHandler) Constructor {
return func(next Handler) Handler {
h := constructorHandler()
return HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r, func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(ctx, w, r)
})
})
}
}
// AdaptInstance returns a constructor that will always return the same Negroni handler. This
// should be safe most of the times as Negroni doesn't setup its chains in a static way.
func AdaptInstance(fn NegroniHandler) Constructor {
return Adapt(func() NegroniHandler {
return fn
})
}
// New creates a new Chain, memorizing the given list of middleware constructors.
// Constructors will not be called until calling Then() or ThenFunc()
func New(constructors ...Constructor) Chain {
c := Chain{
finalize: createRootHandler,
}
c.constructors = append(c.constructors, constructors...)
return c
}
// Then chains the middleware and returns the final http.Handler (!)
// New(m1, m2, m3).Then(h)
// is equivalent to:
// m1(m2(m3(h)))
//
// A Chain can be safely reused by calling Then() several times.
// Note that constructors will be called on every call to Then() and thus
// several instances of the same middleware will be created when a Chain
// is reused.
//
// Then() treats nil as http.DefaultServeMux.
func (c Chain) Then(h Handler) http.Handler {
var final Handler
if h != nil {
final = h
} else {
final = adaptFinal(http.DefaultServeMux)
}
for i := len(c.constructors) - 1; i >= 0; i-- {
final = c.constructors[i](final)
}
return c.finalize(final)
}
// ThenFunc works identically to Then(), but takes a HandlerFunc instead of
// a Handler.
//
// The following to statements are equivalent:
// c.Then(HandlerFunc(fn))
// c.ThenFunc(fn)
func (c Chain) ThenFunc(fn HandlerFunc) http.Handler {
if fn == nil {
return c.Then(nil)
}
return c.Then(HandlerFunc(fn))
}
// Append extends a Chain, adding the specified constructors at the end of the Chain.
// Append returns a new chain, leaving the original one untouched.
func (c Chain) Append(constructors ...Constructor) Chain {
newCons := make([]Constructor, len(c.constructors)+len(constructors))
copy(newCons, c.constructors)
copy(newCons[len(c.constructors):], constructors)
newChain := New(newCons...)
return newChain
}
func createRootHandler(h Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var (
ctx context.Context
cancel context.CancelFunc
)
ctx, cancel = context.WithCancel(context.Background())
defer cancel() // cancel context as soon as the request returns
h.ServeHTTP(ctx, w, r)
})
}
func adaptFinal(h http.Handler) Handler {
return HandlerFunc(func(_ context.Context, w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r)
})
}