-
Notifications
You must be signed in to change notification settings - Fork 43
/
router.go
150 lines (119 loc) · 3.19 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
package neo
import (
"container/list"
"errors"
"github.com/ivpusic/urlregex"
)
///////////////////////////////////////////////////////////////////
// Route
///////////////////////////////////////////////////////////////////
type handler func(*Ctx) (int, error)
// composing route with middlewares. Will be called from ``compose`` fn.
func (h handler) apply(ctx *Ctx, fns []appliable, current int) {
status, err := h(ctx)
if err != nil {
log.Errorf("Error returned from route handler. %s", err.Error())
ctx.Res.Status = 500
} else {
ctx.Res.Status = status
}
current++
if len(fns) > current {
fns[current].apply(ctx, fns, current)
}
}
type Route struct {
*interceptor
fn handler
regex urlregex.UrlRegex
fnChain func(*Ctx)
path string
}
///////////////////////////////////////////////////////////////////
// Router
///////////////////////////////////////////////////////////////////
type router struct {
*interceptor
*methods
regions *list.List
}
type Region struct {
*interceptor
*methods
}
func (r *Region) Prefix(prefix string) *Region {
r.methods.prefix = prefix
return r
}
func (r *router) initRouter() {
m := &methods{}
i := &interceptor{[]appliable{}}
r.methods = m.init()
r.interceptor = i
r.regions = list.New()
}
// making new region instance
func (r *router) makeRegion() *Region {
m := &methods{}
i := &interceptor{[]appliable{}}
region := &Region{i, m.init()}
r.regions.PushBack(region)
return region
}
// forcing all middlewares to copy to appropriate routes
// also cleans up intrnal structures
func (router *router) flush() {
// first copy all middlewares to top-level routes
for method, routesSlice := range router.routes {
for i, route := range routesSlice {
route.fnChain = compose(merge(
router.middlewares,
route.middlewares,
[]appliable{route.fn},
))
router.routes[method][i] = route
}
}
// then copy all middlewares for region level routes
// this will copy all b/a from router and region
// so at the end all b/a for route will be placed directly in route
//
// mr = maybeRegion
for mr := router.regions.Front(); mr != nil; mr = mr.Next() {
region, ok := mr.Value.(*Region)
if !ok {
log.Error("cannot convert element to region")
continue
}
for method, routesSlice := range region.routes {
for _, route := range routesSlice {
route.fnChain = compose(merge(
router.middlewares,
region.middlewares,
route.middlewares,
[]appliable{route.fn},
))
router.routes[method] = append(router.routes[method], route)
}
// remove method key from region (GET, POST,...)
delete(region.routes, method)
}
}
// remove regions, we don't need them anymore
router.regions.Init()
}
// main API function of router
// if will try to invoke fns which are realted to route (if any)
func (router *router) match(req *Request) (*Route, error) {
// todo: consider using goroutines for searching for route
method := req.Method
path := req.URL.Path
for _, route := range router.routes[method] {
params, err := route.regex.Match(path)
if err == nil {
req.Params = params
return &route, nil
}
}
return nil, errors.New("[" + req.Method + "] handler for route " + path + " not found")
}