vendor: revendor metrics-server, custom-metrics-apiserver

This commit is contained in:
Sergiusz Urbaniak 2020-10-28 15:52:52 +01:00
parent 752ce84723
commit 523aa52367
1010 changed files with 91458 additions and 29107 deletions

View file

@ -1,4 +1,6 @@
language: go
arch:
- amd64
- ppc64le
go:
- 1.x
- 1.x

View file

@ -52,6 +52,12 @@ func (prop *ModelProperty) setType(field reflect.StructField) {
}
}
func (prop *ModelProperty) setFormat(field reflect.StructField) {
if tag := field.Tag.Get("format"); tag != "" {
prop.Format = tag
}
}
func (prop *ModelProperty) setMinimum(field reflect.StructField) {
if tag := field.Tag.Get("minimum"); tag != "" {
prop.Minimum = tag
@ -78,4 +84,5 @@ func (prop *ModelProperty) setPropertyMetadata(field reflect.StructField) {
prop.setUniqueItems(field)
prop.setDefaultValue(field)
prop.setType(field)
prop.setFormat(field)
}

View file

@ -68,3 +68,4 @@ examples/restful-html-template
s.html
restful-path-tail
.idea

View file

@ -3,4 +3,11 @@ language: go
go:
- 1.x
script: go test -v
before_install:
- go test -v
script:
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)

View file

@ -1,59 +1,97 @@
## Change history of go-restful
# Change history of go-restful
v2.9.5
## [v2.14.3] - 2020-08-31
- Fixed duplicate compression in dispatch. #449
## [v2.14.2] - 2020-08-31
- Added check on writer to prevent compression of response twice. #447
## [v2.14.0] - 2020-08-19
- Enable content encoding on Handle and ServeHTTP (#446)
- List available representations in 406 body (#437)
- Convert to string using rune() (#443)
## [v2.13.0] - 2020-06-21
- 405 Method Not Allowed must have Allow header (#436)
- add field allowedMethodsWithoutContentType (#424)
## v2.12.0
- support describing response headers (#426)
- fix openapi examples (#425)
- merge v3 fix (#422)
## v2.11.1
- fix WriteError return value (#415)
## v2.11.0
- allow prefix and suffix in path variable expression (#414)
## v2.9.6
- support google custome verb (#413)
## v2.9.5
- fix panic in Response.WriteError if err == nil
v2.9.4
## v2.9.4
- fix issue #400 , parsing mime type quality
- Route Builder added option for contentEncodingEnabled (#398)
v2.9.3
## v2.9.3
- Avoid return of 415 Unsupported Media Type when request body is empty (#396)
v2.9.2
## v2.9.2
- Reduce allocations in per-request methods to improve performance (#395)
v2.9.1
## v2.9.1
- Fix issue with default responses and invalid status code 0. (#393)
v2.9.0
## v2.9.0
- add per Route content encoding setting (overrides container setting)
v2.8.0
## v2.8.0
- add Request.QueryParameters()
- add json-iterator (via build tag)
- disable vgo module (until log is moved)
v2.7.1
## v2.7.1
- add vgo module
v2.6.1
## v2.6.1
- add JSONNewDecoderFunc to allow custom JSON Decoder usage (go 1.10+)
v2.6.0
## v2.6.0
- Make JSR 311 routing and path param processing consistent
- Adding description to RouteBuilder.Reads()
- Update example for Swagger12 and OpenAPI
2017-09-13
## 2017-09-13
- added route condition functions using `.If(func)` in route building.
2017-02-16
## 2017-02-16
- solved issue #304, make operation names unique
2017-01-30
## 2017-01-30
[IMPORTANT] For swagger users, change your import statement to:
swagger "github.com/emicklei/go-restful-swagger12"
@ -61,60 +99,60 @@ v2.6.0
- moved swagger 1.2 code to go-restful-swagger12
- created TAG 2.0.0
2017-01-27
## 2017-01-27
- remove defer request body close
- expose Dispatch for testing filters and Routefunctions
- swagger response model cannot be array
- created TAG 1.0.0
2016-12-22
## 2016-12-22
- (API change) Remove code related to caching request content. Removes SetCacheReadEntity(doCache bool)
2016-11-26
## 2016-11-26
- Default change! now use CurlyRouter (was RouterJSR311)
- Default change! no more caching of request content
- Default change! do not recover from panics
2016-09-22
## 2016-09-22
- fix the DefaultRequestContentType feature
2016-02-14
## 2016-02-14
- take the qualify factor of the Accept header mediatype into account when deciding the contentype of the response
- add constructors for custom entity accessors for xml and json
2015-09-27
## 2015-09-27
- rename new WriteStatusAnd... to WriteHeaderAnd... for consistency
2015-09-25
## 2015-09-25
- fixed problem with changing Header after WriteHeader (issue 235)
2015-09-14
## 2015-09-14
- changed behavior of WriteHeader (immediate write) and WriteEntity (no status write)
- added support for custom EntityReaderWriters.
2015-08-06
## 2015-08-06
- add support for reading entities from compressed request content
- use sync.Pool for compressors of http response and request body
- add Description to Parameter for documentation in Swagger UI
2015-03-20
## 2015-03-20
- add configurable logging
2015-03-18
## 2015-03-18
- if not specified, the Operation is derived from the Route function
2015-03-17
## 2015-03-17
- expose Parameter creation functions
- make trace logger an interface
@ -123,26 +161,26 @@ v2.6.0
- JSR311 router now handles wildcards
- add Notes to Route
2014-11-27
## 2014-11-27
- (api add) PrettyPrint per response. (as proposed in #167)
2014-11-12
## 2014-11-12
- (api add) ApiVersion(.) for documentation in Swagger UI
2014-11-10
## 2014-11-10
- (api change) struct fields tagged with "description" show up in Swagger UI
2014-10-31
## 2014-10-31
- (api change) ReturnsError -> Returns
- (api add) RouteBuilder.Do(aBuilder) for DRY use of RouteBuilder
- fix swagger nested structs
- sort Swagger response messages by code
2014-10-23
## 2014-10-23
- (api add) ReturnsError allows you to document Http codes in swagger
- fixed problem with greedy CurlyRouter
@ -156,73 +194,73 @@ v2.6.0
- (api add) added AllowedDomains in CORS
- (api add) ParameterNamed for detailed documentation
2014-04-16
## 2014-04-16
- (api add) expose constructor of Request for testing.
2014-06-27
## 2014-06-27
- (api add) ParameterNamed gives access to a Parameter definition and its data (for further specification).
- (api add) SetCacheReadEntity allow scontrol over whether or not the request body is being cached (default true for compatibility reasons).
2014-07-03
## 2014-07-03
- (api add) CORS can be configured with a list of allowed domains
2014-03-12
## 2014-03-12
- (api add) Route path parameters can use wildcard or regular expressions. (requires CurlyRouter)
2014-02-26
## 2014-02-26
- (api add) Request now provides information about the matched Route, see method SelectedRoutePath
2014-02-17
## 2014-02-17
- (api change) renamed parameter constants (go-lint checks)
2014-01-10
## 2014-01-10
- (api add) support for CloseNotify, see http://golang.org/pkg/net/http/#CloseNotifier
2014-01-07
## 2014-01-07
- (api change) Write* methods in Response now return the error or nil.
- added example of serving HTML from a Go template.
- fixed comparing Allowed headers in CORS (is now case-insensitive)
2013-11-13
## 2013-11-13
- (api add) Response knows how many bytes are written to the response body.
2013-10-29
## 2013-10-29
- (api add) RecoverHandler(handler RecoverHandleFunction) to change how panic recovery is handled. Default behavior is to log and return a stacktrace. This may be a security issue as it exposes sourcecode information.
2013-10-04
## 2013-10-04
- (api add) Response knows what HTTP status has been written
- (api add) Request can have attributes (map of string->interface, also called request-scoped variables
2013-09-12
## 2013-09-12
- (api change) Router interface simplified
- Implemented CurlyRouter, a Router that does not use|allow regular expressions in paths
2013-08-05
## 2013-08-05
- add OPTIONS support
- add CORS support
2013-08-27
## 2013-08-27
- fixed some reported issues (see github)
- (api change) deprecated use of WriteError; use WriteErrorString instead
2014-04-15
## 2014-04-15
- (fix) v1.0.1 tag: fix Issue 111: WriteErrorString
2013-08-08
## 2013-08-08
- (api add) Added implementation Container: a WebServices collection with its own http.ServeMux allowing multiple endpoints per program. Existing uses of go-restful will register their services to the DefaultContainer.
- (api add) the swagger package has be extended to have a UI per container.
@ -235,38 +273,38 @@ Important API changes:
- (api remove) package variable EnableContentEncoding no longer works ; use restful.DefaultContainer.EnableContentEncoding(true) instead.
2013-07-06
## 2013-07-06
- (api add) Added support for response encoding (gzip and deflate(zlib)). This feature is disabled on default (for backwards compatibility). Use restful.EnableContentEncoding = true in your initialization to enable this feature.
2013-06-19
## 2013-06-19
- (improve) DoNotRecover option, moved request body closer, improved ReadEntity
2013-06-03
## 2013-06-03
- (api change) removed Dispatcher interface, hide PathExpression
- changed receiver names of type functions to be more idiomatic Go
2013-06-02
## 2013-06-02
- (optimize) Cache the RegExp compilation of Paths.
2013-05-22
## 2013-05-22
- (api add) Added support for request/response filter functions
2013-05-18
## 2013-05-18
- (api add) Added feature to change the default Http Request Dispatch function (travis cline)
- (api change) Moved Swagger Webservice to swagger package (see example restful-user)
[2012-11-14 .. 2013-05-18>
## [2012-11-14 .. 2013-05-18>
- See https://github.com/emicklei/go-restful/commits
2012-11-14
## 2012-11-14
- Initial commit

View file

@ -1,7 +1,8 @@
all: test
test:
go test -v .
go vet .
go test -cover -v .
ex:
cd examples && ls *.go | xargs go build -o /tmp/ignore

View file

@ -4,7 +4,8 @@ package for building REST-style Web Services using Google Go
[![Build Status](https://travis-ci.org/emicklei/go-restful.png)](https://travis-ci.org/emicklei/go-restful)
[![Go Report Card](https://goreportcard.com/badge/github.com/emicklei/go-restful)](https://goreportcard.com/report/github.com/emicklei/go-restful)
[![GoDoc](https://godoc.org/github.com/emicklei/go-restful?status.svg)](https://godoc.org/github.com/emicklei/go-restful)
[![GoDoc](https://godoc.org/github.com/emicklei/go-restful?status.svg)](https://pkg.go.dev/github.com/emicklei/go-restful)
[![codecov](https://codecov.io/gh/emicklei/go-restful/branch/master/graph/badge.svg)](https://codecov.io/gh/emicklei/go-restful)
- [Code examples](https://github.com/emicklei/go-restful/tree/master/examples)
@ -18,6 +19,28 @@ REST asks developers to use HTTP methods explicitly and in a way that's consiste
- PATCH = Update partial content of a resource
- OPTIONS = Get information about the communication options for the request URI
### Usage
#### Without Go Modules
All versions up to `v2.*.*` (on the master) are not supporting Go modules.
```
import (
restful "github.com/emicklei/go-restful"
)
```
#### Using Go Modules
As of version `v3.0.0` (on the v3 branch), this package supports Go modules.
```
import (
restful "github.com/emicklei/go-restful/v3"
)
```
### Example
```Go
@ -43,9 +66,9 @@ func (u UserResource) findUser(request *restful.Request, response *restful.Respo
### Features
- Routes for request &#8594; function mapping with path parameter (e.g. {id}) support
- Routes for request &#8594; function mapping with path parameter (e.g. {id} but also prefix_{var} and {var}_suffix) support
- Configurable router:
- (default) Fast routing algorithm that allows static elements, regular expressions and dynamic parameters in the URL path (e.g. /meetings/{id} or /static/{subpath:*}
- (default) Fast routing algorithm that allows static elements, [google custom method](https://cloud.google.com/apis/design/custom_methods), regular expressions and dynamic parameters in the URL path (e.g. /resource/name:customVerb, /meetings/{id} or /static/{subpath:*})
- Routing algorithm after [JSR311](http://jsr311.java.net/nonav/releases/1.1/spec/spec.html) that is implemented using (but does **not** accept) regular expressions
- Request API for reading structs from JSON/XML and accesing parameters (path,query,header)
- Response API for writing structs to JSON/XML and setting headers
@ -85,4 +108,4 @@ TODO: write examples of these.
Type ```git shortlog -s``` for a full list of contributors.
© 2012 - 2018, http://ernestmicklei.com. MIT License. Contributions are welcome.
© 2012 - 2020, http://ernestmicklei.com. MIT License. Contributions are welcome.

View file

@ -185,6 +185,11 @@ func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter)
// when a ServiceError is returned during route selection. Default implementation
// calls resp.WriteErrorString(err.Code, err.Message)
func writeServiceError(err ServiceError, req *Request, resp *Response) {
for header, values := range err.Header {
for _, value := range values {
resp.Header().Add(header, value)
}
}
resp.WriteErrorString(err.Code, err.Message)
}
@ -201,6 +206,7 @@ func (c *Container) Dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
// Dispatch the incoming Http Request to a matching WebService.
func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {
// so we can assign a compressing one later
writer := httpWriter
// CompressingResponseWriter should be closed after all operations are done
@ -231,28 +237,8 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
c.webServices,
httpRequest)
}()
// Detect if compression is needed
// assume without compression, test for override
contentEncodingEnabled := c.contentEncodingEnabled
if route != nil && route.contentEncodingEnabled != nil {
contentEncodingEnabled = *route.contentEncodingEnabled
}
if contentEncodingEnabled {
doCompress, encoding := wantsCompressedResponse(httpRequest)
if doCompress {
var err error
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
if err != nil {
log.Print("unable to install compressor: ", err)
httpWriter.WriteHeader(http.StatusInternalServerError)
return
}
}
}
if err != nil {
// a non-200 response has already been written
// a non-200 response (may be compressed) has already been written
// run container filters anyway ; they should not touch the response...
chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
switch err.(type) {
@ -265,6 +251,29 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer))
return
}
// Unless httpWriter is already an CompressingResponseWriter see if we need to install one
if _, isCompressing := httpWriter.(*CompressingResponseWriter); !isCompressing {
// Detect if compression is needed
// assume without compression, test for override
contentEncodingEnabled := c.contentEncodingEnabled
if route != nil && route.contentEncodingEnabled != nil {
contentEncodingEnabled = *route.contentEncodingEnabled
}
if contentEncodingEnabled {
doCompress, encoding := wantsCompressedResponse(httpRequest)
if doCompress {
var err error
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
if err != nil {
log.Print("unable to install compressor: ", err)
httpWriter.WriteHeader(http.StatusInternalServerError)
return
}
}
}
}
pathProcessor, routerProcessesPath := c.router.(PathProcessor)
if !routerProcessesPath {
pathProcessor = defaultPathProcessor{}
@ -272,16 +281,13 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
pathParams := pathProcessor.ExtractParameters(route, webService, httpRequest.URL.Path)
wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest, pathParams)
// pass through filters (if any)
if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {
if size := len(c.containerFilters) + len(webService.filters) + len(route.Filters); size > 0 {
// compose filter chain
allFilters := []FilterFunction{}
allFilters := make([]FilterFunction, 0, size)
allFilters = append(allFilters, c.containerFilters...)
allFilters = append(allFilters, webService.filters...)
allFilters = append(allFilters, route.Filters...)
chain := FilterChain{Filters: allFilters, Target: func(req *Request, resp *Response) {
// handle request by route after passing all filters
route.Function(wrappedRequest, wrappedResponse)
}}
chain := FilterChain{Filters: allFilters, Target: route.Function}
chain.ProcessFilter(wrappedRequest, wrappedResponse)
} else {
// no filters, handle request by route
@ -299,13 +305,75 @@ func fixedPrefixPath(pathspec string) string {
}
// ServeHTTP implements net/http.Handler therefore a Container can be a Handler in a http.Server
func (c *Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) {
c.ServeMux.ServeHTTP(httpwriter, httpRequest)
func (c *Container) ServeHTTP(httpWriter http.ResponseWriter, httpRequest *http.Request) {
// Skip, if content encoding is disabled
if !c.contentEncodingEnabled {
c.ServeMux.ServeHTTP(httpWriter, httpRequest)
return
}
// content encoding is enabled
// Skip, if httpWriter is already an CompressingResponseWriter
if _, ok := httpWriter.(*CompressingResponseWriter); ok {
c.ServeMux.ServeHTTP(httpWriter, httpRequest)
return
}
writer := httpWriter
// CompressingResponseWriter should be closed after all operations are done
defer func() {
if compressWriter, ok := writer.(*CompressingResponseWriter); ok {
compressWriter.Close()
}
}()
doCompress, encoding := wantsCompressedResponse(httpRequest)
if doCompress {
var err error
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
if err != nil {
log.Print("unable to install compressor: ", err)
httpWriter.WriteHeader(http.StatusInternalServerError)
return
}
}
c.ServeMux.ServeHTTP(writer, httpRequest)
}
// Handle registers the handler for the given pattern. If a handler already exists for pattern, Handle panics.
func (c *Container) Handle(pattern string, handler http.Handler) {
c.ServeMux.Handle(pattern, handler)
c.ServeMux.Handle(pattern, http.HandlerFunc(func(httpWriter http.ResponseWriter, httpRequest *http.Request) {
// Skip, if httpWriter is already an CompressingResponseWriter
if _, ok := httpWriter.(*CompressingResponseWriter); ok {
handler.ServeHTTP(httpWriter, httpRequest)
return
}
writer := httpWriter
// CompressingResponseWriter should be closed after all operations are done
defer func() {
if compressWriter, ok := writer.(*CompressingResponseWriter); ok {
compressWriter.Close()
}
}()
if c.contentEncodingEnabled {
doCompress, encoding := wantsCompressedResponse(httpRequest)
if doCompress {
var err error
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
if err != nil {
log.Print("unable to install compressor: ", err)
httpWriter.WriteHeader(http.StatusInternalServerError)
return
}
}
}
handler.ServeHTTP(writer, httpRequest)
}))
}
// HandleWithFilter registers the handler for the given pattern.
@ -319,7 +387,7 @@ func (c *Container) HandleWithFilter(pattern string, handler http.Handler) {
}
chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
handler.ServeHTTP(httpResponse, httpRequest)
handler.ServeHTTP(resp, req.Request)
}}
chain.ProcessFilter(NewRequest(httpRequest), NewResponse(httpResponse))
}

View file

@ -47,7 +47,7 @@ func (c CurlyRouter) SelectRoute(
func (c CurlyRouter) selectRoutes(ws *WebService, requestTokens []string) sortableCurlyRoutes {
candidates := make(sortableCurlyRoutes, 0, 8)
for _, each := range ws.routes {
matches, paramCount, staticCount := c.matchesRouteByPathTokens(each.pathParts, requestTokens)
matches, paramCount, staticCount := c.matchesRouteByPathTokens(each.pathParts, requestTokens, each.hasCustomVerb)
if matches {
candidates.add(curlyRoute{each, paramCount, staticCount}) // TODO make sure Routes() return pointers?
}
@ -57,7 +57,7 @@ func (c CurlyRouter) selectRoutes(ws *WebService, requestTokens []string) sortab
}
// matchesRouteByPathTokens computes whether it matches, howmany parameters do match and what the number of static path elements are.
func (c CurlyRouter) matchesRouteByPathTokens(routeTokens, requestTokens []string) (matches bool, paramCount int, staticCount int) {
func (c CurlyRouter) matchesRouteByPathTokens(routeTokens, requestTokens []string, routeHasCustomVerb bool) (matches bool, paramCount int, staticCount int) {
if len(routeTokens) < len(requestTokens) {
// proceed in matching only if last routeToken is wildcard
count := len(routeTokens)
@ -72,6 +72,15 @@ func (c CurlyRouter) matchesRouteByPathTokens(routeTokens, requestTokens []strin
return false, 0, 0
}
requestToken := requestTokens[i]
if routeHasCustomVerb && hasCustomVerb(routeToken){
if !isMatchCustomVerb(routeToken, requestToken) {
return false, 0, 0
}
staticCount++
requestToken = removeCustomVerb(requestToken)
routeToken = removeCustomVerb(routeToken)
}
if strings.HasPrefix(routeToken, "{") {
paramCount++
if colon := strings.Index(routeToken, ":"); colon != -1 {

29
vendor/github.com/emicklei/go-restful/custom_verb.go generated vendored Normal file
View file

@ -0,0 +1,29 @@
package restful
import (
"fmt"
"regexp"
)
var (
customVerbReg = regexp.MustCompile(":([A-Za-z]+)$")
)
func hasCustomVerb(routeToken string) bool {
return customVerbReg.MatchString(routeToken)
}
func isMatchCustomVerb(routeToken string, pathToken string) bool {
rs := customVerbReg.FindStringSubmatch(routeToken)
if len(rs) < 2 {
return false
}
customVerb := rs[1]
specificVerbReg := regexp.MustCompile(fmt.Sprintf(":%s$", customVerb))
return specificVerbReg.MatchString(pathToken)
}
func removeCustomVerb(str string) string {
return customVerbReg.ReplaceAllString(str, "")
}

View file

@ -9,6 +9,7 @@ import (
"fmt"
"net/http"
"sort"
"strings"
)
// RouterJSR311 implements the flow for matching Requests to Routes (and consequently Resource Functions)
@ -98,7 +99,18 @@ func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*R
if trace {
traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(previous), httpRequest.Method)
}
return nil, NewError(http.StatusMethodNotAllowed, "405: Method Not Allowed")
allowed := []string{}
allowedLoop:
for _, candidate := range previous {
for _, method := range allowed {
if method == candidate.Method {
continue allowedLoop
}
}
allowed = append(allowed, candidate.Method)
}
header := http.Header{"Allow": []string{strings.Join(allowed, ", ")}}
return nil, NewErrorWithHeader(http.StatusMethodNotAllowed, "405: Method Not Allowed", header)
}
// content-type
@ -135,7 +147,14 @@ func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*R
if trace {
traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(previous), accept)
}
return nil, NewError(http.StatusNotAcceptable, "406: Not Acceptable")
available := []string{}
for _, candidate := range previous {
available = append(available, candidate.Produces...)
}
return nil, NewError(
http.StatusNotAcceptable,
fmt.Sprintf("406: Not Acceptable\n\nAvailable representations: %s", strings.Join(available, ", ")),
)
}
// return r.bestMatchByMedia(outputMediaOk, contentType, accept), nil
return candidates[0], nil

View file

@ -29,7 +29,12 @@ func (d defaultPathProcessor) ExtractParameters(r *Route, _ *WebService, urlPath
} else {
value = urlParts[i]
}
if strings.HasPrefix(key, "{") { // path-parameter
if r.hasCustomVerb && hasCustomVerb(key) {
key = removeCustomVerb(key)
value = removeCustomVerb(value)
}
if strings.Index(key, "{") > -1 { // path-parameter
if colon := strings.Index(key, ":"); colon != -1 {
// extract by regex
regPart := key[colon+1 : len(key)-1]
@ -42,7 +47,13 @@ func (d defaultPathProcessor) ExtractParameters(r *Route, _ *WebService, urlPath
}
} else {
// without enclosing {}
pathParameters[key[1:len(key)-1]] = value
startIndex := strings.Index(key, "{")
endKeyIndex := strings.Index(key, "}")
suffixLength := len(key) - endKeyIndex - 1
endValueIndex := len(value) - suffixLength
pathParameters[key[startIndex+1:endKeyIndex]] = value[startIndex:endValueIndex]
}
}
}

View file

@ -174,15 +174,16 @@ func (r *Response) WriteHeaderAndJson(status int, value interface{}, contentType
return writeJSON(r, status, contentType, value)
}
// WriteError write the http status and the error string on the response. err can be nil.
func (r *Response) WriteError(httpStatus int, err error) error {
// WriteError writes the http status and the error string on the response. err can be nil.
// Return an error if writing was not succesful.
func (r *Response) WriteError(httpStatus int, err error) (writeErr error) {
r.err = err
if err == nil {
r.WriteErrorString(httpStatus, "")
writeErr = r.WriteErrorString(httpStatus, "")
} else {
r.WriteErrorString(httpStatus, err.Error())
writeErr = r.WriteErrorString(httpStatus, err.Error())
}
return err
return writeErr
}
// WriteServiceError is a convenience method for a responding with a status and a ServiceError

View file

@ -49,11 +49,20 @@ type Route struct {
//Overrides the container.contentEncodingEnabled
contentEncodingEnabled *bool
// indicate route path has custom verb
hasCustomVerb bool
// if a request does not include a content-type header then
// depending on the method, it may return a 415 Unsupported Media
// Must have uppercase HTTP Method names such as GET,HEAD,OPTIONS,...
allowedMethodsWithoutContentType []string
}
// Initialize for Route
func (r *Route) postBuild() {
r.pathParts = tokenizePath(r.Path)
r.hasCustomVerb = hasCustomVerb(r.Path)
}
// Create Request and Response from their http versions
@ -67,17 +76,6 @@ func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest
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)
}
}
func stringTrimSpaceCutset(r rune) bool {
return r == ' '
}
@ -121,8 +119,17 @@ func (r Route) matchesContentType(mimeTypes string) bool {
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
// if route specifies less or non-idempotent methods then use that
if len(r.allowedMethodsWithoutContentType) > 0 {
for _, each := range r.allowedMethodsWithoutContentType {
if m == each {
return true
}
}
} else {
if m == "GET" || m == "HEAD" || m == "OPTIONS" || m == "DELETE" || m == "TRACE" {
return true
}
}
// proceed with default
mimeTypes = MIME_OCTET

View file

@ -17,14 +17,15 @@ import (
// RouteBuilder is a helper to construct Routes.
type RouteBuilder struct {
rootPath string
currentPath string
produces []string
consumes []string
httpMethod string // required
function RouteFunction // required
filters []FilterFunction
conditions []RouteSelectionConditionFunction
rootPath string
currentPath string
produces []string
consumes []string
httpMethod string // required
function RouteFunction // required
filters []FilterFunction
conditions []RouteSelectionConditionFunction
allowedMethodsWithoutContentType []string // see Route
typeNameHandleFunc TypeNameHandleFunction // required
@ -176,6 +177,15 @@ func (b *RouteBuilder) Returns(code int, message string, model interface{}) *Rou
return b
}
// ReturnsWithHeaders is similar to Returns, but can specify response headers
func (b *RouteBuilder) ReturnsWithHeaders(code int, message string, model interface{}, headers map[string]Header) *RouteBuilder {
b.Returns(code, message, model)
err := b.errorMap[code]
err.Headers = headers
b.errorMap[code] = err
return b
}
// DefaultReturns is a special Returns call that sets the default of the response.
func (b *RouteBuilder) DefaultReturns(message string, model interface{}) *RouteBuilder {
b.defaultResponse = &ResponseError{
@ -200,14 +210,41 @@ func (b *RouteBuilder) Deprecate() *RouteBuilder {
return b
}
// AllowedMethodsWithoutContentType overides the default list GET,HEAD,OPTIONS,DELETE,TRACE
// If a request does not include a content-type header then
// depending on the method, it may return a 415 Unsupported Media.
// Must have uppercase HTTP Method names such as GET,HEAD,OPTIONS,...
func (b *RouteBuilder) AllowedMethodsWithoutContentType(methods []string) *RouteBuilder {
b.allowedMethodsWithoutContentType = methods
return b
}
// ResponseError represents a response; not necessarily an error.
type ResponseError struct {
Code int
Message string
Model interface{}
Headers map[string]Header
IsDefault bool
}
// Header describes a header for a response of the API
//
// For more information: http://goo.gl/8us55a#headerObject
type Header struct {
*Items
Description string
}
// Items describe swagger simple schemas for headers
type Items struct {
Type string
Format string
Items *Items
CollectionFormat string
Default interface{}
}
func (b *RouteBuilder) servicePath(path string) *RouteBuilder {
b.rootPath = path
return b
@ -276,26 +313,27 @@ func (b *RouteBuilder) Build() Route {
operationName = nameOfFunction(b.function)
}
route := Route{
Method: b.httpMethod,
Path: concatPath(b.rootPath, b.currentPath),
Produces: b.produces,
Consumes: b.consumes,
Function: b.function,
Filters: b.filters,
If: b.conditions,
relativePath: b.currentPath,
pathExpr: pathExpr,
Doc: b.doc,
Notes: b.notes,
Operation: operationName,
ParameterDocs: b.parameters,
ResponseErrors: b.errorMap,
DefaultResponse: b.defaultResponse,
ReadSample: b.readSample,
WriteSample: b.writeSample,
Metadata: b.metadata,
Deprecated: b.deprecated,
contentEncodingEnabled: b.contentEncodingEnabled,
Method: b.httpMethod,
Path: concatPath(b.rootPath, b.currentPath),
Produces: b.produces,
Consumes: b.consumes,
Function: b.function,
Filters: b.filters,
If: b.conditions,
relativePath: b.currentPath,
pathExpr: pathExpr,
Doc: b.doc,
Notes: b.notes,
Operation: operationName,
ParameterDocs: b.parameters,
ResponseErrors: b.errorMap,
DefaultResponse: b.defaultResponse,
ReadSample: b.readSample,
WriteSample: b.writeSample,
Metadata: b.metadata,
Deprecated: b.deprecated,
contentEncodingEnabled: b.contentEncodingEnabled,
allowedMethodsWithoutContentType: b.allowedMethodsWithoutContentType,
}
route.postBuild()
return route

View file

@ -4,12 +4,16 @@ package restful
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import "fmt"
import (
"fmt"
"net/http"
)
// ServiceError is a transport object to pass information about a non-Http error occurred in a WebService while processing a request.
type ServiceError struct {
Code int
Message string
Header http.Header
}
// NewError returns a ServiceError using the code and reason
@ -17,6 +21,11 @@ func NewError(code int, message string) ServiceError {
return ServiceError{Code: code, Message: message}
}
// NewErrorWithHeader returns a ServiceError using the code, reason and header
func NewErrorWithHeader(code int, message string, header http.Header) ServiceError {
return ServiceError{Code: code, Message: message, Header: header}
}
// Error returns a text representation of the service error
func (s ServiceError) Error() string {
return fmt.Sprintf("[ServiceError:%v] %v", s.Code, s.Message)

View file

@ -188,7 +188,7 @@ func (w *WebService) RemoveRoute(path, method string) error {
continue
}
newRoutes[current] = w.routes[ix]
current = current + 1
current++
}
w.routes = newRoutes
return nil