jsr311.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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. )
  11. // RouterJSR311 implements the flow for matching Requests to Routes (and consequently Resource Functions)
  12. // as specified by the JSR311 http://jsr311.java.net/nonav/releases/1.1/spec/spec.html.
  13. // RouterJSR311 implements the Router interface.
  14. // Concept of locators is not implemented.
  15. type RouterJSR311 struct{}
  16. // SelectRoute is part of the Router interface and returns the best match
  17. // for the WebService and its Route for the given Request.
  18. func (r RouterJSR311) SelectRoute(
  19. webServices []*WebService,
  20. httpRequest *http.Request) (selectedService *WebService, selectedRoute *Route, err error) {
  21. // Identify the root resource class (WebService)
  22. dispatcher, finalMatch, err := r.detectDispatcher(httpRequest.URL.Path, webServices)
  23. if err != nil {
  24. return nil, nil, NewError(http.StatusNotFound, "")
  25. }
  26. // Obtain the set of candidate methods (Routes)
  27. routes := r.selectRoutes(dispatcher, finalMatch)
  28. if len(routes) == 0 {
  29. return dispatcher, nil, NewError(http.StatusNotFound, "404: Page Not Found")
  30. }
  31. // Identify the method (Route) that will handle the request
  32. route, ok := r.detectRoute(routes, httpRequest)
  33. return dispatcher, route, ok
  34. }
  35. // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
  36. func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*Route, error) {
  37. // http method
  38. methodOk := []Route{}
  39. for _, each := range routes {
  40. if httpRequest.Method == each.Method {
  41. methodOk = append(methodOk, each)
  42. }
  43. }
  44. if len(methodOk) == 0 {
  45. if trace {
  46. traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(routes), httpRequest.Method)
  47. }
  48. return nil, NewError(http.StatusMethodNotAllowed, "405: Method Not Allowed")
  49. }
  50. inputMediaOk := methodOk
  51. // content-type
  52. contentType := httpRequest.Header.Get(HEADER_ContentType)
  53. inputMediaOk = []Route{}
  54. for _, each := range methodOk {
  55. if each.matchesContentType(contentType) {
  56. inputMediaOk = append(inputMediaOk, each)
  57. }
  58. }
  59. if len(inputMediaOk) == 0 {
  60. if trace {
  61. traceLogger.Printf("no Route found (from %d) that matches HTTP Content-Type: %s\n", len(methodOk), contentType)
  62. }
  63. return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type")
  64. }
  65. // accept
  66. outputMediaOk := []Route{}
  67. accept := httpRequest.Header.Get(HEADER_Accept)
  68. if len(accept) == 0 {
  69. accept = "*/*"
  70. }
  71. for _, each := range inputMediaOk {
  72. if each.matchesAccept(accept) {
  73. outputMediaOk = append(outputMediaOk, each)
  74. }
  75. }
  76. if len(outputMediaOk) == 0 {
  77. if trace {
  78. traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(inputMediaOk), accept)
  79. }
  80. return nil, NewError(http.StatusNotAcceptable, "406: Not Acceptable")
  81. }
  82. // return r.bestMatchByMedia(outputMediaOk, contentType, accept), nil
  83. return &outputMediaOk[0], nil
  84. }
  85. // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
  86. // n/m > n/* > */*
  87. func (r RouterJSR311) bestMatchByMedia(routes []Route, contentType string, accept string) *Route {
  88. // TODO
  89. return &routes[0]
  90. }
  91. // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 (step 2)
  92. func (r RouterJSR311) selectRoutes(dispatcher *WebService, pathRemainder string) []Route {
  93. filtered := &sortableRouteCandidates{}
  94. for _, each := range dispatcher.Routes() {
  95. pathExpr := each.pathExpr
  96. matches := pathExpr.Matcher.FindStringSubmatch(pathRemainder)
  97. if matches != nil {
  98. lastMatch := matches[len(matches)-1]
  99. if len(lastMatch) == 0 || lastMatch == "/" { // do not include if value is neither empty nor ‘/’.
  100. filtered.candidates = append(filtered.candidates,
  101. routeCandidate{each, len(matches) - 1, pathExpr.LiteralCount, pathExpr.VarCount})
  102. }
  103. }
  104. }
  105. if len(filtered.candidates) == 0 {
  106. if trace {
  107. traceLogger.Printf("WebService on path %s has no routes that match URL path remainder:%s\n", dispatcher.rootPath, pathRemainder)
  108. }
  109. return []Route{}
  110. }
  111. sort.Sort(sort.Reverse(filtered))
  112. // select other routes from candidates whoes expression matches rmatch
  113. matchingRoutes := []Route{filtered.candidates[0].route}
  114. for c := 1; c < len(filtered.candidates); c++ {
  115. each := filtered.candidates[c]
  116. if each.route.pathExpr.Matcher.MatchString(pathRemainder) {
  117. matchingRoutes = append(matchingRoutes, each.route)
  118. }
  119. }
  120. return matchingRoutes
  121. }
  122. // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 (step 1)
  123. func (r RouterJSR311) detectDispatcher(requestPath string, dispatchers []*WebService) (*WebService, string, error) {
  124. filtered := &sortableDispatcherCandidates{}
  125. for _, each := range dispatchers {
  126. matches := each.pathExpr.Matcher.FindStringSubmatch(requestPath)
  127. if matches != nil {
  128. filtered.candidates = append(filtered.candidates,
  129. dispatcherCandidate{each, matches[len(matches)-1], len(matches), each.pathExpr.LiteralCount, each.pathExpr.VarCount})
  130. }
  131. }
  132. if len(filtered.candidates) == 0 {
  133. if trace {
  134. traceLogger.Printf("no WebService was found to match URL path:%s\n", requestPath)
  135. }
  136. return nil, "", errors.New("not found")
  137. }
  138. sort.Sort(sort.Reverse(filtered))
  139. return filtered.candidates[0].dispatcher, filtered.candidates[0].finalMatch, nil
  140. }
  141. // Types and functions to support the sorting of Routes
  142. type routeCandidate struct {
  143. route Route
  144. matchesCount int // the number of capturing groups
  145. literalCount int // the number of literal characters (means those not resulting from template variable substitution)
  146. nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ‘([^ /]+?)’)
  147. }
  148. func (r routeCandidate) expressionToMatch() string {
  149. return r.route.pathExpr.Source
  150. }
  151. func (r routeCandidate) String() string {
  152. return fmt.Sprintf("(m=%d,l=%d,n=%d)", r.matchesCount, r.literalCount, r.nonDefaultCount)
  153. }
  154. type sortableRouteCandidates struct {
  155. candidates []routeCandidate
  156. }
  157. func (rcs *sortableRouteCandidates) Len() int {
  158. return len(rcs.candidates)
  159. }
  160. func (rcs *sortableRouteCandidates) Swap(i, j int) {
  161. rcs.candidates[i], rcs.candidates[j] = rcs.candidates[j], rcs.candidates[i]
  162. }
  163. func (rcs *sortableRouteCandidates) Less(i, j int) bool {
  164. ci := rcs.candidates[i]
  165. cj := rcs.candidates[j]
  166. // primary key
  167. if ci.literalCount < cj.literalCount {
  168. return true
  169. }
  170. if ci.literalCount > cj.literalCount {
  171. return false
  172. }
  173. // secundary key
  174. if ci.matchesCount < cj.matchesCount {
  175. return true
  176. }
  177. if ci.matchesCount > cj.matchesCount {
  178. return false
  179. }
  180. // tertiary key
  181. if ci.nonDefaultCount < cj.nonDefaultCount {
  182. return true
  183. }
  184. if ci.nonDefaultCount > cj.nonDefaultCount {
  185. return false
  186. }
  187. // quaternary key ("source" is interpreted as Path)
  188. return ci.route.Path < cj.route.Path
  189. }
  190. // Types and functions to support the sorting of Dispatchers
  191. type dispatcherCandidate struct {
  192. dispatcher *WebService
  193. finalMatch string
  194. matchesCount int // the number of capturing groups
  195. literalCount int // the number of literal characters (means those not resulting from template variable substitution)
  196. nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ‘([^ /]+?)’)
  197. }
  198. type sortableDispatcherCandidates struct {
  199. candidates []dispatcherCandidate
  200. }
  201. func (dc *sortableDispatcherCandidates) Len() int {
  202. return len(dc.candidates)
  203. }
  204. func (dc *sortableDispatcherCandidates) Swap(i, j int) {
  205. dc.candidates[i], dc.candidates[j] = dc.candidates[j], dc.candidates[i]
  206. }
  207. func (dc *sortableDispatcherCandidates) Less(i, j int) bool {
  208. ci := dc.candidates[i]
  209. cj := dc.candidates[j]
  210. // primary key
  211. if ci.matchesCount < cj.matchesCount {
  212. return true
  213. }
  214. if ci.matchesCount > cj.matchesCount {
  215. return false
  216. }
  217. // secundary key
  218. if ci.literalCount < cj.literalCount {
  219. return true
  220. }
  221. if ci.literalCount > cj.literalCount {
  222. return false
  223. }
  224. // tertiary key
  225. return ci.nonDefaultCount < cj.nonDefaultCount
  226. }