1 package restful
2
3
4
5
6
7 import (
8 "net/http"
9 "regexp"
10 "sort"
11 "strings"
12 )
13
14
15 type CurlyRouter struct{}
16
17
18
19 func (c CurlyRouter) SelectRoute(
20 webServices []*WebService,
21 httpRequest *http.Request) (selectedService *WebService, selected *Route, err error) {
22
23 requestTokens := tokenizePath(httpRequest.URL.Path)
24
25 detectedService := c.detectWebService(requestTokens, webServices)
26 if detectedService == nil {
27 if trace {
28 traceLogger.Printf("no WebService was found to match URL path:%s\n", httpRequest.URL.Path)
29 }
30 return nil, nil, NewError(http.StatusNotFound, "404: Page Not Found")
31 }
32 candidateRoutes := c.selectRoutes(detectedService, requestTokens)
33 if len(candidateRoutes) == 0 {
34 if trace {
35 traceLogger.Printf("no Route in WebService with path %s was found to match URL path:%s\n", detectedService.rootPath, httpRequest.URL.Path)
36 }
37 return detectedService, nil, NewError(http.StatusNotFound, "404: Page Not Found")
38 }
39 selectedRoute, err := c.detectRoute(candidateRoutes, httpRequest)
40 if selectedRoute == nil {
41 return detectedService, nil, err
42 }
43 return detectedService, selectedRoute, nil
44 }
45
46
47 func (c CurlyRouter) selectRoutes(ws *WebService, requestTokens []string) sortableCurlyRoutes {
48 candidates := make(sortableCurlyRoutes, 0, 8)
49 for _, each := range ws.routes {
50 matches, paramCount, staticCount := c.matchesRouteByPathTokens(each.pathParts, requestTokens, each.hasCustomVerb)
51 if matches {
52 candidates.add(curlyRoute{each, paramCount, staticCount})
53 }
54 }
55 sort.Sort(candidates)
56 return candidates
57 }
58
59
60 func (c CurlyRouter) matchesRouteByPathTokens(routeTokens, requestTokens []string, routeHasCustomVerb bool) (matches bool, paramCount int, staticCount int) {
61 if len(routeTokens) < len(requestTokens) {
62
63 count := len(routeTokens)
64 if count == 0 || !strings.HasSuffix(routeTokens[count-1], "*}") {
65 return false, 0, 0
66 }
67
68 }
69 for i, routeToken := range routeTokens {
70 if i == len(requestTokens) {
71
72 return false, 0, 0
73 }
74 requestToken := requestTokens[i]
75 if routeHasCustomVerb && hasCustomVerb(routeToken){
76 if !isMatchCustomVerb(routeToken, requestToken) {
77 return false, 0, 0
78 }
79 staticCount++
80 requestToken = removeCustomVerb(requestToken)
81 routeToken = removeCustomVerb(routeToken)
82 }
83
84 if strings.HasPrefix(routeToken, "{") {
85 paramCount++
86 if colon := strings.Index(routeToken, ":"); colon != -1 {
87
88 matchesToken, matchesRemainder := c.regularMatchesPathToken(routeToken, colon, requestToken)
89 if !matchesToken {
90 return false, 0, 0
91 }
92 if matchesRemainder {
93 break
94 }
95 }
96 } else {
97 if requestToken != routeToken {
98 return false, 0, 0
99 }
100 staticCount++
101 }
102 }
103 return true, paramCount, staticCount
104 }
105
106
107
108 func (c CurlyRouter) regularMatchesPathToken(routeToken string, colon int, requestToken string) (matchesToken bool, matchesRemainder bool) {
109 regPart := routeToken[colon+1 : len(routeToken)-1]
110 if regPart == "*" {
111 if trace {
112 traceLogger.Printf("wildcard parameter detected in route token %s that matches %s\n", routeToken, requestToken)
113 }
114 return true, true
115 }
116 matched, err := regexp.MatchString(regPart, requestToken)
117 return (matched && err == nil), false
118 }
119
120 var jsr311Router = RouterJSR311{}
121
122
123
124 func (c CurlyRouter) detectRoute(candidateRoutes sortableCurlyRoutes, httpRequest *http.Request) (*Route, error) {
125
126 return jsr311Router.detectRoute(candidateRoutes.routes(), httpRequest)
127 }
128
129
130
131 func (c CurlyRouter) detectWebService(requestTokens []string, webServices []*WebService) *WebService {
132 var best *WebService
133 score := -1
134 for _, each := range webServices {
135 matches, eachScore := c.computeWebserviceScore(requestTokens, each.pathExpr.tokens)
136 if matches && (eachScore > score) {
137 best = each
138 score = eachScore
139 }
140 }
141 return best
142 }
143
144
145
146 func (c CurlyRouter) computeWebserviceScore(requestTokens []string, tokens []string) (bool, int) {
147 if len(tokens) > len(requestTokens) {
148 return false, 0
149 }
150 score := 0
151 for i := 0; i < len(tokens); i++ {
152 each := requestTokens[i]
153 other := tokens[i]
154 if len(each) == 0 && len(other) == 0 {
155 score++
156 continue
157 }
158 if len(other) > 0 && strings.HasPrefix(other, "{") {
159
160 if len(each) == 0 {
161 return false, score
162 }
163 score += 1
164 } else {
165
166 if each != other {
167 return false, score
168 }
169 score += (len(tokens) - i) * 10
170 }
171 }
172 return true, score
173 }
174
View as plain text