Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / emicklei / go-restful / jsr311.go
1 package restful
2
3 // Copyright 2013 Ernest Micklei. All rights reserved.
4 // Use of this source code is governed by a license
5 // that can be found in the LICENSE file.
6
7 import (
8         "errors"
9         "fmt"
10         "net/http"
11         "sort"
12 )
13
14 // RouterJSR311 implements the flow for matching Requests to Routes (and consequently Resource Functions)
15 // as specified by the JSR311 http://jsr311.java.net/nonav/releases/1.1/spec/spec.html.
16 // RouterJSR311 implements the Router interface.
17 // Concept of locators is not implemented.
18 type RouterJSR311 struct{}
19
20 // SelectRoute is part of the Router interface and returns the best match
21 // for the WebService and its Route for the given Request.
22 func (r RouterJSR311) SelectRoute(
23         webServices []*WebService,
24         httpRequest *http.Request) (selectedService *WebService, selectedRoute *Route, err error) {
25
26         // Identify the root resource class (WebService)
27         dispatcher, finalMatch, err := r.detectDispatcher(httpRequest.URL.Path, webServices)
28         if err != nil {
29                 return nil, nil, NewError(http.StatusNotFound, "")
30         }
31         // Obtain the set of candidate methods (Routes)
32         routes := r.selectRoutes(dispatcher, finalMatch)
33         if len(routes) == 0 {
34                 return dispatcher, nil, NewError(http.StatusNotFound, "404: Page Not Found")
35         }
36
37         // Identify the method (Route) that will handle the request
38         route, ok := r.detectRoute(routes, httpRequest)
39         return dispatcher, route, ok
40 }
41
42 // ExtractParameters is used to obtain the path parameters from the route using the same matching
43 // engine as the JSR 311 router.
44 func (r RouterJSR311) ExtractParameters(route *Route, webService *WebService, urlPath string) map[string]string {
45         webServiceExpr := webService.pathExpr
46         webServiceMatches := webServiceExpr.Matcher.FindStringSubmatch(urlPath)
47         pathParameters := r.extractParams(webServiceExpr, webServiceMatches)
48         routeExpr := route.pathExpr
49         routeMatches := routeExpr.Matcher.FindStringSubmatch(webServiceMatches[len(webServiceMatches)-1])
50         routeParams := r.extractParams(routeExpr, routeMatches)
51         for key, value := range routeParams {
52                 pathParameters[key] = value
53         }
54         return pathParameters
55 }
56
57 func (RouterJSR311) extractParams(pathExpr *pathExpression, matches []string) map[string]string {
58         params := map[string]string{}
59         for i := 1; i < len(matches); i++ {
60                 if len(pathExpr.VarNames) >= i {
61                         params[pathExpr.VarNames[i-1]] = matches[i]
62                 }
63         }
64         return params
65 }
66
67 // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
68 func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*Route, error) {
69         candidates := make([]*Route, 0, 8)
70         for i, each := range routes {
71                 ok := true
72                 for _, fn := range each.If {
73                         if !fn(httpRequest) {
74                                 ok = false
75                                 break
76                         }
77                 }
78                 if ok {
79                         candidates = append(candidates, &routes[i])
80                 }
81         }
82         if len(candidates) == 0 {
83                 if trace {
84                         traceLogger.Printf("no Route found (from %d) that passes conditional checks", len(routes))
85                 }
86                 return nil, NewError(http.StatusNotFound, "404: Not Found")
87         }
88
89         // http method
90         previous := candidates
91         candidates = candidates[:0]
92         for _, each := range previous {
93                 if httpRequest.Method == each.Method {
94                         candidates = append(candidates, each)
95                 }
96         }
97         if len(candidates) == 0 {
98                 if trace {
99                         traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(previous), httpRequest.Method)
100                 }
101                 return nil, NewError(http.StatusMethodNotAllowed, "405: Method Not Allowed")
102         }
103
104         // content-type
105         contentType := httpRequest.Header.Get(HEADER_ContentType)
106         previous = candidates
107         candidates = candidates[:0]
108         for _, each := range previous {
109                 if each.matchesContentType(contentType) {
110                         candidates = append(candidates, each)
111                 }
112         }
113         if len(candidates) == 0 {
114                 if trace {
115                         traceLogger.Printf("no Route found (from %d) that matches HTTP Content-Type: %s\n", len(previous), contentType)
116                 }
117                 if httpRequest.ContentLength > 0 {
118                         return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type")
119                 }
120         }
121
122         // accept
123         previous = candidates
124         candidates = candidates[:0]
125         accept := httpRequest.Header.Get(HEADER_Accept)
126         if len(accept) == 0 {
127                 accept = "*/*"
128         }
129         for _, each := range previous {
130                 if each.matchesAccept(accept) {
131                         candidates = append(candidates, each)
132                 }
133         }
134         if len(candidates) == 0 {
135                 if trace {
136                         traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(previous), accept)
137                 }
138                 return nil, NewError(http.StatusNotAcceptable, "406: Not Acceptable")
139         }
140         // return r.bestMatchByMedia(outputMediaOk, contentType, accept), nil
141         return candidates[0], nil
142 }
143
144 // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
145 // n/m > n/* > */*
146 func (r RouterJSR311) bestMatchByMedia(routes []Route, contentType string, accept string) *Route {
147         // TODO
148         return &routes[0]
149 }
150
151 // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2  (step 2)
152 func (r RouterJSR311) selectRoutes(dispatcher *WebService, pathRemainder string) []Route {
153         filtered := &sortableRouteCandidates{}
154         for _, each := range dispatcher.Routes() {
155                 pathExpr := each.pathExpr
156                 matches := pathExpr.Matcher.FindStringSubmatch(pathRemainder)
157                 if matches != nil {
158                         lastMatch := matches[len(matches)-1]
159                         if len(lastMatch) == 0 || lastMatch == "/" { // do not include if value is neither empty nor ‘/’.
160                                 filtered.candidates = append(filtered.candidates,
161                                         routeCandidate{each, len(matches) - 1, pathExpr.LiteralCount, pathExpr.VarCount})
162                         }
163                 }
164         }
165         if len(filtered.candidates) == 0 {
166                 if trace {
167                         traceLogger.Printf("WebService on path %s has no routes that match URL path remainder:%s\n", dispatcher.rootPath, pathRemainder)
168                 }
169                 return []Route{}
170         }
171         sort.Sort(sort.Reverse(filtered))
172
173         // select other routes from candidates whoes expression matches rmatch
174         matchingRoutes := []Route{filtered.candidates[0].route}
175         for c := 1; c < len(filtered.candidates); c++ {
176                 each := filtered.candidates[c]
177                 if each.route.pathExpr.Matcher.MatchString(pathRemainder) {
178                         matchingRoutes = append(matchingRoutes, each.route)
179                 }
180         }
181         return matchingRoutes
182 }
183
184 // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 (step 1)
185 func (r RouterJSR311) detectDispatcher(requestPath string, dispatchers []*WebService) (*WebService, string, error) {
186         filtered := &sortableDispatcherCandidates{}
187         for _, each := range dispatchers {
188                 matches := each.pathExpr.Matcher.FindStringSubmatch(requestPath)
189                 if matches != nil {
190                         filtered.candidates = append(filtered.candidates,
191                                 dispatcherCandidate{each, matches[len(matches)-1], len(matches), each.pathExpr.LiteralCount, each.pathExpr.VarCount})
192                 }
193         }
194         if len(filtered.candidates) == 0 {
195                 if trace {
196                         traceLogger.Printf("no WebService was found to match URL path:%s\n", requestPath)
197                 }
198                 return nil, "", errors.New("not found")
199         }
200         sort.Sort(sort.Reverse(filtered))
201         return filtered.candidates[0].dispatcher, filtered.candidates[0].finalMatch, nil
202 }
203
204 // Types and functions to support the sorting of Routes
205
206 type routeCandidate struct {
207         route           Route
208         matchesCount    int // the number of capturing groups
209         literalCount    int // the number of literal characters (means those not resulting from template variable substitution)
210         nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ‘([^  /]+?)’)
211 }
212
213 func (r routeCandidate) expressionToMatch() string {
214         return r.route.pathExpr.Source
215 }
216
217 func (r routeCandidate) String() string {
218         return fmt.Sprintf("(m=%d,l=%d,n=%d)", r.matchesCount, r.literalCount, r.nonDefaultCount)
219 }
220
221 type sortableRouteCandidates struct {
222         candidates []routeCandidate
223 }
224
225 func (rcs *sortableRouteCandidates) Len() int {
226         return len(rcs.candidates)
227 }
228 func (rcs *sortableRouteCandidates) Swap(i, j int) {
229         rcs.candidates[i], rcs.candidates[j] = rcs.candidates[j], rcs.candidates[i]
230 }
231 func (rcs *sortableRouteCandidates) Less(i, j int) bool {
232         ci := rcs.candidates[i]
233         cj := rcs.candidates[j]
234         // primary key
235         if ci.literalCount < cj.literalCount {
236                 return true
237         }
238         if ci.literalCount > cj.literalCount {
239                 return false
240         }
241         // secundary key
242         if ci.matchesCount < cj.matchesCount {
243                 return true
244         }
245         if ci.matchesCount > cj.matchesCount {
246                 return false
247         }
248         // tertiary key
249         if ci.nonDefaultCount < cj.nonDefaultCount {
250                 return true
251         }
252         if ci.nonDefaultCount > cj.nonDefaultCount {
253                 return false
254         }
255         // quaternary key ("source" is interpreted as Path)
256         return ci.route.Path < cj.route.Path
257 }
258
259 // Types and functions to support the sorting of Dispatchers
260
261 type dispatcherCandidate struct {
262         dispatcher      *WebService
263         finalMatch      string
264         matchesCount    int // the number of capturing groups
265         literalCount    int // the number of literal characters (means those not resulting from template variable substitution)
266         nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ‘([^  /]+?)’)
267 }
268 type sortableDispatcherCandidates struct {
269         candidates []dispatcherCandidate
270 }
271
272 func (dc *sortableDispatcherCandidates) Len() int {
273         return len(dc.candidates)
274 }
275 func (dc *sortableDispatcherCandidates) Swap(i, j int) {
276         dc.candidates[i], dc.candidates[j] = dc.candidates[j], dc.candidates[i]
277 }
278 func (dc *sortableDispatcherCandidates) Less(i, j int) bool {
279         ci := dc.candidates[i]
280         cj := dc.candidates[j]
281         // primary key
282         if ci.matchesCount < cj.matchesCount {
283                 return true
284         }
285         if ci.matchesCount > cj.matchesCount {
286                 return false
287         }
288         // secundary key
289         if ci.literalCount < cj.literalCount {
290                 return true
291         }
292         if ci.literalCount > cj.literalCount {
293                 return false
294         }
295         // tertiary key
296         return ci.nonDefaultCount < cj.nonDefaultCount
297 }