forked from qiangxue/fasthttp-routing
-
Notifications
You must be signed in to change notification settings - Fork 0
/
router.go
169 lines (151 loc) · 4.55 KB
/
router.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package routing provides high performance and powerful HTTP routing capabilities.
package routing
import (
"net/http"
"sort"
"strings"
"sync"
"github.com/valyala/fasthttp"
)
type (
// Handler is the function for handling HTTP requests.
Handler func(*Context) error
// Router manages routes and dispatches HTTP requests to the handlers of the matching routes.
Router struct {
RouteGroup
pool sync.Pool
routes map[string]*Route
stores map[string]routeStore
maxParams int
notFound []Handler
notFoundHandlers []Handler
}
// routeStore stores route paths and the corresponding handlers.
routeStore interface {
Add(key string, data interface{}) int
Get(key string, pvalues []string) (data interface{}, pnames []string)
String() string
}
)
// Methods lists all supported HTTP methods by Router.
var Methods = []string{
"CONNECT",
"DELETE",
"GET",
"HEAD",
"OPTIONS",
"PATCH",
"POST",
"PUT",
"TRACE",
}
// New creates a new Router object.
func New() *Router {
r := &Router{
routes: make(map[string]*Route),
stores: make(map[string]routeStore),
}
r.RouteGroup = *newRouteGroup("", r, make([]Handler, 0))
r.NotFound(MethodNotAllowedHandler, NotFoundHandler)
r.pool.New = func() interface{} {
return &Context{
pvalues: make([]string, r.maxParams),
router: r,
}
}
return r
}
// HandleRequest handles the HTTP request.
func (r *Router) HandleRequest(ctx *fasthttp.RequestCtx) {
c := r.pool.Get().(*Context)
c.init(ctx)
c.handlers, c.pnames = r.find(string(ctx.Method()), string(ctx.Path()), c.pvalues)
if err := c.Next(); err != nil {
r.handleError(c, err)
}
r.pool.Put(c)
}
// Route returns the named route.
// Nil is returned if the named route cannot be found.
func (r *Router) Route(name string) *Route {
return r.routes[name]
}
// Use appends the specified handlers to the router and shares them with all routes.
func (r *Router) Use(handlers ...Handler) {
r.RouteGroup.Use(handlers...)
r.notFoundHandlers = combineHandlers(r.handlers, r.notFound)
}
// NotFound specifies the handlers that should be invoked when the router cannot find any route matching a request.
// Note that the handlers registered via Use will be invoked first in this case.
func (r *Router) NotFound(handlers ...Handler) {
r.notFound = handlers
r.notFoundHandlers = combineHandlers(r.handlers, r.notFound)
}
// handleError is the error handler for handling any unhandled errors.
func (r *Router) handleError(c *Context, err error) {
if httpError, ok := err.(HTTPError); ok {
c.Error(httpError.Error(), httpError.StatusCode())
} else {
c.Error(err.Error(), http.StatusInternalServerError)
}
}
func (r *Router) add(method, path string, handlers []Handler) {
store := r.stores[method]
if store == nil {
store = newStore()
r.stores[method] = store
}
if n := store.Add(path, handlers); n > r.maxParams {
r.maxParams = n
}
}
func (r *Router) find(method, path string, pvalues []string) (handlers []Handler, pnames []string) {
var hh interface{}
if store := r.stores[method]; store != nil {
hh, pnames = store.Get(path, pvalues)
}
if hh != nil {
return hh.([]Handler), pnames
}
return r.notFoundHandlers, pnames
}
func (r *Router) findAllowedMethods(path string) map[string]bool {
methods := make(map[string]bool)
pvalues := make([]string, r.maxParams)
for m, store := range r.stores {
if handlers, _ := store.Get(path, pvalues); handlers != nil {
methods[m] = true
}
}
return methods
}
// NotFoundHandler returns a 404 HTTP error indicating a request has no matching route.
func NotFoundHandler(*Context) error {
return NewHTTPError(http.StatusNotFound)
}
// MethodNotAllowedHandler handles the situation when a request has matching route without matching HTTP method.
// In this case, the handler will respond with an Allow HTTP header listing the allowed HTTP methods.
// Otherwise, the handler will do nothing and let the next handler (usually a NotFoundHandler) to handle the problem.
func MethodNotAllowedHandler(c *Context) error {
methods := c.Router().findAllowedMethods(string(c.Path()))
if len(methods) == 0 {
return nil
}
methods["OPTIONS"] = true
ms := make([]string, len(methods))
i := 0
for method := range methods {
ms[i] = method
i++
}
sort.Strings(ms)
c.Response.Header.Set("Allow", strings.Join(ms, ", "))
if string(c.Method()) != "OPTIONS" {
c.Response.SetStatusCode(http.StatusMethodNotAllowed)
}
c.Abort()
return nil
}