File: mux.go

package info (click to toggle)
golang-github-zenazn-goji 1.0.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 464 kB
  • sloc: makefile: 3
file content (213 lines) | stat: -rw-r--r-- 7,483 bytes parent folder | download | duplicates (3)
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
package web

import (
	"net/http"
)

/*
Mux is an HTTP multiplexer, much like net/http's ServeMux. It functions as both
a middleware stack and as an HTTP router.

Middleware provide a great abstraction for actions that must be performed on
every request, such as request logging and authentication. To append, insert,
and remove middleware, you can call the Use, Insert, and Abandon functions
respectively.

Routes may be added using any of the HTTP verb functions (Get, Post, etc.), or
through the generic Handle function. Goji's routing algorithm is very simple:
routes are processed in the order they are added, and the first matching route
will be executed. Routes match if their HTTP method and Pattern both match.
*/
type Mux struct {
	ms mStack
	rt router
}

// New creates a new Mux without any routes or middleware.
func New() *Mux {
	mux := Mux{
		ms: mStack{
			stack: make([]mLayer, 0),
			pool:  makeCPool(),
		},
		rt: router{
			routes:   make([]route, 0),
			notFound: parseHandler(http.NotFound),
		},
	}
	mux.ms.router = &mux.rt
	return &mux
}

// ServeHTTP processes HTTP requests. Satisfies net/http.Handler.
func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	stack := m.ms.alloc()
	stack.ServeHTTP(w, r)
	m.ms.release(stack)
}

// ServeHTTPC creates a context dependent request with the given Mux. Satisfies
// the Handler interface.
func (m *Mux) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
	stack := m.ms.alloc()
	stack.ServeHTTPC(c, w, r)
	m.ms.release(stack)
}

// Middleware Stack functions

// Use appends the given middleware to the middleware stack.
//
// No attempt is made to enforce the uniqueness of middlewares. It is illegal to
// call this function concurrently with active requests.
func (m *Mux) Use(middleware MiddlewareType) {
	m.ms.Use(middleware)
}

// Insert inserts the given middleware immediately before a given existing
// middleware in the stack. Returns an error if "before" cannot be found in the
// current stack.
//
// No attempt is made to enforce the uniqueness of middlewares. If the insertion
// point is ambiguous, the first (outermost) one is chosen. It is illegal to
// call this function concurrently with active requests.
func (m *Mux) Insert(middleware, before MiddlewareType) error {
	return m.ms.Insert(middleware, before)
}

// Abandon removes the given middleware from the middleware stack. Returns an
// error if no such middleware can be found.
//
// If the name of the middleware to delete is ambiguous, the first (outermost)
// one is chosen. It is illegal to call this function concurrently with active
// requests.
func (m *Mux) Abandon(middleware MiddlewareType) error {
	return m.ms.Abandon(middleware)
}

// Router functions

type routerMiddleware struct {
	m *Mux
	c *C
	h http.Handler
}

func (rm routerMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if rm.c.Env == nil {
		rm.c.Env = make(map[interface{}]interface{}, 1)
	}
	rm.c.Env[MatchKey] = rm.m.rt.getMatch(rm.c, w, r)
	rm.h.ServeHTTP(w, r)
}

/*
Router is a middleware that performs routing and stores the resulting Match in
Goji's environment. If a routing Match is present at the end of the middleware
stack, that Match is used instead of re-routing.

This middleware is especially useful to create post-routing middleware, e.g. a
request logger which prints which pattern or handler was selected, or an
authentication middleware which only applies to certain routes.

If you use nested Muxes with explicit routing, you should be aware that the
explicit routing information set by an outer Mux can be picked up by an inner
Mux, inadvertently causing an infinite routing loop. If you use both explicit
routing and nested Muxes, you should be sure to unset MatchKey before the inner
Mux performs routing (or attach a Router to the inner Mux as well).
*/
func (m *Mux) Router(c *C, h http.Handler) http.Handler {
	return routerMiddleware{m, c, h}
}

/*
Handle dispatches to the given handler when the pattern matches, regardless of
HTTP method.

This method is commonly used to implement sub-routing: an admin application, for
instance, can expose a single handler that is attached to the main Mux by
calling Handle("/admin/*", adminHandler) or similar. Note that this function
doesn't strip this prefix from the path before forwarding it on (e.g., the
handler will see the full path, including the "/admin/" part), but this
functionality can easily be performed by an extra middleware layer.
*/
func (m *Mux) Handle(pattern PatternType, handler HandlerType) {
	m.rt.handleUntyped(pattern, mALL, handler)
}

// Connect dispatches to the given handler when the pattern matches and the HTTP
// method is CONNECT.
func (m *Mux) Connect(pattern PatternType, handler HandlerType) {
	m.rt.handleUntyped(pattern, mCONNECT, handler)
}

// Delete dispatches to the given handler when the pattern matches and the HTTP
// method is DELETE.
func (m *Mux) Delete(pattern PatternType, handler HandlerType) {
	m.rt.handleUntyped(pattern, mDELETE, handler)
}

// Get dispatches to the given handler when the pattern matches and the HTTP
// method is GET.
//
// All GET handlers also transparently serve HEAD requests, since net/http will
// take care of all the fiddly bits for you. If you wish to provide an alternate
// implementation of HEAD, you should add a handler explicitly and place it
// above your GET handler.
func (m *Mux) Get(pattern PatternType, handler HandlerType) {
	m.rt.handleUntyped(pattern, mGET|mHEAD, handler)
}

// Head dispatches to the given handler when the pattern matches and the HTTP
// method is HEAD.
func (m *Mux) Head(pattern PatternType, handler HandlerType) {
	m.rt.handleUntyped(pattern, mHEAD, handler)
}

// Options dispatches to the given handler when the pattern matches and the HTTP
// method is OPTIONS.
func (m *Mux) Options(pattern PatternType, handler HandlerType) {
	m.rt.handleUntyped(pattern, mOPTIONS, handler)
}

// Patch dispatches to the given handler when the pattern matches and the HTTP
// method is PATCH.
func (m *Mux) Patch(pattern PatternType, handler HandlerType) {
	m.rt.handleUntyped(pattern, mPATCH, handler)
}

// Post dispatches to the given handler when the pattern matches and the HTTP
// method is POST.
func (m *Mux) Post(pattern PatternType, handler HandlerType) {
	m.rt.handleUntyped(pattern, mPOST, handler)
}

// Put dispatches to the given handler when the pattern matches and the HTTP
// method is PUT.
func (m *Mux) Put(pattern PatternType, handler HandlerType) {
	m.rt.handleUntyped(pattern, mPUT, handler)
}

// Trace dispatches to the given handler when the pattern matches and the HTTP
// method is TRACE.
func (m *Mux) Trace(pattern PatternType, handler HandlerType) {
	m.rt.handleUntyped(pattern, mTRACE, handler)
}

// NotFound sets the fallback (i.e., 404) handler for this mux.
//
// As a convenience, the context environment variable "goji.web.validMethods"
// (also available as the constant ValidMethodsKey) will be set to the list of
// HTTP methods that could have been routed had they been provided on an
// otherwise identical request.
func (m *Mux) NotFound(handler HandlerType) {
	m.rt.notFound = parseHandler(handler)
}

// Compile compiles the list of routes into bytecode. This only needs to be done
// once after all the routes have been added, and will be called automatically
// for you (at some performance cost on the first request) if you do not call it
// explicitly.
func (m *Mux) Compile() {
	m.rt.compile()
}