jsr311.go 10 KB

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