-
Notifications
You must be signed in to change notification settings - Fork 0
/
router.go
96 lines (82 loc) · 2.37 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
package handlr
import (
"net/http"
"path"
"strings"
)
// A router instance will house paths and handlers.
// It will also keep track of their hierarchy.
//
// Ideally, you'd like to distribute your handlers
// into separate files and plugin them in as Routes.
type Router struct {
path string
parent *Router
children []*Router
handler *ActionHandlerFunc
}
func (rt *Router) Route(path string, routeHandler RouteHandler) {
rt.RouteFunc(path, routeHandler.RouteHTTP)
}
// Allows RouteFunc registration.
// You don't program behavior through this method.
func (rt *Router) RouteFunc(path string, routeHandlerFunc RouteHandlerFunc) {
router := &Router{path: path, parent: rt}
routeHandlerFunc(router)
rt.children = append(rt.children, router)
}
func (rt *Router) Handle(path string, actionHandler http.Handler) {
rt.HandleFunc(path, actionHandler.ServeHTTP)
}
// Allows HandlerFunc registration which gives you the ability
// to tie a behavior to a path.
// i.e. Get a record from database by hiting URL:
// http://example.org/get/record/1
func (rt *Router) HandleFunc(path string, actionHandlerFunc ActionHandlerFunc) {
router := &Router{path: path, parent: rt, handler: &actionHandlerFunc}
rt.children = append(rt.children, router)
}
func (rt *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if handler := rt.findHandler(r); handler != nil {
(*handler)(w, r)
return
}
http.NotFound(w, r)
}
// It attempts to produce Router's endpoint path
// based on parent and children routes.
func (rt *Router) buildPath() string {
if rt.parent == nil {
return rt.path
}
return path.Join(rt.parent.buildPath(), trimSlash(rt.path))
}
// Recursively find a handler to the request
func (rt *Router) findHandler(r *http.Request) *ActionHandlerFunc {
for _, v := range rt.children {
if v.handler != nil && v.isMatch(r) {
return v.handler
}
x := v.findHandler(r)
if x != nil {
return x
}
}
return nil
}
// Asserts if Router path matches URL from Request
func (rt *Router) isMatch(r *http.Request) bool {
rp := strings.Split(trimSlash(rt.buildPath()), "/")
ep := strings.Split(trimSlash(r.URL.Path), "/")
if len(rp) == len(ep) {
m := true
for i := 0; i < len(rp); i++ {
rs := strings.ToLower(rp[i])
es := strings.ToLower(ep[i])
// Very basic slug matching. Regex later.
m = m && rs == es || (rs[0] == ':' && es != "")
}
return m
}
return false
}