k3s/vendor/github.com/emicklei/go-restful/route.go

171 lines
5.1 KiB
Go
Raw Normal View History

2019-01-12 04:58:27 +00:00
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 (
"net/http"
"strings"
)
// RouteFunction declares the signature of a function that can be bound to a Route.
type RouteFunction func(*Request, *Response)
2019-09-27 21:51:53 +00:00
// RouteSelectionConditionFunction declares the signature of a function that
// can be used to add extra conditional logic when selecting whether the route
// matches the HTTP request.
type RouteSelectionConditionFunction func(httpRequest *http.Request) bool
2019-01-12 04:58:27 +00:00
// Route binds a HTTP Method,Path,Consumes combination to a RouteFunction.
type Route struct {
Method string
Produces []string
Consumes []string
Path string // webservice root path + described path
Function RouteFunction
Filters []FilterFunction
2019-09-27 21:51:53 +00:00
If []RouteSelectionConditionFunction
2019-01-12 04:58:27 +00:00
// cached values for dispatching
relativePath string
pathParts []string
pathExpr *pathExpression // cached compilation of relativePath as RegExp
// documentation
Doc string
Notes string
Operation string
ParameterDocs []*Parameter
ResponseErrors map[int]ResponseError
2019-09-27 21:51:53 +00:00
DefaultResponse *ResponseError
2019-01-12 04:58:27 +00:00
ReadSample, WriteSample interface{} // structs that model an example request or response payload
// Extra information used to store custom information about the route.
Metadata map[string]interface{}
2019-09-27 21:51:53 +00:00
// marks a route as deprecated
Deprecated bool
//Overrides the container.contentEncodingEnabled
contentEncodingEnabled *bool
2019-01-12 04:58:27 +00:00
}
// Initialize for Route
func (r *Route) postBuild() {
r.pathParts = tokenizePath(r.Path)
}
// Create Request and Response from their http versions
2019-09-27 21:51:53 +00:00
func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request, pathParams map[string]string) (*Request, *Response) {
2019-01-12 04:58:27 +00:00
wrappedRequest := NewRequest(httpRequest)
2019-09-27 21:51:53 +00:00
wrappedRequest.pathParameters = pathParams
2019-01-12 04:58:27 +00:00
wrappedRequest.selectedRoutePath = r.Path
wrappedResponse := NewResponse(httpWriter)
wrappedResponse.requestAccept = httpRequest.Header.Get(HEADER_Accept)
wrappedResponse.routeProduces = r.Produces
return wrappedRequest, wrappedResponse
}
// dispatchWithFilters call the function after passing through its own filters
func (r *Route) dispatchWithFilters(wrappedRequest *Request, wrappedResponse *Response) {
if len(r.Filters) > 0 {
chain := FilterChain{Filters: r.Filters, Target: r.Function}
chain.ProcessFilter(wrappedRequest, wrappedResponse)
} else {
// unfiltered
r.Function(wrappedRequest, wrappedResponse)
}
}
2019-09-27 21:51:53 +00:00
func stringTrimSpaceCutset(r rune) bool {
return r == ' '
}
2019-01-12 04:58:27 +00:00
// Return whether the mimeType matches to what this Route can produce.
func (r Route) matchesAccept(mimeTypesWithQuality string) bool {
2019-09-27 21:51:53 +00:00
remaining := mimeTypesWithQuality
for {
var mimeType string
if end := strings.Index(remaining, ","); end == -1 {
mimeType, remaining = remaining, ""
2019-01-12 04:58:27 +00:00
} else {
2019-09-27 21:51:53 +00:00
mimeType, remaining = remaining[:end], remaining[end+1:]
2019-01-12 04:58:27 +00:00
}
2019-09-27 21:51:53 +00:00
if quality := strings.Index(mimeType, ";"); quality != -1 {
mimeType = mimeType[:quality]
}
mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset)
if mimeType == "*/*" {
2019-01-12 04:58:27 +00:00
return true
}
for _, producibleType := range r.Produces {
2019-09-27 21:51:53 +00:00
if producibleType == "*/*" || producibleType == mimeType {
2019-01-12 04:58:27 +00:00
return true
}
}
2019-09-27 21:51:53 +00:00
if len(remaining) == 0 {
return false
}
2019-01-12 04:58:27 +00:00
}
}
// Return whether this Route can consume content with a type specified by mimeTypes (can be empty).
func (r Route) matchesContentType(mimeTypes string) bool {
if len(r.Consumes) == 0 {
// did not specify what it can consume ; any media type (“*/*”) is assumed
return true
}
if len(mimeTypes) == 0 {
// idempotent methods with (most-likely or guaranteed) empty content match missing Content-Type
m := r.Method
if m == "GET" || m == "HEAD" || m == "OPTIONS" || m == "DELETE" || m == "TRACE" {
return true
}
// proceed with default
mimeTypes = MIME_OCTET
}
2019-09-27 21:51:53 +00:00
remaining := mimeTypes
for {
var mimeType string
if end := strings.Index(remaining, ","); end == -1 {
mimeType, remaining = remaining, ""
2019-01-12 04:58:27 +00:00
} else {
2019-09-27 21:51:53 +00:00
mimeType, remaining = remaining[:end], remaining[end+1:]
}
if quality := strings.Index(mimeType, ";"); quality != -1 {
mimeType = mimeType[:quality]
2019-01-12 04:58:27 +00:00
}
2019-09-27 21:51:53 +00:00
mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset)
2019-01-12 04:58:27 +00:00
for _, consumeableType := range r.Consumes {
2019-09-27 21:51:53 +00:00
if consumeableType == "*/*" || consumeableType == mimeType {
2019-01-12 04:58:27 +00:00
return true
}
}
2019-09-27 21:51:53 +00:00
if len(remaining) == 0 {
return false
2019-01-12 04:58:27 +00:00
}
}
}
// Tokenize an URL path using the slash separator ; the result does not have empty tokens
func tokenizePath(path string) []string {
if "/" == path {
2019-09-27 21:51:53 +00:00
return nil
2019-01-12 04:58:27 +00:00
}
return strings.Split(strings.Trim(path, "/"), "/")
}
// for debugging
func (r Route) String() string {
return r.Method + " " + r.Path
}
2019-09-27 21:51:53 +00:00
// EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses. Overrides the container.contentEncodingEnabled value.
func (r Route) EnableContentEncoding(enabled bool) {
r.contentEncodingEnabled = &enabled
}