1 package restful
2
3
4
5
6
7 import (
8 "errors"
9 "fmt"
10 "net/http"
11 "sort"
12 "strings"
13 )
14
15
16
17
18
19 type RouterJSR311 struct{}
20
21
22
23 func (r RouterJSR311) SelectRoute(
24 webServices []*WebService,
25 httpRequest *http.Request) (selectedService *WebService, selectedRoute *Route, err error) {
26
27
28 dispatcher, finalMatch, err := r.detectDispatcher(httpRequest.URL.Path, webServices)
29 if err != nil {
30 return nil, nil, NewError(http.StatusNotFound, "")
31 }
32
33 routes := r.selectRoutes(dispatcher, finalMatch)
34 if len(routes) == 0 {
35 return dispatcher, nil, NewError(http.StatusNotFound, "404: Page Not Found")
36 }
37
38
39 route, ok := r.detectRoute(routes, httpRequest)
40 return dispatcher, route, ok
41 }
42
43
44
45 func (r RouterJSR311) ExtractParameters(route *Route, webService *WebService, urlPath string) map[string]string {
46 webServiceExpr := webService.pathExpr
47 webServiceMatches := webServiceExpr.Matcher.FindStringSubmatch(urlPath)
48 pathParameters := r.extractParams(webServiceExpr, webServiceMatches)
49 routeExpr := route.pathExpr
50 routeMatches := routeExpr.Matcher.FindStringSubmatch(webServiceMatches[len(webServiceMatches)-1])
51 routeParams := r.extractParams(routeExpr, routeMatches)
52 for key, value := range routeParams {
53 pathParameters[key] = value
54 }
55 return pathParameters
56 }
57
58 func (RouterJSR311) extractParams(pathExpr *pathExpression, matches []string) map[string]string {
59 params := map[string]string{}
60 for i := 1; i < len(matches); i++ {
61 if len(pathExpr.VarNames) >= i {
62 params[pathExpr.VarNames[i-1]] = matches[i]
63 }
64 }
65 return params
66 }
67
68
69 func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*Route, error) {
70 candidates := make([]*Route, 0, 8)
71 for i, each := range routes {
72 ok := true
73 for _, fn := range each.If {
74 if !fn(httpRequest) {
75 ok = false
76 break
77 }
78 }
79 if ok {
80 candidates = append(candidates, &routes[i])
81 }
82 }
83 if len(candidates) == 0 {
84 if trace {
85 traceLogger.Printf("no Route found (from %d) that passes conditional checks", len(routes))
86 }
87 return nil, NewError(http.StatusNotFound, "404: Not Found")
88 }
89
90
91 previous := candidates
92 candidates = candidates[:0]
93 for _, each := range previous {
94 if httpRequest.Method == each.Method {
95 candidates = append(candidates, each)
96 }
97 }
98 if len(candidates) == 0 {
99 if trace {
100 traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(previous), httpRequest.Method)
101 }
102 allowed := []string{}
103 allowedLoop:
104 for _, candidate := range previous {
105 for _, method := range allowed {
106 if method == candidate.Method {
107 continue allowedLoop
108 }
109 }
110 allowed = append(allowed, candidate.Method)
111 }
112 header := http.Header{"Allow": []string{strings.Join(allowed, ", ")}}
113 return nil, NewErrorWithHeader(http.StatusMethodNotAllowed, "405: Method Not Allowed", header)
114 }
115
116
117 contentType := httpRequest.Header.Get(HEADER_ContentType)
118 previous = candidates
119 candidates = candidates[:0]
120 for _, each := range previous {
121 if each.matchesContentType(contentType) {
122 candidates = append(candidates, each)
123 }
124 }
125 if len(candidates) == 0 {
126 if trace {
127 traceLogger.Printf("no Route found (from %d) that matches HTTP Content-Type: %s\n", len(previous), contentType)
128 }
129 if httpRequest.ContentLength > 0 {
130 return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type")
131 }
132 }
133
134
135 previous = candidates
136 candidates = candidates[:0]
137 accept := httpRequest.Header.Get(HEADER_Accept)
138 if len(accept) == 0 {
139 accept = "*/*"
140 }
141 for _, each := range previous {
142 if each.matchesAccept(accept) {
143 candidates = append(candidates, each)
144 }
145 }
146 if len(candidates) == 0 {
147 if trace {
148 traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(previous), accept)
149 }
150 available := []string{}
151 for _, candidate := range previous {
152 available = append(available, candidate.Produces...)
153 }
154
155 method, length := httpRequest.Method, httpRequest.Header.Get("Content-Length")
156 if (method == http.MethodPost ||
157 method == http.MethodPut ||
158 method == http.MethodPatch) && (length == "" || length == "0") {
159 return nil, NewError(
160 http.StatusUnsupportedMediaType,
161 fmt.Sprintf("415: Unsupported Media Type\n\nAvailable representations: %s", strings.Join(available, ", ")),
162 )
163 }
164 return nil, NewError(
165 http.StatusNotAcceptable,
166 fmt.Sprintf("406: Not Acceptable\n\nAvailable representations: %s", strings.Join(available, ", ")),
167 )
168 }
169
170 return candidates[0], nil
171 }
172
173
174
175 func (r RouterJSR311) bestMatchByMedia(routes []Route, contentType string, accept string) *Route {
176
177 return &routes[0]
178 }
179
180
181 func (r RouterJSR311) selectRoutes(dispatcher *WebService, pathRemainder string) []Route {
182 filtered := &sortableRouteCandidates{}
183 for _, each := range dispatcher.Routes() {
184 pathExpr := each.pathExpr
185 matches := pathExpr.Matcher.FindStringSubmatch(pathRemainder)
186 if matches != nil {
187 lastMatch := matches[len(matches)-1]
188 if len(lastMatch) == 0 || lastMatch == "/" {
189 filtered.candidates = append(filtered.candidates,
190 routeCandidate{each, len(matches) - 1, pathExpr.LiteralCount, pathExpr.VarCount})
191 }
192 }
193 }
194 if len(filtered.candidates) == 0 {
195 if trace {
196 traceLogger.Printf("WebService on path %s has no routes that match URL path remainder:%s\n", dispatcher.rootPath, pathRemainder)
197 }
198 return []Route{}
199 }
200 sort.Sort(sort.Reverse(filtered))
201
202
203 matchingRoutes := []Route{filtered.candidates[0].route}
204 for c := 1; c < len(filtered.candidates); c++ {
205 each := filtered.candidates[c]
206 if each.route.pathExpr.Matcher.MatchString(pathRemainder) {
207 matchingRoutes = append(matchingRoutes, each.route)
208 }
209 }
210 return matchingRoutes
211 }
212
213
214 func (r RouterJSR311) detectDispatcher(requestPath string, dispatchers []*WebService) (*WebService, string, error) {
215 filtered := &sortableDispatcherCandidates{}
216 for _, each := range dispatchers {
217 matches := each.pathExpr.Matcher.FindStringSubmatch(requestPath)
218 if matches != nil {
219 filtered.candidates = append(filtered.candidates,
220 dispatcherCandidate{each, matches[len(matches)-1], len(matches), each.pathExpr.LiteralCount, each.pathExpr.VarCount})
221 }
222 }
223 if len(filtered.candidates) == 0 {
224 if trace {
225 traceLogger.Printf("no WebService was found to match URL path:%s\n", requestPath)
226 }
227 return nil, "", errors.New("not found")
228 }
229 sort.Sort(sort.Reverse(filtered))
230 return filtered.candidates[0].dispatcher, filtered.candidates[0].finalMatch, nil
231 }
232
233
234
235 type routeCandidate struct {
236 route Route
237 matchesCount int
238 literalCount int
239 nonDefaultCount int
240 }
241
242 func (r routeCandidate) expressionToMatch() string {
243 return r.route.pathExpr.Source
244 }
245
246 func (r routeCandidate) String() string {
247 return fmt.Sprintf("(m=%d,l=%d,n=%d)", r.matchesCount, r.literalCount, r.nonDefaultCount)
248 }
249
250 type sortableRouteCandidates struct {
251 candidates []routeCandidate
252 }
253
254 func (rcs *sortableRouteCandidates) Len() int {
255 return len(rcs.candidates)
256 }
257 func (rcs *sortableRouteCandidates) Swap(i, j int) {
258 rcs.candidates[i], rcs.candidates[j] = rcs.candidates[j], rcs.candidates[i]
259 }
260 func (rcs *sortableRouteCandidates) Less(i, j int) bool {
261 ci := rcs.candidates[i]
262 cj := rcs.candidates[j]
263
264 if ci.literalCount < cj.literalCount {
265 return true
266 }
267 if ci.literalCount > cj.literalCount {
268 return false
269 }
270
271 if ci.matchesCount < cj.matchesCount {
272 return true
273 }
274 if ci.matchesCount > cj.matchesCount {
275 return false
276 }
277
278 if ci.nonDefaultCount < cj.nonDefaultCount {
279 return true
280 }
281 if ci.nonDefaultCount > cj.nonDefaultCount {
282 return false
283 }
284
285 return ci.route.Path < cj.route.Path
286 }
287
288
289
290 type dispatcherCandidate struct {
291 dispatcher *WebService
292 finalMatch string
293 matchesCount int
294 literalCount int
295 nonDefaultCount int
296 }
297 type sortableDispatcherCandidates struct {
298 candidates []dispatcherCandidate
299 }
300
301 func (dc *sortableDispatcherCandidates) Len() int {
302 return len(dc.candidates)
303 }
304 func (dc *sortableDispatcherCandidates) Swap(i, j int) {
305 dc.candidates[i], dc.candidates[j] = dc.candidates[j], dc.candidates[i]
306 }
307 func (dc *sortableDispatcherCandidates) Less(i, j int) bool {
308 ci := dc.candidates[i]
309 cj := dc.candidates[j]
310
311 if ci.matchesCount < cj.matchesCount {
312 return true
313 }
314 if ci.matchesCount > cj.matchesCount {
315 return false
316 }
317
318 if ci.literalCount < cj.literalCount {
319 return true
320 }
321 if ci.literalCount > cj.literalCount {
322 return false
323 }
324
325 return ci.nonDefaultCount < cj.nonDefaultCount
326 }
327
View as plain text