1
// Copyright 2013 Julien Schmidt. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be found
3
// in the LICENSE file.
5
// Package httprouter is a trie based high performance HTTP request router.
7
// A trivial example is:
13
// "github.com/julienschmidt/httprouter"
18
// func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
19
// fmt.Fprint(w, "Welcome!\n")
22
// func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
23
// fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
27
// router := httprouter.New()
28
// router.GET("/", Index)
29
// router.GET("/hello/:name", Hello)
31
// log.Fatal(http.ListenAndServe(":8080", router))
34
// The router matches incoming requests by the request method and the path.
35
// If a handle is registered for this path and method, the router delegates the
36
// request to that function.
37
// For the methods GET, POST, PUT, PATCH and DELETE shortcut functions exist to
38
// register handles, for all other methods router.Handle can be used.
40
// The registered path, against which the router matches incoming requests, can
41
// contain two types of parameters:
43
// :name named parameter
44
// *name catch-all parameter
46
// Named parameters are dynamic path segments. They match anything until the
47
// next '/' or the path end:
48
// Path: /blog/:category/:post
51
// /blog/go/request-routers match: category="go", post="request-routers"
52
// /blog/go/request-routers/ no match, but the router would redirect
54
// /blog/go/request-routers/comments no match
56
// Catch-all parameters match anything until the path end, including the
57
// directory index (the '/' before the catch-all). Since they match anything
58
// until the end, catch-all parameters must always be the final path element.
59
// Path: /files/*filepath
62
// /files/ match: filepath="/"
63
// /files/LICENSE match: filepath="/LICENSE"
64
// /files/templates/article.html match: filepath="/templates/article.html"
65
// /files no match, but the router would redirect
67
// The value of parameters is saved as a slice of the Param struct, consisting
68
// each of a key and a value. The slice is passed to the Handle func as a third
70
// There are two ways to retrieve the value of a parameter:
71
// // by the name of the parameter
72
// user := ps.ByName("user") // defined by :user or *user
74
// // by the index of the parameter. This way you can also get the name (key)
75
// thirdKey := ps[2].Key // the name of the 3rd parameter
76
// thirdValue := ps[2].Value // the value of the 3rd parameter
83
// Handle is a function that can be registered to a route to handle HTTP
84
// requests. Like http.HandlerFunc, but has a third parameter for the values of
85
// wildcards (variables).
86
type Handle func(http.ResponseWriter, *http.Request, Params)
88
// Param is a single URL parameter, consisting of a key and a value.
94
// Params is a Param-slice, as returned by the router.
95
// The slice is ordered, the first URL parameter is also the first slice value.
96
// It is therefore safe to read values by the index.
99
// ByName returns the value of the first Param which key matches the given name.
100
// If no matching Param is found, an empty string is returned.
101
func (ps Params) ByName(name string) string {
103
if ps[i].Key == name {
110
// Router is a http.Handler which can be used to dispatch requests to different
111
// handler functions via configurable routes
113
trees map[string]*node
115
// Enables automatic redirection if the current route can't be matched but a
116
// handler for the path with (without) the trailing slash exists.
117
// For example if /foo/ is requested but a route only exists for /foo, the
118
// client is redirected to /foo with http status code 301 for GET requests
119
// and 307 for all other request methods.
120
RedirectTrailingSlash bool
122
// If enabled, the router tries to fix the current request path, if no
123
// handle is registered for it.
124
// First superfluous path elements like ../ or // are removed.
125
// Afterwards the router does a case-insensitive lookup of the cleaned path.
126
// If a handle can be found for this route, the router makes a redirection
127
// to the corrected path with status code 301 for GET requests and 307 for
128
// all other request methods.
129
// For example /FOO and /..//Foo could be redirected to /foo.
130
// RedirectTrailingSlash is independent of this option.
131
RedirectFixedPath bool
133
// If enabled, the router checks if another method is allowed for the
134
// current route, if the current request can not be routed.
135
// If this is the case, the request is answered with 'Method Not Allowed'
136
// and HTTP status code 405.
137
// If no other Method is allowed, the request is delegated to the NotFound
139
HandleMethodNotAllowed bool
141
// Configurable http.Handler which is called when no matching route is
142
// found. If it is not set, http.NotFound is used.
143
NotFound http.Handler
145
// Configurable http.Handler which is called when a request
146
// cannot be routed and HandleMethodNotAllowed is true.
147
// If it is not set, http.Error with http.StatusMethodNotAllowed is used.
148
MethodNotAllowed http.Handler
150
// Function to handle panics recovered from http handlers.
151
// It should be used to generate a error page and return the http error code
152
// 500 (Internal Server Error).
153
// The handler can be used to keep your server from crashing because of
154
// unrecovered panics.
155
PanicHandler func(http.ResponseWriter, *http.Request, interface{})
158
// Make sure the Router conforms with the http.Handler interface
159
var _ http.Handler = New()
161
// New returns a new initialized Router.
162
// Path auto-correction, including trailing slashes, is enabled by default.
165
RedirectTrailingSlash: true,
166
RedirectFixedPath: true,
167
HandleMethodNotAllowed: true,
171
// GET is a shortcut for router.Handle("GET", path, handle)
172
func (r *Router) GET(path string, handle Handle) {
173
r.Handle("GET", path, handle)
176
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
177
func (r *Router) HEAD(path string, handle Handle) {
178
r.Handle("HEAD", path, handle)
181
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
182
func (r *Router) OPTIONS(path string, handle Handle) {
183
r.Handle("OPTIONS", path, handle)
186
// POST is a shortcut for router.Handle("POST", path, handle)
187
func (r *Router) POST(path string, handle Handle) {
188
r.Handle("POST", path, handle)
191
// PUT is a shortcut for router.Handle("PUT", path, handle)
192
func (r *Router) PUT(path string, handle Handle) {
193
r.Handle("PUT", path, handle)
196
// PATCH is a shortcut for router.Handle("PATCH", path, handle)
197
func (r *Router) PATCH(path string, handle Handle) {
198
r.Handle("PATCH", path, handle)
201
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
202
func (r *Router) DELETE(path string, handle Handle) {
203
r.Handle("DELETE", path, handle)
206
// Handle registers a new request handle with the given path and method.
208
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
209
// functions can be used.
211
// This function is intended for bulk loading and to allow the usage of less
212
// frequently used, non-standardized or custom methods (e.g. for internal
213
// communication with a proxy).
214
func (r *Router) Handle(method, path string, handle Handle) {
216
panic("path must begin with '/' in path '" + path + "'")
220
r.trees = make(map[string]*node)
223
root := r.trees[method]
226
r.trees[method] = root
229
root.addRoute(path, handle)
232
// Handler is an adapter which allows the usage of an http.Handler as a
234
func (r *Router) Handler(method, path string, handler http.Handler) {
235
r.Handle(method, path,
236
func(w http.ResponseWriter, req *http.Request, _ Params) {
237
handler.ServeHTTP(w, req)
242
// HandlerFunc is an adapter which allows the usage of an http.HandlerFunc as a
244
func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
245
r.Handler(method, path, handler)
248
// ServeFiles serves files from the given file system root.
249
// The path must end with "/*filepath", files are then served from the local
250
// path /defined/root/dir/*filepath.
251
// For example if root is "/etc" and *filepath is "passwd", the local file
252
// "/etc/passwd" would be served.
253
// Internally a http.FileServer is used, therefore http.NotFound is used instead
254
// of the Router's NotFound handler.
255
// To use the operating system's file system implementation,
257
// router.ServeFiles("/src/*filepath", http.Dir("/var/www"))
258
func (r *Router) ServeFiles(path string, root http.FileSystem) {
259
if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
260
panic("path must end with /*filepath in path '" + path + "'")
263
fileServer := http.FileServer(root)
265
r.GET(path, func(w http.ResponseWriter, req *http.Request, ps Params) {
266
req.URL.Path = ps.ByName("filepath")
267
fileServer.ServeHTTP(w, req)
271
func (r *Router) recv(w http.ResponseWriter, req *http.Request) {
272
if rcv := recover(); rcv != nil {
273
r.PanicHandler(w, req, rcv)
277
// Lookup allows the manual lookup of a method + path combo.
278
// This is e.g. useful to build a framework around this router.
279
// If the path was found, it returns the handle function and the path parameter
280
// values. Otherwise the third return value indicates whether a redirection to
281
// the same path with an extra / without the trailing slash should be performed.
282
func (r *Router) Lookup(method, path string) (Handle, Params, bool) {
283
if root := r.trees[method]; root != nil {
284
return root.getValue(path)
286
return nil, nil, false
289
// ServeHTTP makes the router implement the http.Handler interface.
290
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
291
if r.PanicHandler != nil {
295
if root := r.trees[req.Method]; root != nil {
298
if handle, ps, tsr := root.getValue(path); handle != nil {
301
} else if req.Method != "CONNECT" && path != "/" {
302
code := 301 // Permanent redirect, request with GET method
303
if req.Method != "GET" {
304
// Temporary redirect, request with same method
305
// As of Go 1.3, Go does not support status code 308.
309
if tsr && r.RedirectTrailingSlash {
310
if len(path) > 1 && path[len(path)-1] == '/' {
311
req.URL.Path = path[:len(path)-1]
313
req.URL.Path = path + "/"
315
http.Redirect(w, req, req.URL.String(), code)
319
// Try to fix the request path
320
if r.RedirectFixedPath {
321
fixedPath, found := root.findCaseInsensitivePath(
323
r.RedirectTrailingSlash,
326
req.URL.Path = string(fixedPath)
327
http.Redirect(w, req, req.URL.String(), code)
335
if r.HandleMethodNotAllowed {
336
for method := range r.trees {
337
// Skip the requested method - we already tried this one
338
if method == req.Method {
342
handle, _, _ := r.trees[method].getValue(req.URL.Path)
344
if r.MethodNotAllowed != nil {
345
r.MethodNotAllowed.ServeHTTP(w, req)
348
http.StatusText(http.StatusMethodNotAllowed),
349
http.StatusMethodNotAllowed,
358
if r.NotFound != nil {
359
r.NotFound.ServeHTTP(w, req)
361
http.NotFound(w, req)