1
// Package pat implements a simple URL pattern muxer
10
// PatternServeMux is an HTTP request multiplexer. It matches the URL of each
11
// incoming request against a list of registered patterns with their associated
12
// methods and calls the handler for the pattern that most closely matches the
15
// Pattern matching attempts each pattern in the order in which they were
18
// Patterns may contain literals or captures. Capture names start with a colon
19
// and consist of letters A-Z, a-z, _, and 0-9. The rest of the pattern
20
// matches literally. The portion of the URL matching each name ends with an
21
// occurrence of the character in the pattern immediately following the name,
22
// or a /, whichever comes first. It is possible for a name to match the empty
25
// Example pattern with one capture:
47
// A pattern ending with a slash will add an implicit redirect for its non-slash
48
// version. For example: Get("/foo/", handler) also registers
49
// Get("/foo", handler) as a redirect. You may override it by registering
50
// Get("/foo", anotherhandler) before the slash version.
52
// Retrieve the capture from the r.URL.Query().Get(":name") in a handler (note
53
// the colon). If a capture name appears more than once, the additional values
54
// are appended to the previous values (see
55
// http://golang.org/pkg/net/url/#Values)
57
// A trivial example server is:
64
// "github.com/bmizerany/pat"
68
// // hello world, the web server
69
// func HelloServer(w http.ResponseWriter, req *http.Request) {
70
// io.WriteString(w, "hello, "+req.URL.Query().Get(":name")+"!\n")
75
// m.Get("/hello/:name", http.HandlerFunc(HelloServer))
77
// // Register this pat with the default serve mux so that other packages
78
// // may also be exported. (i.e. /debug/pprof/*)
79
// http.Handle("/", m)
80
// err := http.ListenAndServe(":12345", nil)
82
// log.Fatal("ListenAndServe: ", err)
86
// When "Method Not Allowed":
88
// Pat knows what methods are allowed given a pattern and a URI. For
89
// convenience, PatternServeMux will add the Allow header for requests that
90
// match a pattern for a method other than the method requested and set the
91
// Status to "405 Method Not Allowed".
93
// If the NotFound handler is set, then it is used whenever the pattern doesn't
94
// match the request path for the current method (and the Allow header is not
96
type PatternServeMux struct {
97
// NotFound, if set, is used whenever the request doesn't match any
98
// pattern for its method. NotFound should be set before serving any
100
NotFound http.Handler
101
handlers map[string][]*patHandler
104
// New returns a new PatternServeMux.
105
func New() *PatternServeMux {
106
return &PatternServeMux{handlers: make(map[string][]*patHandler)}
109
// ServeHTTP matches r.URL.Path against its routing table using the rules
111
func (p *PatternServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
112
for _, ph := range p.handlers[r.Method] {
113
if params, ok := ph.try(r.URL.Path); ok {
114
if len(params) > 0 && !ph.redirect {
115
r.URL.RawQuery = url.Values(params).Encode() + "&" + r.URL.RawQuery
122
if p.NotFound != nil {
123
p.NotFound.ServeHTTP(w, r)
127
allowed := make([]string, 0, len(p.handlers))
128
for meth, handlers := range p.handlers {
129
if meth == r.Method {
133
for _, ph := range handlers {
134
if _, ok := ph.try(r.URL.Path); ok {
135
allowed = append(allowed, meth)
140
if len(allowed) == 0 {
145
w.Header().Add("Allow", strings.Join(allowed, ", "))
146
http.Error(w, "Method Not Allowed", 405)
149
// Head will register a pattern with a handler for HEAD requests.
150
func (p *PatternServeMux) Head(pat string, h http.Handler) {
151
p.Add("HEAD", pat, h)
154
// Get will register a pattern with a handler for GET requests.
155
// It also registers pat for HEAD requests. If this needs to be overridden, use
156
// Head before Get with pat.
157
func (p *PatternServeMux) Get(pat string, h http.Handler) {
158
p.Add("HEAD", pat, h)
162
// Post will register a pattern with a handler for POST requests.
163
func (p *PatternServeMux) Post(pat string, h http.Handler) {
164
p.Add("POST", pat, h)
167
// Put will register a pattern with a handler for PUT requests.
168
func (p *PatternServeMux) Put(pat string, h http.Handler) {
172
// Del will register a pattern with a handler for DELETE requests.
173
func (p *PatternServeMux) Del(pat string, h http.Handler) {
174
p.Add("DELETE", pat, h)
177
// Options will register a pattern with a handler for OPTIONS requests.
178
func (p *PatternServeMux) Options(pat string, h http.Handler) {
179
p.Add("OPTIONS", pat, h)
182
// Patch will register a pattern with a handler for PATCH requests.
183
func (p *PatternServeMux) Patch(pat string, h http.Handler) {
184
p.Add("PATCH", pat, h)
187
// Add will register a pattern with a handler for meth requests.
188
func (p *PatternServeMux) Add(meth, pat string, h http.Handler) {
189
p.add(meth, pat, h, false)
192
func (p *PatternServeMux) add(meth, pat string, h http.Handler, redirect bool) {
193
handlers := p.handlers[meth]
194
for _, p1 := range handlers {
196
return // found existing pattern; do nothing
199
handler := &patHandler{
204
p.handlers[meth] = append(handlers, handler)
207
if n > 0 && pat[n-1] == '/' {
208
p.add(meth, pat[:n-1], http.HandlerFunc(addSlashRedirect), true)
212
func addSlashRedirect(w http.ResponseWriter, r *http.Request) {
215
http.Redirect(w, r, u.String(), http.StatusMovedPermanently)
218
// Tail returns the trailing string in path after the final slash for a pat ending with a slash.
222
// Tail("/hello/:title/", "/hello/mr/mizerany") == "mizerany"
223
// Tail("/:a/", "/x/y/z") == "y/z"
225
func Tail(pat, path string) string {
230
if pat[len(pat)-1] == '/' {
236
_, nextc, j = match(pat, isAlnum, j+1)
237
_, _, i = match(path, matchPart(nextc), i)
238
case path[i] == pat[j]:
248
type patHandler struct {
254
func (ph *patHandler) try(path string) (url.Values, bool) {
255
p := make(url.Values)
259
case j >= len(ph.pat):
260
if ph.pat != "/" && len(ph.pat) > 0 && ph.pat[len(ph.pat)-1] == '/' {
264
case ph.pat[j] == ':':
267
name, nextc, j = match(ph.pat, isAlnum, j+1)
268
val, _, i = match(path, matchPart(nextc), i)
270
case path[i] == ph.pat[j]:
277
if j != len(ph.pat) {
283
func matchPart(b byte) func(byte) bool {
284
return func(c byte) bool {
285
return c != b && c != '/'
289
func match(s string, f func(byte) bool, i int) (matched string, next byte, j int) {
291
for j < len(s) && f(s[j]) {
297
return s[i:j], next, j
300
func isAlpha(ch byte) bool {
301
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
304
func isDigit(ch byte) bool {
305
return '0' <= ch && ch <= '9'
308
func isAlnum(ch byte) bool {
309
return isAlpha(ch) || isDigit(ch)