forked from stephens2424/muxchain
-
Notifications
You must be signed in to change notification settings - Fork 0
/
muxchain.go
136 lines (115 loc) · 4.1 KB
/
muxchain.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
package muxchain
import "net/http"
var Default = &MuxChain{}
// Chain registers the pattern and http.Handler chain to the DefaultMuxChain.
func Chain(pattern string, handlers ...http.Handler) {
Default.Chain(pattern, handlers...)
}
// ChainHandlers chains together a set of handlers under an empty path.
func ChainHandlers(handlers ...http.Handler) http.Handler {
m := &MuxChain{}
m.Chain("/", handlers...)
return m
}
type MuxChain struct {
Muxer
}
func (m *MuxChain) ServeHTTPChain(w http.ResponseWriter, req *http.Request, handlers ...http.Handler) {
HandleChain(w, req, handlers...)
}
// Chain registers a pattern to a sequence of http.Handlers. Upon receiving a request,
// the mux chain will find the best matching pattern and call that chain of handlers.
// The handlers will be called in turn until one of them writes a response or the end
// of the chain is reached. If one of the handlers is a Muxer (e.g. http.ServeMux),
// the MuxChain will skip it if no pattern matches.
func (m *MuxChain) Chain(pattern string, handlers ...http.Handler) {
if m.Muxer == nil {
m.Muxer = http.NewServeMux()
}
m.HandleFunc(pattern, func(w http.ResponseWriter, req *http.Request) {
if len(handlers) == 0 {
http.NotFound(w, req)
}
HandleChain(w, req, handlers...)
})
}
// HandleChain is the utility function chained handlers are responsible for calling
// when they are complete.
func HandleChain(w http.ResponseWriter, req *http.Request, handlers ...http.Handler) {
for i, h := range handlers {
var remaining []http.Handler
if len(handlers) > i+1 {
remaining = handlers[i+1:]
}
if handle(h, remaining, w, req) {
return
}
}
}
// ChainedHander allows implementers to call the handlers after them in a muxchain
// on their own. This allows handlers to defer functions until after the handler
// chain following them has been completed.
type ChainedHandler interface {
http.Handler
// ServeHTTPChain allows the handler to do its work and then call the remaining
// handler chain. Implementers should call muxchain.HandleChain when complete.
ServeHTTPChain(w http.ResponseWriter, req *http.Request, h ...http.Handler)
}
// ChainedHandlerFunc represents a handler that is able to be chained to subsequent handlers.
type ChainedHandlerFunc func(w http.ResponseWriter, req *http.Request, handlers ...http.Handler)
// ServeHTTP allows ChainedHandlerFuncs to implement http.Handler
func (c ChainedHandlerFunc) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c(w, req, nil)
}
// ServeHTTPChain allows ChainedHandlerFuncs to be identified
func (c ChainedHandlerFunc) ServeHTTPChain(w http.ResponseWriter, req *http.Request, handlers ...http.Handler) {
c(w, req, handlers...)
}
// handle runs and attempts to serve on the current handler. It returns true if data
// was written to the response writer.
func handle(h http.Handler, remaining []http.Handler, w http.ResponseWriter, req *http.Request) bool {
// Is the current handler a Muxer?
if childMux, ok := h.(Muxer); ok {
_, p := childMux.Handler(req)
// Ignore this handler if this ServeMux doesn't apply, unless we have no more handlers
if p == "" && len(remaining) == 0 {
return true
}
}
var cw CheckedResponseWriter
var ok bool
if cw, ok = w.(CheckedResponseWriter); !ok {
cw = &checked{w, false}
}
if chainedHandler, ok := h.(ChainedHandler); ok {
chainedHandler.ServeHTTPChain(cw, req, remaining...)
return true
}
// Serve for the current handler
h.ServeHTTP(cw, req)
return cw.Written()
}
// Muxer identifies types that act as a ServeMux.
type Muxer interface {
http.Handler
Handle(pattern string, handler http.Handler)
Handler(r *http.Request) (h http.Handler, pattern string)
HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
}
// CheckedResponseWriter augments http.ResponseWriter to indicate if the response has been
// written to.
type CheckedResponseWriter interface {
http.ResponseWriter
Written() bool
}
type checked struct {
http.ResponseWriter
written bool
}
func (c *checked) Write(p []byte) (int, error) {
c.written = true
return c.ResponseWriter.Write(p)
}
func (c *checked) Written() bool {
return c.written
}