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.
12 // RouteFunction declares the signature of a function that can be bound to a Route.
13 type RouteFunction func(*Request, *Response)
15 // RouteSelectionConditionFunction declares the signature of a function that
16 // can be used to add extra conditional logic when selecting whether the route
17 // matches the HTTP request.
18 type RouteSelectionConditionFunction func(httpRequest *http.Request) bool
20 // Route binds a HTTP Method,Path,Consumes combination to a RouteFunction.
25 Path string // webservice root path + described path
26 Function RouteFunction
27 Filters []FilterFunction
28 If []RouteSelectionConditionFunction
30 // cached values for dispatching
33 pathExpr *pathExpression // cached compilation of relativePath as RegExp
39 ParameterDocs []*Parameter
40 ResponseErrors map[int]ResponseError
41 DefaultResponse *ResponseError
42 ReadSample, WriteSample interface{} // structs that model an example request or response payload
44 // Extra information used to store custom information about the route.
45 Metadata map[string]interface{}
47 // marks a route as deprecated
50 //Overrides the container.contentEncodingEnabled
51 contentEncodingEnabled *bool
54 // Initialize for Route
55 func (r *Route) postBuild() {
56 r.pathParts = tokenizePath(r.Path)
59 // Create Request and Response from their http versions
60 func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request, pathParams map[string]string) (*Request, *Response) {
61 wrappedRequest := NewRequest(httpRequest)
62 wrappedRequest.pathParameters = pathParams
63 wrappedRequest.selectedRoutePath = r.Path
64 wrappedResponse := NewResponse(httpWriter)
65 wrappedResponse.requestAccept = httpRequest.Header.Get(HEADER_Accept)
66 wrappedResponse.routeProduces = r.Produces
67 return wrappedRequest, wrappedResponse
70 // dispatchWithFilters call the function after passing through its own filters
71 func (r *Route) dispatchWithFilters(wrappedRequest *Request, wrappedResponse *Response) {
72 if len(r.Filters) > 0 {
73 chain := FilterChain{Filters: r.Filters, Target: r.Function}
74 chain.ProcessFilter(wrappedRequest, wrappedResponse)
77 r.Function(wrappedRequest, wrappedResponse)
81 func stringTrimSpaceCutset(r rune) bool {
85 // Return whether the mimeType matches to what this Route can produce.
86 func (r Route) matchesAccept(mimeTypesWithQuality string) bool {
87 remaining := mimeTypesWithQuality
90 if end := strings.Index(remaining, ","); end == -1 {
91 mimeType, remaining = remaining, ""
93 mimeType, remaining = remaining[:end], remaining[end+1:]
95 if quality := strings.Index(mimeType, ";"); quality != -1 {
96 mimeType = mimeType[:quality]
98 mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset)
99 if mimeType == "*/*" {
102 for _, producibleType := range r.Produces {
103 if producibleType == "*/*" || producibleType == mimeType {
107 if len(remaining) == 0 {
113 // Return whether this Route can consume content with a type specified by mimeTypes (can be empty).
114 func (r Route) matchesContentType(mimeTypes string) bool {
116 if len(r.Consumes) == 0 {
117 // did not specify what it can consume ; any media type (“*/*”) is assumed
121 if len(mimeTypes) == 0 {
122 // idempotent methods with (most-likely or guaranteed) empty content match missing Content-Type
124 if m == "GET" || m == "HEAD" || m == "OPTIONS" || m == "DELETE" || m == "TRACE" {
127 // proceed with default
128 mimeTypes = MIME_OCTET
131 remaining := mimeTypes
134 if end := strings.Index(remaining, ","); end == -1 {
135 mimeType, remaining = remaining, ""
137 mimeType, remaining = remaining[:end], remaining[end+1:]
139 if quality := strings.Index(mimeType, ";"); quality != -1 {
140 mimeType = mimeType[:quality]
142 mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset)
143 for _, consumeableType := range r.Consumes {
144 if consumeableType == "*/*" || consumeableType == mimeType {
148 if len(remaining) == 0 {
154 // Tokenize an URL path using the slash separator ; the result does not have empty tokens
155 func tokenizePath(path string) []string {
159 return strings.Split(strings.Trim(path, "/"), "/")
163 func (r Route) String() string {
164 return r.Method + " " + r.Path
167 // EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses. Overrides the container.contentEncodingEnabled value.
168 func (r Route) EnableContentEncoding(enabled bool) {
169 r.contentEncodingEnabled = &enabled