123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- package restful
- // Copyright 2013 Ernest Micklei. All rights reserved.
- // Use of this source code is governed by a license
- // that can be found in the LICENSE file.
- import (
- "errors"
- "fmt"
- "net/http"
- "sort"
- )
- // RouterJSR311 implements the flow for matching Requests to Routes (and consequently Resource Functions)
- // as specified by the JSR311 http://jsr311.java.net/nonav/releases/1.1/spec/spec.html.
- // RouterJSR311 implements the Router interface.
- // Concept of locators is not implemented.
- type RouterJSR311 struct{}
- // SelectRoute is part of the Router interface and returns the best match
- // for the WebService and its Route for the given Request.
- func (r RouterJSR311) SelectRoute(
- webServices []*WebService,
- httpRequest *http.Request) (selectedService *WebService, selectedRoute *Route, err error) {
- // Identify the root resource class (WebService)
- dispatcher, finalMatch, err := r.detectDispatcher(httpRequest.URL.Path, webServices)
- if err != nil {
- return nil, nil, NewError(http.StatusNotFound, "")
- }
- // Obtain the set of candidate methods (Routes)
- routes := r.selectRoutes(dispatcher, finalMatch)
- if len(routes) == 0 {
- return dispatcher, nil, NewError(http.StatusNotFound, "404: Page Not Found")
- }
- // Identify the method (Route) that will handle the request
- route, ok := r.detectRoute(routes, httpRequest)
- return dispatcher, route, ok
- }
- // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
- func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*Route, error) {
- // http method
- methodOk := []Route{}
- for _, each := range routes {
- if httpRequest.Method == each.Method {
- methodOk = append(methodOk, each)
- }
- }
- if len(methodOk) == 0 {
- if trace {
- traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(routes), httpRequest.Method)
- }
- return nil, NewError(http.StatusMethodNotAllowed, "405: Method Not Allowed")
- }
- inputMediaOk := methodOk
- // content-type
- contentType := httpRequest.Header.Get(HEADER_ContentType)
- inputMediaOk = []Route{}
- for _, each := range methodOk {
- if each.matchesContentType(contentType) {
- inputMediaOk = append(inputMediaOk, each)
- }
- }
- if len(inputMediaOk) == 0 {
- if trace {
- traceLogger.Printf("no Route found (from %d) that matches HTTP Content-Type: %s\n", len(methodOk), contentType)
- }
- return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type")
- }
- // accept
- outputMediaOk := []Route{}
- accept := httpRequest.Header.Get(HEADER_Accept)
- if len(accept) == 0 {
- accept = "*/*"
- }
- for _, each := range inputMediaOk {
- if each.matchesAccept(accept) {
- outputMediaOk = append(outputMediaOk, each)
- }
- }
- if len(outputMediaOk) == 0 {
- if trace {
- traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(inputMediaOk), accept)
- }
- return nil, NewError(http.StatusNotAcceptable, "406: Not Acceptable")
- }
- // return r.bestMatchByMedia(outputMediaOk, contentType, accept), nil
- return &outputMediaOk[0], nil
- }
- // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
- // n/m > n/* > */*
- func (r RouterJSR311) bestMatchByMedia(routes []Route, contentType string, accept string) *Route {
- // TODO
- return &routes[0]
- }
- // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 (step 2)
- func (r RouterJSR311) selectRoutes(dispatcher *WebService, pathRemainder string) []Route {
- filtered := &sortableRouteCandidates{}
- for _, each := range dispatcher.Routes() {
- pathExpr := each.pathExpr
- matches := pathExpr.Matcher.FindStringSubmatch(pathRemainder)
- if matches != nil {
- lastMatch := matches[len(matches)-1]
- if len(lastMatch) == 0 || lastMatch == "/" { // do not include if value is neither empty nor ‘/’.
- filtered.candidates = append(filtered.candidates,
- routeCandidate{each, len(matches) - 1, pathExpr.LiteralCount, pathExpr.VarCount})
- }
- }
- }
- if len(filtered.candidates) == 0 {
- if trace {
- traceLogger.Printf("WebService on path %s has no routes that match URL path remainder:%s\n", dispatcher.rootPath, pathRemainder)
- }
- return []Route{}
- }
- sort.Sort(sort.Reverse(filtered))
- // select other routes from candidates whoes expression matches rmatch
- matchingRoutes := []Route{filtered.candidates[0].route}
- for c := 1; c < len(filtered.candidates); c++ {
- each := filtered.candidates[c]
- if each.route.pathExpr.Matcher.MatchString(pathRemainder) {
- matchingRoutes = append(matchingRoutes, each.route)
- }
- }
- return matchingRoutes
- }
- // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 (step 1)
- func (r RouterJSR311) detectDispatcher(requestPath string, dispatchers []*WebService) (*WebService, string, error) {
- filtered := &sortableDispatcherCandidates{}
- for _, each := range dispatchers {
- matches := each.pathExpr.Matcher.FindStringSubmatch(requestPath)
- if matches != nil {
- filtered.candidates = append(filtered.candidates,
- dispatcherCandidate{each, matches[len(matches)-1], len(matches), each.pathExpr.LiteralCount, each.pathExpr.VarCount})
- }
- }
- if len(filtered.candidates) == 0 {
- if trace {
- traceLogger.Printf("no WebService was found to match URL path:%s\n", requestPath)
- }
- return nil, "", errors.New("not found")
- }
- sort.Sort(sort.Reverse(filtered))
- return filtered.candidates[0].dispatcher, filtered.candidates[0].finalMatch, nil
- }
- // Types and functions to support the sorting of Routes
- type routeCandidate struct {
- route Route
- matchesCount int // the number of capturing groups
- literalCount int // the number of literal characters (means those not resulting from template variable substitution)
- nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ‘([^ /]+?)’)
- }
- func (r routeCandidate) expressionToMatch() string {
- return r.route.pathExpr.Source
- }
- func (r routeCandidate) String() string {
- return fmt.Sprintf("(m=%d,l=%d,n=%d)", r.matchesCount, r.literalCount, r.nonDefaultCount)
- }
- type sortableRouteCandidates struct {
- candidates []routeCandidate
- }
- func (rcs *sortableRouteCandidates) Len() int {
- return len(rcs.candidates)
- }
- func (rcs *sortableRouteCandidates) Swap(i, j int) {
- rcs.candidates[i], rcs.candidates[j] = rcs.candidates[j], rcs.candidates[i]
- }
- func (rcs *sortableRouteCandidates) Less(i, j int) bool {
- ci := rcs.candidates[i]
- cj := rcs.candidates[j]
- // primary key
- if ci.literalCount < cj.literalCount {
- return true
- }
- if ci.literalCount > cj.literalCount {
- return false
- }
- // secundary key
- if ci.matchesCount < cj.matchesCount {
- return true
- }
- if ci.matchesCount > cj.matchesCount {
- return false
- }
- // tertiary key
- if ci.nonDefaultCount < cj.nonDefaultCount {
- return true
- }
- if ci.nonDefaultCount > cj.nonDefaultCount {
- return false
- }
- // quaternary key ("source" is interpreted as Path)
- return ci.route.Path < cj.route.Path
- }
- // Types and functions to support the sorting of Dispatchers
- type dispatcherCandidate struct {
- dispatcher *WebService
- finalMatch string
- matchesCount int // the number of capturing groups
- literalCount int // the number of literal characters (means those not resulting from template variable substitution)
- nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ‘([^ /]+?)’)
- }
- type sortableDispatcherCandidates struct {
- candidates []dispatcherCandidate
- }
- func (dc *sortableDispatcherCandidates) Len() int {
- return len(dc.candidates)
- }
- func (dc *sortableDispatcherCandidates) Swap(i, j int) {
- dc.candidates[i], dc.candidates[j] = dc.candidates[j], dc.candidates[i]
- }
- func (dc *sortableDispatcherCandidates) Less(i, j int) bool {
- ci := dc.candidates[i]
- cj := dc.candidates[j]
- // primary key
- if ci.matchesCount < cj.matchesCount {
- return true
- }
- if ci.matchesCount > cj.matchesCount {
- return false
- }
- // secundary key
- if ci.literalCount < cj.literalCount {
- return true
- }
- if ci.literalCount > cj.literalCount {
- return false
- }
- // tertiary key
- return ci.nonDefaultCount < cj.nonDefaultCount
- }
|