...
1 package restful
2
3
4
5
6
7 import (
8 "net/http"
9 "strings"
10 )
11
12
13 type RouteFunction func(*Request, *Response)
14
15
16
17
18 type RouteSelectionConditionFunction func(httpRequest *http.Request) bool
19
20
21 type Route struct {
22 ExtensionProperties
23 Method string
24 Produces []string
25 Consumes []string
26 Path string
27 Function RouteFunction
28 Filters []FilterFunction
29 If []RouteSelectionConditionFunction
30
31
32 relativePath string
33 pathParts []string
34 pathExpr *pathExpression
35
36
37 Doc string
38 Notes string
39 Operation string
40 ParameterDocs []*Parameter
41 ResponseErrors map[int]ResponseError
42 DefaultResponse *ResponseError
43 ReadSample, WriteSample interface{}
44 WriteSamples []interface{}
45
46
47 Metadata map[string]interface{}
48
49
50 Deprecated bool
51
52
53 contentEncodingEnabled *bool
54
55
56 hasCustomVerb bool
57
58
59
60
61 allowedMethodsWithoutContentType []string
62 }
63
64
65 func (r *Route) postBuild() {
66 r.pathParts = tokenizePath(r.Path)
67 r.hasCustomVerb = hasCustomVerb(r.Path)
68 }
69
70
71 func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request, pathParams map[string]string) (*Request, *Response) {
72 wrappedRequest := NewRequest(httpRequest)
73 wrappedRequest.pathParameters = pathParams
74 wrappedRequest.selectedRoute = r
75 wrappedResponse := NewResponse(httpWriter)
76 wrappedResponse.requestAccept = httpRequest.Header.Get(HEADER_Accept)
77 wrappedResponse.routeProduces = r.Produces
78 return wrappedRequest, wrappedResponse
79 }
80
81 func stringTrimSpaceCutset(r rune) bool {
82 return r == ' '
83 }
84
85
86 func (r Route) matchesAccept(mimeTypesWithQuality string) bool {
87 remaining := mimeTypesWithQuality
88 for {
89 var mimeType string
90 if end := strings.Index(remaining, ","); end == -1 {
91 mimeType, remaining = remaining, ""
92 } else {
93 mimeType, remaining = remaining[:end], remaining[end+1:]
94 }
95 if quality := strings.Index(mimeType, ";"); quality != -1 {
96 mimeType = mimeType[:quality]
97 }
98 mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset)
99 if mimeType == "*/*" {
100 return true
101 }
102 for _, producibleType := range r.Produces {
103 if producibleType == "*/*" || producibleType == mimeType {
104 return true
105 }
106 }
107 if len(remaining) == 0 {
108 return false
109 }
110 }
111 }
112
113
114 func (r Route) matchesContentType(mimeTypes string) bool {
115
116 if len(r.Consumes) == 0 {
117
118 return true
119 }
120
121 if len(mimeTypes) == 0 {
122
123 m := r.Method
124
125 if len(r.allowedMethodsWithoutContentType) > 0 {
126 for _, each := range r.allowedMethodsWithoutContentType {
127 if m == each {
128 return true
129 }
130 }
131 } else {
132 if m == "GET" || m == "HEAD" || m == "OPTIONS" || m == "DELETE" || m == "TRACE" {
133 return true
134 }
135 }
136
137 mimeTypes = MIME_OCTET
138 }
139
140 remaining := mimeTypes
141 for {
142 var mimeType string
143 if end := strings.Index(remaining, ","); end == -1 {
144 mimeType, remaining = remaining, ""
145 } else {
146 mimeType, remaining = remaining[:end], remaining[end+1:]
147 }
148 if quality := strings.Index(mimeType, ";"); quality != -1 {
149 mimeType = mimeType[:quality]
150 }
151 mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset)
152 for _, consumeableType := range r.Consumes {
153 if consumeableType == "*/*" || consumeableType == mimeType {
154 return true
155 }
156 }
157 if len(remaining) == 0 {
158 return false
159 }
160 }
161 }
162
163
164 func tokenizePath(path string) []string {
165 if "/" == path {
166 return nil
167 }
168 if TrimRightSlashEnabled {
169
170 return strings.Split(strings.Trim(path, "/"), "/")
171 } else {
172
173 return strings.Split(strings.TrimLeft(path, "/"), "/")
174 }
175 }
176
177
178 func (r *Route) String() string {
179 return r.Method + " " + r.Path
180 }
181
182
183 func (r *Route) EnableContentEncoding(enabled bool) {
184 r.contentEncodingEnabled = &enabled
185 }
186
187
188
189
190
191 var TrimRightSlashEnabled = true
192
View as plain text