aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/emicklei
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2025-01-02 11:58:29 +0100
committerAleksandr Nogikh <nogikh@google.com>2025-01-22 13:17:53 +0000
commit7512e6e7738143bd302d9b20cb1fd0d1d7af9643 (patch)
tree67988d580d111bacbd009acfc0057f89aafa6522 /vendor/github.com/emicklei
parent44f2ad31190603135f4ac758273f26111ca6003c (diff)
vendor: fetch the dependencies
Diffstat (limited to 'vendor/github.com/emicklei')
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/.gitignore71
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/.goconvey1
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/.travis.yml13
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/CHANGES.md396
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/LICENSE22
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/Makefile8
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/README.md112
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/SECURITY.md13
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/Srcfile1
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/bench_test.sh10
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/compress.go127
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/compressor_cache.go103
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/compressor_pools.go91
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/compressors.go54
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/constants.go32
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/container.go450
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/cors_filter.go193
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/coverage.sh2
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/curly.go173
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/curly_route.go54
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/custom_verb.go29
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/doc.go185
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/entity_accessors.go162
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/extensions.go21
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/filter.go37
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/filter_adapter.go21
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/json.go11
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/jsoniter.go12
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/jsr311.go326
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/log/log.go34
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/logger.go32
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/mime.go50
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/options_filter.go34
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/parameter.go242
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/path_expression.go74
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/path_processor.go74
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/request.go133
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/response.go259
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/route.go191
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/route_builder.go389
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/route_reader.go66
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/router.go20
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/service_error.go32
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/web_service.go305
-rw-r--r--vendor/github.com/emicklei/go-restful/v3/web_service_container.go39
45 files changed, 4704 insertions, 0 deletions
diff --git a/vendor/github.com/emicklei/go-restful/v3/.gitignore b/vendor/github.com/emicklei/go-restful/v3/.gitignore
new file mode 100644
index 000000000..446be09b4
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/.gitignore
@@ -0,0 +1,71 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+
+restful.html
+
+*.out
+
+tmp.prof
+
+go-restful.test
+
+examples/restful-basic-authentication
+
+examples/restful-encoding-filter
+
+examples/restful-filters
+
+examples/restful-hello-world
+
+examples/restful-resource-functions
+
+examples/restful-serve-static
+
+examples/restful-user-service
+
+*.DS_Store
+examples/restful-user-resource
+
+examples/restful-multi-containers
+
+examples/restful-form-handling
+
+examples/restful-CORS-filter
+
+examples/restful-options-filter
+
+examples/restful-curly-router
+
+examples/restful-cpuprofiler-service
+
+examples/restful-pre-post-filters
+
+curly.prof
+
+examples/restful-NCSA-logging
+
+examples/restful-html-template
+
+s.html
+restful-path-tail
+.idea
diff --git a/vendor/github.com/emicklei/go-restful/v3/.goconvey b/vendor/github.com/emicklei/go-restful/v3/.goconvey
new file mode 100644
index 000000000..8485e986e
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/.goconvey
@@ -0,0 +1 @@
+ignore \ No newline at end of file
diff --git a/vendor/github.com/emicklei/go-restful/v3/.travis.yml b/vendor/github.com/emicklei/go-restful/v3/.travis.yml
new file mode 100644
index 000000000..3a0bf5ff1
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/.travis.yml
@@ -0,0 +1,13 @@
+language: go
+
+go:
+ - 1.x
+
+before_install:
+ - go test -v
+
+script:
+ - go test -race -coverprofile=coverage.txt -covermode=atomic
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash) \ No newline at end of file
diff --git a/vendor/github.com/emicklei/go-restful/v3/CHANGES.md b/vendor/github.com/emicklei/go-restful/v3/CHANGES.md
new file mode 100644
index 000000000..5edd5a7ca
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/CHANGES.md
@@ -0,0 +1,396 @@
+# Change history of go-restful
+
+## [v3.11.0] - 2023-08-19
+
+- restored behavior as <= v3.9.0 with option to change path strategy using TrimRightSlashEnabled.
+
+## [v3.10.2] - 2023-03-09 - DO NOT USE
+
+- introduced MergePathStrategy to be able to revert behaviour of path concatenation to 3.9.0
+ see comment in Readme how to customize this behaviour.
+
+## [v3.10.1] - 2022-11-19 - DO NOT USE
+
+- fix broken 3.10.0 by using path package for joining paths
+
+## [v3.10.0] - 2022-10-11 - BROKEN
+
+- changed tokenizer to match std route match behavior; do not trimright the path (#511)
+- Add MIME_ZIP (#512)
+- Add MIME_ZIP and HEADER_ContentDisposition (#513)
+- Changed how to get query parameter issue #510
+
+## [v3.9.0] - 2022-07-21
+
+- add support for http.Handler implementations to work as FilterFunction, issue #504 (thanks to https://github.com/ggicci)
+
+## [v3.8.0] - 2022-06-06
+
+- use exact matching of allowed domain entries, issue #489 (#493)
+ - this changes fixes [security] Authorization Bypass Through User-Controlled Key
+ by changing the behaviour of the AllowedDomains setting in the CORS filter.
+ To support the previous behaviour, the CORS filter type now has a AllowedDomainFunc
+ callback mechanism which is called when a simple domain match fails.
+- add test and fix for POST without body and Content-type, issue #492 (#496)
+- [Minor] Bad practice to have a mix of Receiver types. (#491)
+
+## [v3.7.2] - 2021-11-24
+
+- restored FilterChain (#482 by SVilgelm)
+
+
+## [v3.7.1] - 2021-10-04
+
+- fix problem with contentEncodingEnabled setting (#479)
+
+## [v3.7.0] - 2021-09-24
+
+- feat(parameter): adds additional openapi mappings (#478)
+
+## [v3.6.0] - 2021-09-18
+
+- add support for vendor extensions (#477 thx erraggy)
+
+## [v3.5.2] - 2021-07-14
+
+- fix removing absent route from webservice (#472)
+
+## [v3.5.1] - 2021-04-12
+
+- fix handling no match access selected path
+- remove obsolete field
+
+## [v3.5.0] - 2021-04-10
+
+- add check for wildcard (#463) in CORS
+- add access to Route from Request, issue #459 (#462)
+
+## [v3.4.0] - 2020-11-10
+
+- Added OPTIONS to WebService
+
+## [v3.3.2] - 2020-01-23
+
+- Fixed duplicate compression in dispatch. #449
+
+
+## [v3.3.1] - 2020-08-31
+
+- Added check on writer to prevent compression of response twice. #447
+
+## [v3.3.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)
+
+## [v3.2.0] - 2020-06-21
+
+- 405 Method Not Allowed must have Allow header (#436) (thx Bracken <abdawson@gmail.com>)
+- add field allowedMethodsWithoutContentType (#424)
+
+## [v3.1.0]
+
+- support describing response headers (#426)
+- fix openapi examples (#425)
+
+v3.0.0
+
+- fix: use request/response resulting from filter chain
+- add Go module
+ Module consumer should use github.com/emicklei/go-restful/v3 as import path
+
+v2.10.0
+
+- support for Custom Verbs (thanks Vinci Xu <277040271@qq.com>)
+- fixed static example (thanks Arthur <yang_yapo@126.com>)
+- simplify code (thanks Christian Muehlhaeuser <muesli@gmail.com>)
+- added JWT HMAC with SHA-512 authentication code example (thanks Amim Knabben <amim.knabben@gmail.com>)
+
+v2.9.6
+
+- small optimization in filter code
+
+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
+
+- fix issue #400 , parsing mime type quality
+- Route Builder added option for contentEncodingEnabled (#398)
+
+v2.9.3
+
+- Avoid return of 415 Unsupported Media Type when request body is empty (#396)
+
+v2.9.2
+
+- Reduce allocations in per-request methods to improve performance (#395)
+
+v2.9.1
+
+- Fix issue with default responses and invalid status code 0. (#393)
+
+v2.9.0
+
+- add per Route content encoding setting (overrides container setting)
+
+v2.8.0
+
+- add Request.QueryParameters()
+- add json-iterator (via build tag)
+- disable vgo module (until log is moved)
+
+v2.7.1
+
+- add vgo module
+
+v2.6.1
+
+- add JSONNewDecoderFunc to allow custom JSON Decoder usage (go 1.10+)
+
+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
+
+- added route condition functions using `.If(func)` in route building.
+
+2017-02-16
+
+- solved issue #304, make operation names unique
+
+2017-01-30
+
+ [IMPORTANT] For swagger users, change your import statement to:
+ swagger "github.com/emicklei/go-restful-swagger12"
+
+- moved swagger 1.2 code to go-restful-swagger12
+- created TAG 2.0.0
+
+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
+
+- (API change) Remove code related to caching request content. Removes SetCacheReadEntity(doCache bool)
+
+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
+
+- fix the DefaultRequestContentType feature
+
+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
+
+- rename new WriteStatusAnd... to WriteHeaderAnd... for consistency
+
+2015-09-25
+
+- fixed problem with changing Header after WriteHeader (issue 235)
+
+2015-09-14
+
+- changed behavior of WriteHeader (immediate write) and WriteEntity (no status write)
+- added support for custom EntityReaderWriters.
+
+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
+
+- add configurable logging
+
+2015-03-18
+
+- if not specified, the Operation is derived from the Route function
+
+2015-03-17
+
+- expose Parameter creation functions
+- make trace logger an interface
+- fix OPTIONSFilter
+- customize rendering of ServiceError
+- JSR311 router now handles wildcards
+- add Notes to Route
+
+2014-11-27
+
+- (api add) PrettyPrint per response. (as proposed in #167)
+
+2014-11-12
+
+- (api add) ApiVersion(.) for documentation in Swagger UI
+
+2014-11-10
+
+- (api change) struct fields tagged with "description" show up in Swagger UI
+
+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
+
+- (api add) ReturnsError allows you to document Http codes in swagger
+- fixed problem with greedy CurlyRouter
+- (api add) Access-Control-Max-Age in CORS
+- add tracing functionality (injectable) for debugging purposes
+- support JSON parse 64bit int
+- fix empty parameters for swagger
+- WebServicesUrl is now optional for swagger
+- fixed duplicate AccessControlAllowOrigin in CORS
+- (api change) expose ServeMux in container
+- (api add) added AllowedDomains in CORS
+- (api add) ParameterNamed for detailed documentation
+
+2014-04-16
+
+- (api add) expose constructor of Request for testing.
+
+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
+
+- (api add) CORS can be configured with a list of allowed domains
+
+2014-03-12
+
+- (api add) Route path parameters can use wildcard or regular expressions. (requires CurlyRouter)
+
+2014-02-26
+
+- (api add) Request now provides information about the matched Route, see method SelectedRoutePath
+
+2014-02-17
+
+- (api change) renamed parameter constants (go-lint checks)
+
+2014-01-10
+
+- (api add) support for CloseNotify, see http://golang.org/pkg/net/http/#CloseNotifier
+
+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
+
+- (api add) Response knows how many bytes are written to the response body.
+
+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
+
+- (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
+
+- (api change) Router interface simplified
+- Implemented CurlyRouter, a Router that does not use|allow regular expressions in paths
+
+2013-08-05
+ - add OPTIONS support
+ - add CORS support
+
+2013-08-27
+
+- fixed some reported issues (see github)
+- (api change) deprecated use of WriteError; use WriteErrorString instead
+
+2014-04-15
+
+- (fix) v1.0.1 tag: fix Issue 111: WriteErrorString
+
+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.
+- if panic is detected then a small stack trace is printed (thanks to runner-mei)
+- (api add) WriteErrorString to Response
+
+Important API changes:
+
+- (api remove) package variable DoNotRecover no longer works ; use restful.DefaultContainer.DoNotRecover(true) instead.
+- (api remove) package variable EnableContentEncoding no longer works ; use restful.DefaultContainer.EnableContentEncoding(true) instead.
+
+
+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
+
+- (improve) DoNotRecover option, moved request body closer, improved ReadEntity
+
+2013-06-03
+
+- (api change) removed Dispatcher interface, hide PathExpression
+- changed receiver names of type functions to be more idiomatic Go
+
+2013-06-02
+
+- (optimize) Cache the RegExp compilation of Paths.
+
+2013-05-22
+
+- (api add) Added support for request/response filter functions
+
+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>
+
+- See https://github.com/emicklei/go-restful/commits
+
+2012-11-14
+
+- Initial commit
+
+
diff --git a/vendor/github.com/emicklei/go-restful/v3/LICENSE b/vendor/github.com/emicklei/go-restful/v3/LICENSE
new file mode 100644
index 000000000..ece7ec61e
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2012,2013 Ernest Micklei
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/vendor/github.com/emicklei/go-restful/v3/Makefile b/vendor/github.com/emicklei/go-restful/v3/Makefile
new file mode 100644
index 000000000..16d0b80bb
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/Makefile
@@ -0,0 +1,8 @@
+all: test
+
+test:
+ go vet .
+ go test -cover -v .
+
+ex:
+ find ./examples -type f -name "*.go" | xargs -I {} go build -o /tmp/ignore {} \ No newline at end of file
diff --git a/vendor/github.com/emicklei/go-restful/v3/README.md b/vendor/github.com/emicklei/go-restful/v3/README.md
new file mode 100644
index 000000000..e3e30080e
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/README.md
@@ -0,0 +1,112 @@
+go-restful
+==========
+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://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 use v3](https://github.com/emicklei/go-restful/tree/v3/examples)
+
+REST asks developers to use HTTP methods explicitly and in a way that's consistent with the protocol definition. This basic REST design principle establishes a one-to-one mapping between create, read, update, and delete (CRUD) operations and HTTP methods. According to this mapping:
+
+- GET = Retrieve a representation of a resource
+- POST = Create if you are sending content to the server to create a subordinate of the specified resource collection, using some server-side algorithm.
+- PUT = Create if you are sending the full content of the specified resource (URI).
+- PUT = Update if you are updating the full content of the specified resource.
+- DELETE = Delete if you are requesting the server to delete the resource
+- 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
+ws := new(restful.WebService)
+ws.
+ Path("/users").
+ Consumes(restful.MIME_XML, restful.MIME_JSON).
+ Produces(restful.MIME_JSON, restful.MIME_XML)
+
+ws.Route(ws.GET("/{user-id}").To(u.findUser).
+ Doc("get a user").
+ Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
+ Writes(User{}))
+...
+
+func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
+ id := request.PathParameter("user-id")
+ ...
+}
+```
+
+[Full API of a UserResource](https://github.com/emicklei/go-restful/blob/v3/examples/user-resource/restful-user-resource.go)
+
+### Features
+
+- 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, [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 accessing parameters (path,query,header)
+- Response API for writing structs to JSON/XML and setting headers
+- Customizable encoding using EntityReaderWriter registration
+- Filters for intercepting the request &#8594; response flow on Service or Route level
+- Request-scoped variables using attributes
+- Containers for WebServices on different HTTP endpoints
+- Content encoding (gzip,deflate) of request and response payloads
+- Automatic responses on OPTIONS (using a filter)
+- Automatic CORS request handling (using a filter)
+- API declaration for Swagger UI ([go-restful-openapi](https://github.com/emicklei/go-restful-openapi))
+- Panic recovery to produce HTTP 500, customizable using RecoverHandler(...)
+- Route errors produce HTTP 404/405/406/415 errors, customizable using ServiceErrorHandler(...)
+- Configurable (trace) logging
+- Customizable gzip/deflate readers and writers using CompressorProvider registration
+- Inject your own http.Handler using the `HttpMiddlewareHandlerToFilter` function
+
+## How to customize
+There are several hooks to customize the behavior of the go-restful package.
+
+- Router algorithm
+- Panic recovery
+- JSON decoder
+- Trace logging
+- Compression
+- Encoders for other serializers
+- Use [jsoniter](https://github.com/json-iterator/go) by building this package using a build tag, e.g. `go build -tags=jsoniter .`
+- Use the package variable `TrimRightSlashEnabled` (default true) to control the behavior of matching routes that end with a slash `/`
+
+## Resources
+
+- [Example programs](./examples)
+- [Example posted on blog](http://ernestmicklei.com/2012/11/go-restful-first-working-example/)
+- [Design explained on blog](http://ernestmicklei.com/2012/11/go-restful-api-design/)
+- [sourcegraph](https://sourcegraph.com/github.com/emicklei/go-restful)
+- [showcase: Zazkia - tcp proxy for testing resiliency](https://github.com/emicklei/zazkia)
+- [showcase: Mora - MongoDB REST Api server](https://github.com/emicklei/mora)
+
+Type ```git shortlog -s``` for a full list of contributors.
+
+© 2012 - 2023, http://ernestmicklei.com. MIT License. Contributions are welcome.
diff --git a/vendor/github.com/emicklei/go-restful/v3/SECURITY.md b/vendor/github.com/emicklei/go-restful/v3/SECURITY.md
new file mode 100644
index 000000000..810d3b510
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/SECURITY.md
@@ -0,0 +1,13 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported |
+| ------- | ------------------ |
+| v3.7.x | :white_check_mark: |
+| < v3.0.1 | :x: |
+
+## Reporting a Vulnerability
+
+Create an Issue and put the label `[security]` in the title of the issue.
+Valid reported security issues are expected to be solved within a week.
diff --git a/vendor/github.com/emicklei/go-restful/v3/Srcfile b/vendor/github.com/emicklei/go-restful/v3/Srcfile
new file mode 100644
index 000000000..16fd18689
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/Srcfile
@@ -0,0 +1 @@
+{"SkipDirs": ["examples"]}
diff --git a/vendor/github.com/emicklei/go-restful/v3/bench_test.sh b/vendor/github.com/emicklei/go-restful/v3/bench_test.sh
new file mode 100644
index 000000000..47ffbe4ac
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/bench_test.sh
@@ -0,0 +1,10 @@
+#go test -run=none -file bench_test.go -test.bench . -cpuprofile=bench_test.out
+
+go test -c
+./go-restful.test -test.run=none -test.cpuprofile=tmp.prof -test.bench=BenchmarkMany
+./go-restful.test -test.run=none -test.cpuprofile=curly.prof -test.bench=BenchmarkManyCurly
+
+#go tool pprof go-restful.test tmp.prof
+go tool pprof go-restful.test curly.prof
+
+
diff --git a/vendor/github.com/emicklei/go-restful/v3/compress.go b/vendor/github.com/emicklei/go-restful/v3/compress.go
new file mode 100644
index 000000000..1ff239f99
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/compress.go
@@ -0,0 +1,127 @@
+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 (
+ "bufio"
+ "compress/gzip"
+ "compress/zlib"
+ "errors"
+ "io"
+ "net"
+ "net/http"
+ "strings"
+)
+
+// OBSOLETE : use restful.DefaultContainer.EnableContentEncoding(true) to change this setting.
+var EnableContentEncoding = false
+
+// CompressingResponseWriter is a http.ResponseWriter that can perform content encoding (gzip and zlib)
+type CompressingResponseWriter struct {
+ writer http.ResponseWriter
+ compressor io.WriteCloser
+ encoding string
+}
+
+// Header is part of http.ResponseWriter interface
+func (c *CompressingResponseWriter) Header() http.Header {
+ return c.writer.Header()
+}
+
+// WriteHeader is part of http.ResponseWriter interface
+func (c *CompressingResponseWriter) WriteHeader(status int) {
+ c.writer.WriteHeader(status)
+}
+
+// Write is part of http.ResponseWriter interface
+// It is passed through the compressor
+func (c *CompressingResponseWriter) Write(bytes []byte) (int, error) {
+ if c.isCompressorClosed() {
+ return -1, errors.New("Compressing error: tried to write data using closed compressor")
+ }
+ return c.compressor.Write(bytes)
+}
+
+// CloseNotify is part of http.CloseNotifier interface
+func (c *CompressingResponseWriter) CloseNotify() <-chan bool {
+ return c.writer.(http.CloseNotifier).CloseNotify()
+}
+
+// Close the underlying compressor
+func (c *CompressingResponseWriter) Close() error {
+ if c.isCompressorClosed() {
+ return errors.New("Compressing error: tried to close already closed compressor")
+ }
+
+ c.compressor.Close()
+ if ENCODING_GZIP == c.encoding {
+ currentCompressorProvider.ReleaseGzipWriter(c.compressor.(*gzip.Writer))
+ }
+ if ENCODING_DEFLATE == c.encoding {
+ currentCompressorProvider.ReleaseZlibWriter(c.compressor.(*zlib.Writer))
+ }
+ // gc hint needed?
+ c.compressor = nil
+ return nil
+}
+
+func (c *CompressingResponseWriter) isCompressorClosed() bool {
+ return nil == c.compressor
+}
+
+// Hijack implements the Hijacker interface
+// This is especially useful when combining Container.EnabledContentEncoding
+// in combination with websockets (for instance gorilla/websocket)
+func (c *CompressingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ hijacker, ok := c.writer.(http.Hijacker)
+ if !ok {
+ return nil, nil, errors.New("ResponseWriter doesn't support Hijacker interface")
+ }
+ return hijacker.Hijack()
+}
+
+// WantsCompressedResponse reads the Accept-Encoding header to see if and which encoding is requested.
+// It also inspects the httpWriter whether its content-encoding is already set (non-empty).
+func wantsCompressedResponse(httpRequest *http.Request, httpWriter http.ResponseWriter) (bool, string) {
+ if contentEncoding := httpWriter.Header().Get(HEADER_ContentEncoding); contentEncoding != "" {
+ return false, ""
+ }
+ header := httpRequest.Header.Get(HEADER_AcceptEncoding)
+ gi := strings.Index(header, ENCODING_GZIP)
+ zi := strings.Index(header, ENCODING_DEFLATE)
+ // use in order of appearance
+ if gi == -1 {
+ return zi != -1, ENCODING_DEFLATE
+ } else if zi == -1 {
+ return gi != -1, ENCODING_GZIP
+ } else {
+ if gi < zi {
+ return true, ENCODING_GZIP
+ }
+ return true, ENCODING_DEFLATE
+ }
+}
+
+// NewCompressingResponseWriter create a CompressingResponseWriter for a known encoding = {gzip,deflate}
+func NewCompressingResponseWriter(httpWriter http.ResponseWriter, encoding string) (*CompressingResponseWriter, error) {
+ httpWriter.Header().Set(HEADER_ContentEncoding, encoding)
+ c := new(CompressingResponseWriter)
+ c.writer = httpWriter
+ var err error
+ if ENCODING_GZIP == encoding {
+ w := currentCompressorProvider.AcquireGzipWriter()
+ w.Reset(httpWriter)
+ c.compressor = w
+ c.encoding = ENCODING_GZIP
+ } else if ENCODING_DEFLATE == encoding {
+ w := currentCompressorProvider.AcquireZlibWriter()
+ w.Reset(httpWriter)
+ c.compressor = w
+ c.encoding = ENCODING_DEFLATE
+ } else {
+ return nil, errors.New("Unknown encoding:" + encoding)
+ }
+ return c, err
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/compressor_cache.go b/vendor/github.com/emicklei/go-restful/v3/compressor_cache.go
new file mode 100644
index 000000000..ee426010a
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/compressor_cache.go
@@ -0,0 +1,103 @@
+package restful
+
+// Copyright 2015 Ernest Micklei. All rights reserved.
+// Use of this source code is governed by a license
+// that can be found in the LICENSE file.
+
+import (
+ "compress/gzip"
+ "compress/zlib"
+)
+
+// BoundedCachedCompressors is a CompressorProvider that uses a cache with a fixed amount
+// of writers and readers (resources).
+// If a new resource is acquired and all are in use, it will return a new unmanaged resource.
+type BoundedCachedCompressors struct {
+ gzipWriters chan *gzip.Writer
+ gzipReaders chan *gzip.Reader
+ zlibWriters chan *zlib.Writer
+ writersCapacity int
+ readersCapacity int
+}
+
+// NewBoundedCachedCompressors returns a new, with filled cache, BoundedCachedCompressors.
+func NewBoundedCachedCompressors(writersCapacity, readersCapacity int) *BoundedCachedCompressors {
+ b := &BoundedCachedCompressors{
+ gzipWriters: make(chan *gzip.Writer, writersCapacity),
+ gzipReaders: make(chan *gzip.Reader, readersCapacity),
+ zlibWriters: make(chan *zlib.Writer, writersCapacity),
+ writersCapacity: writersCapacity,
+ readersCapacity: readersCapacity,
+ }
+ for ix := 0; ix < writersCapacity; ix++ {
+ b.gzipWriters <- newGzipWriter()
+ b.zlibWriters <- newZlibWriter()
+ }
+ for ix := 0; ix < readersCapacity; ix++ {
+ b.gzipReaders <- newGzipReader()
+ }
+ return b
+}
+
+// AcquireGzipWriter returns an resettable *gzip.Writer. Needs to be released.
+func (b *BoundedCachedCompressors) AcquireGzipWriter() *gzip.Writer {
+ var writer *gzip.Writer
+ select {
+ case writer, _ = <-b.gzipWriters:
+ default:
+ // return a new unmanaged one
+ writer = newGzipWriter()
+ }
+ return writer
+}
+
+// ReleaseGzipWriter accepts a writer (does not have to be one that was cached)
+// only when the cache has room for it. It will ignore it otherwise.
+func (b *BoundedCachedCompressors) ReleaseGzipWriter(w *gzip.Writer) {
+ // forget the unmanaged ones
+ if len(b.gzipWriters) < b.writersCapacity {
+ b.gzipWriters <- w
+ }
+}
+
+// AcquireGzipReader returns a *gzip.Reader. Needs to be released.
+func (b *BoundedCachedCompressors) AcquireGzipReader() *gzip.Reader {
+ var reader *gzip.Reader
+ select {
+ case reader, _ = <-b.gzipReaders:
+ default:
+ // return a new unmanaged one
+ reader = newGzipReader()
+ }
+ return reader
+}
+
+// ReleaseGzipReader accepts a reader (does not have to be one that was cached)
+// only when the cache has room for it. It will ignore it otherwise.
+func (b *BoundedCachedCompressors) ReleaseGzipReader(r *gzip.Reader) {
+ // forget the unmanaged ones
+ if len(b.gzipReaders) < b.readersCapacity {
+ b.gzipReaders <- r
+ }
+}
+
+// AcquireZlibWriter returns an resettable *zlib.Writer. Needs to be released.
+func (b *BoundedCachedCompressors) AcquireZlibWriter() *zlib.Writer {
+ var writer *zlib.Writer
+ select {
+ case writer, _ = <-b.zlibWriters:
+ default:
+ // return a new unmanaged one
+ writer = newZlibWriter()
+ }
+ return writer
+}
+
+// ReleaseZlibWriter accepts a writer (does not have to be one that was cached)
+// only when the cache has room for it. It will ignore it otherwise.
+func (b *BoundedCachedCompressors) ReleaseZlibWriter(w *zlib.Writer) {
+ // forget the unmanaged ones
+ if len(b.zlibWriters) < b.writersCapacity {
+ b.zlibWriters <- w
+ }
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/compressor_pools.go b/vendor/github.com/emicklei/go-restful/v3/compressor_pools.go
new file mode 100644
index 000000000..d866ce64b
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/compressor_pools.go
@@ -0,0 +1,91 @@
+package restful
+
+// Copyright 2015 Ernest Micklei. All rights reserved.
+// Use of this source code is governed by a license
+// that can be found in the LICENSE file.
+
+import (
+ "bytes"
+ "compress/gzip"
+ "compress/zlib"
+ "sync"
+)
+
+// SyncPoolCompessors is a CompressorProvider that use the standard sync.Pool.
+type SyncPoolCompessors struct {
+ GzipWriterPool *sync.Pool
+ GzipReaderPool *sync.Pool
+ ZlibWriterPool *sync.Pool
+}
+
+// NewSyncPoolCompessors returns a new ("empty") SyncPoolCompessors.
+func NewSyncPoolCompessors() *SyncPoolCompessors {
+ return &SyncPoolCompessors{
+ GzipWriterPool: &sync.Pool{
+ New: func() interface{} { return newGzipWriter() },
+ },
+ GzipReaderPool: &sync.Pool{
+ New: func() interface{} { return newGzipReader() },
+ },
+ ZlibWriterPool: &sync.Pool{
+ New: func() interface{} { return newZlibWriter() },
+ },
+ }
+}
+
+func (s *SyncPoolCompessors) AcquireGzipWriter() *gzip.Writer {
+ return s.GzipWriterPool.Get().(*gzip.Writer)
+}
+
+func (s *SyncPoolCompessors) ReleaseGzipWriter(w *gzip.Writer) {
+ s.GzipWriterPool.Put(w)
+}
+
+func (s *SyncPoolCompessors) AcquireGzipReader() *gzip.Reader {
+ return s.GzipReaderPool.Get().(*gzip.Reader)
+}
+
+func (s *SyncPoolCompessors) ReleaseGzipReader(r *gzip.Reader) {
+ s.GzipReaderPool.Put(r)
+}
+
+func (s *SyncPoolCompessors) AcquireZlibWriter() *zlib.Writer {
+ return s.ZlibWriterPool.Get().(*zlib.Writer)
+}
+
+func (s *SyncPoolCompessors) ReleaseZlibWriter(w *zlib.Writer) {
+ s.ZlibWriterPool.Put(w)
+}
+
+func newGzipWriter() *gzip.Writer {
+ // create with an empty bytes writer; it will be replaced before using the gzipWriter
+ writer, err := gzip.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed)
+ if err != nil {
+ panic(err.Error())
+ }
+ return writer
+}
+
+func newGzipReader() *gzip.Reader {
+ // create with an empty reader (but with GZIP header); it will be replaced before using the gzipReader
+ // we can safely use currentCompressProvider because it is set on package initialization.
+ w := currentCompressorProvider.AcquireGzipWriter()
+ defer currentCompressorProvider.ReleaseGzipWriter(w)
+ b := new(bytes.Buffer)
+ w.Reset(b)
+ w.Flush()
+ w.Close()
+ reader, err := gzip.NewReader(bytes.NewReader(b.Bytes()))
+ if err != nil {
+ panic(err.Error())
+ }
+ return reader
+}
+
+func newZlibWriter() *zlib.Writer {
+ writer, err := zlib.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed)
+ if err != nil {
+ panic(err.Error())
+ }
+ return writer
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/compressors.go b/vendor/github.com/emicklei/go-restful/v3/compressors.go
new file mode 100644
index 000000000..9db4a8c8e
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/compressors.go
@@ -0,0 +1,54 @@
+package restful
+
+// Copyright 2015 Ernest Micklei. All rights reserved.
+// Use of this source code is governed by a license
+// that can be found in the LICENSE file.
+
+import (
+ "compress/gzip"
+ "compress/zlib"
+)
+
+// CompressorProvider describes a component that can provider compressors for the std methods.
+type CompressorProvider interface {
+ // Returns a *gzip.Writer which needs to be released later.
+ // Before using it, call Reset().
+ AcquireGzipWriter() *gzip.Writer
+
+ // Releases an acquired *gzip.Writer.
+ ReleaseGzipWriter(w *gzip.Writer)
+
+ // Returns a *gzip.Reader which needs to be released later.
+ AcquireGzipReader() *gzip.Reader
+
+ // Releases an acquired *gzip.Reader.
+ ReleaseGzipReader(w *gzip.Reader)
+
+ // Returns a *zlib.Writer which needs to be released later.
+ // Before using it, call Reset().
+ AcquireZlibWriter() *zlib.Writer
+
+ // Releases an acquired *zlib.Writer.
+ ReleaseZlibWriter(w *zlib.Writer)
+}
+
+// DefaultCompressorProvider is the actual provider of compressors (zlib or gzip).
+var currentCompressorProvider CompressorProvider
+
+func init() {
+ currentCompressorProvider = NewSyncPoolCompessors()
+}
+
+// CurrentCompressorProvider returns the current CompressorProvider.
+// It is initialized using a SyncPoolCompessors.
+func CurrentCompressorProvider() CompressorProvider {
+ return currentCompressorProvider
+}
+
+// SetCompressorProvider sets the actual provider of compressors (zlib or gzip).
+func SetCompressorProvider(p CompressorProvider) {
+ if p == nil {
+ panic("cannot set compressor provider to nil")
+ }
+ currentCompressorProvider = p
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/constants.go b/vendor/github.com/emicklei/go-restful/v3/constants.go
new file mode 100644
index 000000000..2328bde6c
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/constants.go
@@ -0,0 +1,32 @@
+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.
+
+const (
+ MIME_XML = "application/xml" // Accept or Content-Type used in Consumes() and/or Produces()
+ MIME_JSON = "application/json" // Accept or Content-Type used in Consumes() and/or Produces()
+ MIME_ZIP = "application/zip" // Accept or Content-Type used in Consumes() and/or Produces()
+ MIME_OCTET = "application/octet-stream" // If Content-Type is not present in request, use the default
+
+ HEADER_Allow = "Allow"
+ HEADER_Accept = "Accept"
+ HEADER_Origin = "Origin"
+ HEADER_ContentType = "Content-Type"
+ HEADER_ContentDisposition = "Content-Disposition"
+ HEADER_LastModified = "Last-Modified"
+ HEADER_AcceptEncoding = "Accept-Encoding"
+ HEADER_ContentEncoding = "Content-Encoding"
+ HEADER_AccessControlExposeHeaders = "Access-Control-Expose-Headers"
+ HEADER_AccessControlRequestMethod = "Access-Control-Request-Method"
+ HEADER_AccessControlRequestHeaders = "Access-Control-Request-Headers"
+ HEADER_AccessControlAllowMethods = "Access-Control-Allow-Methods"
+ HEADER_AccessControlAllowOrigin = "Access-Control-Allow-Origin"
+ HEADER_AccessControlAllowCredentials = "Access-Control-Allow-Credentials"
+ HEADER_AccessControlAllowHeaders = "Access-Control-Allow-Headers"
+ HEADER_AccessControlMaxAge = "Access-Control-Max-Age"
+
+ ENCODING_GZIP = "gzip"
+ ENCODING_DEFLATE = "deflate"
+)
diff --git a/vendor/github.com/emicklei/go-restful/v3/container.go b/vendor/github.com/emicklei/go-restful/v3/container.go
new file mode 100644
index 000000000..dd56246dd
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/container.go
@@ -0,0 +1,450 @@
+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 (
+ "bytes"
+ "errors"
+ "fmt"
+ "net/http"
+ "os"
+ "runtime"
+ "strings"
+ "sync"
+
+ "github.com/emicklei/go-restful/v3/log"
+)
+
+// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
+// The requests are further dispatched to routes of WebServices using a RouteSelector
+type Container struct {
+ webServicesLock sync.RWMutex
+ webServices []*WebService
+ ServeMux *http.ServeMux
+ isRegisteredOnRoot bool
+ containerFilters []FilterFunction
+ doNotRecover bool // default is true
+ recoverHandleFunc RecoverHandleFunction
+ serviceErrorHandleFunc ServiceErrorHandleFunction
+ router RouteSelector // default is a CurlyRouter (RouterJSR311 is a slower alternative)
+ contentEncodingEnabled bool // default is false
+}
+
+// NewContainer creates a new Container using a new ServeMux and default router (CurlyRouter)
+func NewContainer() *Container {
+ return &Container{
+ webServices: []*WebService{},
+ ServeMux: http.NewServeMux(),
+ isRegisteredOnRoot: false,
+ containerFilters: []FilterFunction{},
+ doNotRecover: true,
+ recoverHandleFunc: logStackOnRecover,
+ serviceErrorHandleFunc: writeServiceError,
+ router: CurlyRouter{},
+ contentEncodingEnabled: false}
+}
+
+// RecoverHandleFunction declares functions that can be used to handle a panic situation.
+// The first argument is what recover() returns. The second must be used to communicate an error response.
+type RecoverHandleFunction func(interface{}, http.ResponseWriter)
+
+// RecoverHandler changes the default function (logStackOnRecover) to be called
+// when a panic is detected. DoNotRecover must be have its default value (=false).
+func (c *Container) RecoverHandler(handler RecoverHandleFunction) {
+ c.recoverHandleFunc = handler
+}
+
+// ServiceErrorHandleFunction declares functions that can be used to handle a service error situation.
+// The first argument is the service error, the second is the request that resulted in the error and
+// the third must be used to communicate an error response.
+type ServiceErrorHandleFunction func(ServiceError, *Request, *Response)
+
+// ServiceErrorHandler changes the default function (writeServiceError) to be called
+// when a ServiceError is detected.
+func (c *Container) ServiceErrorHandler(handler ServiceErrorHandleFunction) {
+ c.serviceErrorHandleFunc = handler
+}
+
+// DoNotRecover controls whether panics will be caught to return HTTP 500.
+// If set to true, Route functions are responsible for handling any error situation.
+// Default value is true.
+func (c *Container) DoNotRecover(doNot bool) {
+ c.doNotRecover = doNot
+}
+
+// Router changes the default Router (currently CurlyRouter)
+func (c *Container) Router(aRouter RouteSelector) {
+ c.router = aRouter
+}
+
+// EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses.
+func (c *Container) EnableContentEncoding(enabled bool) {
+ c.contentEncodingEnabled = enabled
+}
+
+// Add a WebService to the Container. It will detect duplicate root paths and exit in that case.
+func (c *Container) Add(service *WebService) *Container {
+ c.webServicesLock.Lock()
+ defer c.webServicesLock.Unlock()
+
+ // if rootPath was not set then lazy initialize it
+ if len(service.rootPath) == 0 {
+ service.Path("/")
+ }
+
+ // cannot have duplicate root paths
+ for _, each := range c.webServices {
+ if each.RootPath() == service.RootPath() {
+ log.Printf("WebService with duplicate root path detected:['%v']", each)
+ os.Exit(1)
+ }
+ }
+
+ // If not registered on root then add specific mapping
+ if !c.isRegisteredOnRoot {
+ c.isRegisteredOnRoot = c.addHandler(service, c.ServeMux)
+ }
+ c.webServices = append(c.webServices, service)
+ return c
+}
+
+// addHandler may set a new HandleFunc for the serveMux
+// this function must run inside the critical region protected by the webServicesLock.
+// returns true if the function was registered on root ("/")
+func (c *Container) addHandler(service *WebService, serveMux *http.ServeMux) bool {
+ pattern := fixedPrefixPath(service.RootPath())
+ // check if root path registration is needed
+ if "/" == pattern || "" == pattern {
+ serveMux.HandleFunc("/", c.dispatch)
+ return true
+ }
+ // detect if registration already exists
+ alreadyMapped := false
+ for _, each := range c.webServices {
+ if each.RootPath() == service.RootPath() {
+ alreadyMapped = true
+ break
+ }
+ }
+ if !alreadyMapped {
+ serveMux.HandleFunc(pattern, c.dispatch)
+ if !strings.HasSuffix(pattern, "/") {
+ serveMux.HandleFunc(pattern+"/", c.dispatch)
+ }
+ }
+ return false
+}
+
+func (c *Container) Remove(ws *WebService) error {
+ if c.ServeMux == http.DefaultServeMux {
+ errMsg := fmt.Sprintf("cannot remove a WebService from a Container using the DefaultServeMux: ['%v']", ws)
+ log.Print(errMsg)
+ return errors.New(errMsg)
+ }
+ c.webServicesLock.Lock()
+ defer c.webServicesLock.Unlock()
+ // build a new ServeMux and re-register all WebServices
+ newServeMux := http.NewServeMux()
+ newServices := []*WebService{}
+ newIsRegisteredOnRoot := false
+ for _, each := range c.webServices {
+ if each.rootPath != ws.rootPath {
+ // If not registered on root then add specific mapping
+ if !newIsRegisteredOnRoot {
+ newIsRegisteredOnRoot = c.addHandler(each, newServeMux)
+ }
+ newServices = append(newServices, each)
+ }
+ }
+ c.webServices, c.ServeMux, c.isRegisteredOnRoot = newServices, newServeMux, newIsRegisteredOnRoot
+ return nil
+}
+
+// logStackOnRecover is the default RecoverHandleFunction and is called
+// when DoNotRecover is false and the recoverHandleFunc is not set for the container.
+// Default implementation logs the stacktrace and writes the stacktrace on the response.
+// This may be a security issue as it exposes sourcecode information.
+func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) {
+ var buffer bytes.Buffer
+ buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason))
+ for i := 2; ; i += 1 {
+ _, file, line, ok := runtime.Caller(i)
+ if !ok {
+ break
+ }
+ buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line))
+ }
+ log.Print(buffer.String())
+ httpWriter.WriteHeader(http.StatusInternalServerError)
+ httpWriter.Write(buffer.Bytes())
+}
+
+// writeServiceError is the default ServiceErrorHandleFunction and is called
+// 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)
+}
+
+// Dispatch the incoming Http Request to a matching WebService.
+func (c *Container) Dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {
+ if httpWriter == nil {
+ panic("httpWriter cannot be nil")
+ }
+ if httpRequest == nil {
+ panic("httpRequest cannot be nil")
+ }
+ c.dispatch(httpWriter, httpRequest)
+}
+
+// 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
+ defer func() {
+ if compressWriter, ok := writer.(*CompressingResponseWriter); ok {
+ compressWriter.Close()
+ }
+ }()
+
+ // Instal panic recovery unless told otherwise
+ if !c.doNotRecover { // catch all for 500 response
+ defer func() {
+ if r := recover(); r != nil {
+ c.recoverHandleFunc(r, writer)
+ return
+ }
+ }()
+ }
+
+ // Find best match Route ; err is non nil if no match was found
+ var webService *WebService
+ var route *Route
+ var err error
+ func() {
+ c.webServicesLock.RLock()
+ defer c.webServicesLock.RUnlock()
+ webService, route, err = c.router.SelectRoute(
+ c.webServices,
+ httpRequest)
+ }()
+ if err != nil {
+ // 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) {
+ case ServiceError:
+ ser := err.(ServiceError)
+ c.serviceErrorHandleFunc(ser, req, resp)
+ }
+ // TODO
+ }}
+ 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, httpWriter)
+ 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{}
+ }
+ pathParams := pathProcessor.ExtractParameters(route, webService, httpRequest.URL.Path)
+ wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest, pathParams)
+ // pass through filters (if any)
+ if size := len(c.containerFilters) + len(webService.filters) + len(route.Filters); size > 0 {
+ // compose filter chain
+ 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: route.Function,
+ ParameterDocs: route.ParameterDocs,
+ Operation: route.Operation,
+ }
+ chain.ProcessFilter(wrappedRequest, wrappedResponse)
+ } else {
+ // no filters, handle request by route
+ route.Function(wrappedRequest, wrappedResponse)
+ }
+}
+
+// fixedPrefixPath returns the fixed part of the partspec ; it may include template vars {}
+func fixedPrefixPath(pathspec string) string {
+ varBegin := strings.Index(pathspec, "{")
+ if -1 == varBegin {
+ return pathspec
+ }
+ return pathspec[:varBegin]
+}
+
+// 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) {
+ // 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, httpWriter)
+ 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, 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, httpWriter)
+ 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.
+// Container's filter chain is applied for handler.
+// If a handler already exists for pattern, HandleWithFilter panics.
+func (c *Container) HandleWithFilter(pattern string, handler http.Handler) {
+ f := func(httpResponse http.ResponseWriter, httpRequest *http.Request) {
+ if len(c.containerFilters) == 0 {
+ handler.ServeHTTP(httpResponse, httpRequest)
+ return
+ }
+
+ chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
+ handler.ServeHTTP(resp, req.Request)
+ }}
+ chain.ProcessFilter(NewRequest(httpRequest), NewResponse(httpResponse))
+ }
+
+ c.Handle(pattern, http.HandlerFunc(f))
+}
+
+// Filter appends a container FilterFunction. These are called before dispatching
+// a http.Request to a WebService from the container
+func (c *Container) Filter(filter FilterFunction) {
+ c.containerFilters = append(c.containerFilters, filter)
+}
+
+// RegisteredWebServices returns the collections of added WebServices
+func (c *Container) RegisteredWebServices() []*WebService {
+ c.webServicesLock.RLock()
+ defer c.webServicesLock.RUnlock()
+ result := make([]*WebService, len(c.webServices))
+ for ix := range c.webServices {
+ result[ix] = c.webServices[ix]
+ }
+ return result
+}
+
+// computeAllowedMethods returns a list of HTTP methods that are valid for a Request
+func (c *Container) computeAllowedMethods(req *Request) []string {
+ // Go through all RegisteredWebServices() and all its Routes to collect the options
+ methods := []string{}
+ requestPath := req.Request.URL.Path
+ for _, ws := range c.RegisteredWebServices() {
+ matches := ws.pathExpr.Matcher.FindStringSubmatch(requestPath)
+ if matches != nil {
+ finalMatch := matches[len(matches)-1]
+ for _, rt := range ws.Routes() {
+ matches := rt.pathExpr.Matcher.FindStringSubmatch(finalMatch)
+ if matches != nil {
+ lastMatch := matches[len(matches)-1]
+ if lastMatch == "" || lastMatch == "/" { // do not include if value is neither empty nor ‘/’.
+ methods = append(methods, rt.Method)
+ }
+ }
+ }
+ }
+ }
+ // methods = append(methods, "OPTIONS") not sure about this
+ return methods
+}
+
+// newBasicRequestResponse creates a pair of Request,Response from its http versions.
+// It is basic because no parameter or (produces) content-type information is given.
+func newBasicRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) {
+ resp := NewResponse(httpWriter)
+ resp.requestAccept = httpRequest.Header.Get(HEADER_Accept)
+ return NewRequest(httpRequest), resp
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/cors_filter.go b/vendor/github.com/emicklei/go-restful/v3/cors_filter.go
new file mode 100644
index 000000000..9d18dfb7b
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/cors_filter.go
@@ -0,0 +1,193 @@
+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 (
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// CrossOriginResourceSharing is used to create a Container Filter that implements CORS.
+// Cross-origin resource sharing (CORS) is a mechanism that allows JavaScript on a web page
+// to make XMLHttpRequests to another domain, not the domain the JavaScript originated from.
+//
+// http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+// http://enable-cors.org/server.html
+// http://www.html5rocks.com/en/tutorials/cors/#toc-handling-a-not-so-simple-request
+type CrossOriginResourceSharing struct {
+ ExposeHeaders []string // list of Header names
+
+ // AllowedHeaders is alist of Header names. Checking is case-insensitive.
+ // The list may contain the special wildcard string ".*" ; all is allowed
+ AllowedHeaders []string
+
+ // AllowedDomains is a list of allowed values for Http Origin.
+ // The list may contain the special wildcard string ".*" ; all is allowed
+ // If empty all are allowed.
+ AllowedDomains []string
+
+ // AllowedDomainFunc is optional and is a function that will do the check
+ // when the origin is not part of the AllowedDomains and it does not contain the wildcard ".*".
+ AllowedDomainFunc func(origin string) bool
+
+ // AllowedMethods is either empty or has a list of http methods names. Checking is case-insensitive.
+ AllowedMethods []string
+ MaxAge int // number of seconds before requiring new Options request
+ CookiesAllowed bool
+ Container *Container
+
+ allowedOriginPatterns []*regexp.Regexp // internal field for origin regexp check.
+}
+
+// Filter is a filter function that implements the CORS flow as documented on http://enable-cors.org/server.html
+// and http://www.html5rocks.com/static/images/cors_server_flowchart.png
+func (c CrossOriginResourceSharing) Filter(req *Request, resp *Response, chain *FilterChain) {
+ origin := req.Request.Header.Get(HEADER_Origin)
+ if len(origin) == 0 {
+ if trace {
+ traceLogger.Print("no Http header Origin set")
+ }
+ chain.ProcessFilter(req, resp)
+ return
+ }
+ if !c.isOriginAllowed(origin) { // check whether this origin is allowed
+ if trace {
+ traceLogger.Printf("HTTP Origin:%s is not part of %v, neither matches any part of %v", origin, c.AllowedDomains, c.allowedOriginPatterns)
+ }
+ chain.ProcessFilter(req, resp)
+ return
+ }
+ if req.Request.Method != "OPTIONS" {
+ c.doActualRequest(req, resp)
+ chain.ProcessFilter(req, resp)
+ return
+ }
+ if acrm := req.Request.Header.Get(HEADER_AccessControlRequestMethod); acrm != "" {
+ c.doPreflightRequest(req, resp)
+ } else {
+ c.doActualRequest(req, resp)
+ chain.ProcessFilter(req, resp)
+ return
+ }
+}
+
+func (c CrossOriginResourceSharing) doActualRequest(req *Request, resp *Response) {
+ c.setOptionsHeaders(req, resp)
+ // continue processing the response
+}
+
+func (c *CrossOriginResourceSharing) doPreflightRequest(req *Request, resp *Response) {
+ if len(c.AllowedMethods) == 0 {
+ if c.Container == nil {
+ c.AllowedMethods = DefaultContainer.computeAllowedMethods(req)
+ } else {
+ c.AllowedMethods = c.Container.computeAllowedMethods(req)
+ }
+ }
+
+ acrm := req.Request.Header.Get(HEADER_AccessControlRequestMethod)
+ if !c.isValidAccessControlRequestMethod(acrm, c.AllowedMethods) {
+ if trace {
+ traceLogger.Printf("Http header %s:%s is not in %v",
+ HEADER_AccessControlRequestMethod,
+ acrm,
+ c.AllowedMethods)
+ }
+ return
+ }
+ acrhs := req.Request.Header.Get(HEADER_AccessControlRequestHeaders)
+ if len(acrhs) > 0 {
+ for _, each := range strings.Split(acrhs, ",") {
+ if !c.isValidAccessControlRequestHeader(strings.Trim(each, " ")) {
+ if trace {
+ traceLogger.Printf("Http header %s:%s is not in %v",
+ HEADER_AccessControlRequestHeaders,
+ acrhs,
+ c.AllowedHeaders)
+ }
+ return
+ }
+ }
+ }
+ resp.AddHeader(HEADER_AccessControlAllowMethods, strings.Join(c.AllowedMethods, ","))
+ resp.AddHeader(HEADER_AccessControlAllowHeaders, acrhs)
+ c.setOptionsHeaders(req, resp)
+
+ // return http 200 response, no body
+}
+
+func (c CrossOriginResourceSharing) setOptionsHeaders(req *Request, resp *Response) {
+ c.checkAndSetExposeHeaders(resp)
+ c.setAllowOriginHeader(req, resp)
+ c.checkAndSetAllowCredentials(resp)
+ if c.MaxAge > 0 {
+ resp.AddHeader(HEADER_AccessControlMaxAge, strconv.Itoa(c.MaxAge))
+ }
+}
+
+func (c CrossOriginResourceSharing) isOriginAllowed(origin string) bool {
+ if len(origin) == 0 {
+ return false
+ }
+ lowerOrigin := strings.ToLower(origin)
+ if len(c.AllowedDomains) == 0 {
+ if c.AllowedDomainFunc != nil {
+ return c.AllowedDomainFunc(lowerOrigin)
+ }
+ return true
+ }
+
+ // exact match on each allowed domain
+ for _, domain := range c.AllowedDomains {
+ if domain == ".*" || strings.ToLower(domain) == lowerOrigin {
+ return true
+ }
+ }
+ if c.AllowedDomainFunc != nil {
+ return c.AllowedDomainFunc(origin)
+ }
+ return false
+}
+
+func (c CrossOriginResourceSharing) setAllowOriginHeader(req *Request, resp *Response) {
+ origin := req.Request.Header.Get(HEADER_Origin)
+ if c.isOriginAllowed(origin) {
+ resp.AddHeader(HEADER_AccessControlAllowOrigin, origin)
+ }
+}
+
+func (c CrossOriginResourceSharing) checkAndSetExposeHeaders(resp *Response) {
+ if len(c.ExposeHeaders) > 0 {
+ resp.AddHeader(HEADER_AccessControlExposeHeaders, strings.Join(c.ExposeHeaders, ","))
+ }
+}
+
+func (c CrossOriginResourceSharing) checkAndSetAllowCredentials(resp *Response) {
+ if c.CookiesAllowed {
+ resp.AddHeader(HEADER_AccessControlAllowCredentials, "true")
+ }
+}
+
+func (c CrossOriginResourceSharing) isValidAccessControlRequestMethod(method string, allowedMethods []string) bool {
+ for _, each := range allowedMethods {
+ if each == method {
+ return true
+ }
+ }
+ return false
+}
+
+func (c CrossOriginResourceSharing) isValidAccessControlRequestHeader(header string) bool {
+ for _, each := range c.AllowedHeaders {
+ if strings.ToLower(each) == strings.ToLower(header) {
+ return true
+ }
+ if each == "*" {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/coverage.sh b/vendor/github.com/emicklei/go-restful/v3/coverage.sh
new file mode 100644
index 000000000..e27dbf1a9
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/coverage.sh
@@ -0,0 +1,2 @@
+go test -coverprofile=coverage.out
+go tool cover -html=coverage.out \ No newline at end of file
diff --git a/vendor/github.com/emicklei/go-restful/v3/curly.go b/vendor/github.com/emicklei/go-restful/v3/curly.go
new file mode 100644
index 000000000..ba1fc5d5f
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/curly.go
@@ -0,0 +1,173 @@
+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"
+ "regexp"
+ "sort"
+ "strings"
+)
+
+// CurlyRouter expects Routes with paths that contain zero or more parameters in curly brackets.
+type CurlyRouter struct{}
+
+// SelectRoute is part of the Router interface and returns the best match
+// for the WebService and its Route for the given Request.
+func (c CurlyRouter) SelectRoute(
+ webServices []*WebService,
+ httpRequest *http.Request) (selectedService *WebService, selected *Route, err error) {
+
+ requestTokens := tokenizePath(httpRequest.URL.Path)
+
+ detectedService := c.detectWebService(requestTokens, webServices)
+ if detectedService == nil {
+ if trace {
+ traceLogger.Printf("no WebService was found to match URL path:%s\n", httpRequest.URL.Path)
+ }
+ return nil, nil, NewError(http.StatusNotFound, "404: Page Not Found")
+ }
+ candidateRoutes := c.selectRoutes(detectedService, requestTokens)
+ if len(candidateRoutes) == 0 {
+ if trace {
+ traceLogger.Printf("no Route in WebService with path %s was found to match URL path:%s\n", detectedService.rootPath, httpRequest.URL.Path)
+ }
+ return detectedService, nil, NewError(http.StatusNotFound, "404: Page Not Found")
+ }
+ selectedRoute, err := c.detectRoute(candidateRoutes, httpRequest)
+ if selectedRoute == nil {
+ return detectedService, nil, err
+ }
+ return detectedService, selectedRoute, nil
+}
+
+// selectRoutes return a collection of Route from a WebService that matches the path tokens from the request.
+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, each.hasCustomVerb)
+ if matches {
+ candidates.add(curlyRoute{each, paramCount, staticCount}) // TODO make sure Routes() return pointers?
+ }
+ }
+ sort.Sort(candidates)
+ return candidates
+}
+
+// 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, 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)
+ if count == 0 || !strings.HasSuffix(routeTokens[count-1], "*}") {
+ return false, 0, 0
+ }
+ // proceed
+ }
+ for i, routeToken := range routeTokens {
+ if i == len(requestTokens) {
+ // reached end of request path
+ 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 {
+ // match by regex
+ matchesToken, matchesRemainder := c.regularMatchesPathToken(routeToken, colon, requestToken)
+ if !matchesToken {
+ return false, 0, 0
+ }
+ if matchesRemainder {
+ break
+ }
+ }
+ } else { // no { prefix
+ if requestToken != routeToken {
+ return false, 0, 0
+ }
+ staticCount++
+ }
+ }
+ return true, paramCount, staticCount
+}
+
+// regularMatchesPathToken tests whether the regular expression part of routeToken matches the requestToken or all remaining tokens
+// format routeToken is {someVar:someExpression}, e.g. {zipcode:[\d][\d][\d][\d][A-Z][A-Z]}
+func (c CurlyRouter) regularMatchesPathToken(routeToken string, colon int, requestToken string) (matchesToken bool, matchesRemainder bool) {
+ regPart := routeToken[colon+1 : len(routeToken)-1]
+ if regPart == "*" {
+ if trace {
+ traceLogger.Printf("wildcard parameter detected in route token %s that matches %s\n", routeToken, requestToken)
+ }
+ return true, true
+ }
+ matched, err := regexp.MatchString(regPart, requestToken)
+ return (matched && err == nil), false
+}
+
+var jsr311Router = RouterJSR311{}
+
+// detectRoute selectes from a list of Route the first match by inspecting both the Accept and Content-Type
+// headers of the Request. See also RouterJSR311 in jsr311.go
+func (c CurlyRouter) detectRoute(candidateRoutes sortableCurlyRoutes, httpRequest *http.Request) (*Route, error) {
+ // tracing is done inside detectRoute
+ return jsr311Router.detectRoute(candidateRoutes.routes(), httpRequest)
+}
+
+// detectWebService returns the best matching webService given the list of path tokens.
+// see also computeWebserviceScore
+func (c CurlyRouter) detectWebService(requestTokens []string, webServices []*WebService) *WebService {
+ var best *WebService
+ score := -1
+ for _, each := range webServices {
+ matches, eachScore := c.computeWebserviceScore(requestTokens, each.pathExpr.tokens)
+ if matches && (eachScore > score) {
+ best = each
+ score = eachScore
+ }
+ }
+ return best
+}
+
+// computeWebserviceScore returns whether tokens match and
+// the weighted score of the longest matching consecutive tokens from the beginning.
+func (c CurlyRouter) computeWebserviceScore(requestTokens []string, tokens []string) (bool, int) {
+ if len(tokens) > len(requestTokens) {
+ return false, 0
+ }
+ score := 0
+ for i := 0; i < len(tokens); i++ {
+ each := requestTokens[i]
+ other := tokens[i]
+ if len(each) == 0 && len(other) == 0 {
+ score++
+ continue
+ }
+ if len(other) > 0 && strings.HasPrefix(other, "{") {
+ // no empty match
+ if len(each) == 0 {
+ return false, score
+ }
+ score += 1
+ } else {
+ // not a parameter
+ if each != other {
+ return false, score
+ }
+ score += (len(tokens) - i) * 10 //fuzzy
+ }
+ }
+ return true, score
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/curly_route.go b/vendor/github.com/emicklei/go-restful/v3/curly_route.go
new file mode 100644
index 000000000..403dd3be9
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/curly_route.go
@@ -0,0 +1,54 @@
+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.
+
+// curlyRoute exits for sorting Routes by the CurlyRouter based on number of parameters and number of static path elements.
+type curlyRoute struct {
+ route Route
+ paramCount int
+ staticCount int
+}
+
+// sortableCurlyRoutes orders by most parameters and path elements first.
+type sortableCurlyRoutes []curlyRoute
+
+func (s *sortableCurlyRoutes) add(route curlyRoute) {
+ *s = append(*s, route)
+}
+
+func (s sortableCurlyRoutes) routes() (routes []Route) {
+ routes = make([]Route, 0, len(s))
+ for _, each := range s {
+ routes = append(routes, each.route) // TODO change return type
+ }
+ return routes
+}
+
+func (s sortableCurlyRoutes) Len() int {
+ return len(s)
+}
+func (s sortableCurlyRoutes) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+func (s sortableCurlyRoutes) Less(i, j int) bool {
+ a := s[j]
+ b := s[i]
+
+ // primary key
+ if a.staticCount < b.staticCount {
+ return true
+ }
+ if a.staticCount > b.staticCount {
+ return false
+ }
+ // secundary key
+ if a.paramCount < b.paramCount {
+ return true
+ }
+ if a.paramCount > b.paramCount {
+ return false
+ }
+ return a.route.Path < b.route.Path
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/custom_verb.go b/vendor/github.com/emicklei/go-restful/v3/custom_verb.go
new file mode 100644
index 000000000..bfc17efde
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/custom_verb.go
@@ -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, "")
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/doc.go b/vendor/github.com/emicklei/go-restful/v3/doc.go
new file mode 100644
index 000000000..69b13057d
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/doc.go
@@ -0,0 +1,185 @@
+/*
+Package restful , a lean package for creating REST-style WebServices without magic.
+
+WebServices and Routes
+
+A WebService has a collection of Route objects that dispatch incoming Http Requests to a function calls.
+Typically, a WebService has a root path (e.g. /users) and defines common MIME types for its routes.
+WebServices must be added to a container (see below) in order to handler Http requests from a server.
+
+A Route is defined by a HTTP method, an URL path and (optionally) the MIME types it consumes (Content-Type) and produces (Accept).
+This package has the logic to find the best matching Route and if found, call its Function.
+
+ ws := new(restful.WebService)
+ ws.
+ Path("/users").
+ Consumes(restful.MIME_JSON, restful.MIME_XML).
+ Produces(restful.MIME_JSON, restful.MIME_XML)
+
+ ws.Route(ws.GET("/{user-id}").To(u.findUser)) // u is a UserResource
+
+ ...
+
+ // GET http://localhost:8080/users/1
+ func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
+ id := request.PathParameter("user-id")
+ ...
+ }
+
+The (*Request, *Response) arguments provide functions for reading information from the request and writing information back to the response.
+
+See the example https://github.com/emicklei/go-restful/blob/v3/examples/user-resource/restful-user-resource.go with a full implementation.
+
+Regular expression matching Routes
+
+A Route parameter can be specified using the format "uri/{var[:regexp]}" or the special version "uri/{var:*}" for matching the tail of the path.
+For example, /persons/{name:[A-Z][A-Z]} can be used to restrict values for the parameter "name" to only contain capital alphabetic characters.
+Regular expressions must use the standard Go syntax as described in the regexp package. (https://code.google.com/p/re2/wiki/Syntax)
+This feature requires the use of a CurlyRouter.
+
+Containers
+
+A Container holds a collection of WebServices, Filters and a http.ServeMux for multiplexing http requests.
+Using the statements "restful.Add(...) and restful.Filter(...)" will register WebServices and Filters to the Default Container.
+The Default container of go-restful uses the http.DefaultServeMux.
+You can create your own Container and create a new http.Server for that particular container.
+
+ container := restful.NewContainer()
+ server := &http.Server{Addr: ":8081", Handler: container}
+
+Filters
+
+A filter dynamically intercepts requests and responses to transform or use the information contained in the requests or responses.
+You can use filters to perform generic logging, measurement, authentication, redirect, set response headers etc.
+In the restful package there are three hooks into the request,response flow where filters can be added.
+Each filter must define a FilterFunction:
+
+ func (req *restful.Request, resp *restful.Response, chain *restful.FilterChain)
+
+Use the following statement to pass the request,response pair to the next filter or RouteFunction
+
+ chain.ProcessFilter(req, resp)
+
+Container Filters
+
+These are processed before any registered WebService.
+
+ // install a (global) filter for the default container (processed before any webservice)
+ restful.Filter(globalLogging)
+
+WebService Filters
+
+These are processed before any Route of a WebService.
+
+ // install a webservice filter (processed before any route)
+ ws.Filter(webserviceLogging).Filter(measureTime)
+
+
+Route Filters
+
+These are processed before calling the function associated with the Route.
+
+ // install 2 chained route filters (processed before calling findUser)
+ ws.Route(ws.GET("/{user-id}").Filter(routeLogging).Filter(NewCountFilter().routeCounter).To(findUser))
+
+See the example https://github.com/emicklei/go-restful/blob/v3/examples/filters/restful-filters.go with full implementations.
+
+Response Encoding
+
+Two encodings are supported: gzip and deflate. To enable this for all responses:
+
+ restful.DefaultContainer.EnableContentEncoding(true)
+
+If a Http request includes the Accept-Encoding header then the response content will be compressed using the specified encoding.
+Alternatively, you can create a Filter that performs the encoding and install it per WebService or Route.
+
+See the example https://github.com/emicklei/go-restful/blob/v3/examples/encoding/restful-encoding-filter.go
+
+OPTIONS support
+
+By installing a pre-defined container filter, your Webservice(s) can respond to the OPTIONS Http request.
+
+ Filter(OPTIONSFilter())
+
+CORS
+
+By installing the filter of a CrossOriginResourceSharing (CORS), your WebService(s) can handle CORS requests.
+
+ cors := CrossOriginResourceSharing{ExposeHeaders: []string{"X-My-Header"}, CookiesAllowed: false, Container: DefaultContainer}
+ Filter(cors.Filter)
+
+Error Handling
+
+Unexpected things happen. If a request cannot be processed because of a failure, your service needs to tell via the response what happened and why.
+For this reason HTTP status codes exist and it is important to use the correct code in every exceptional situation.
+
+ 400: Bad Request
+
+If path or query parameters are not valid (content or type) then use http.StatusBadRequest.
+
+ 404: Not Found
+
+Despite a valid URI, the resource requested may not be available
+
+ 500: Internal Server Error
+
+If the application logic could not process the request (or write the response) then use http.StatusInternalServerError.
+
+ 405: Method Not Allowed
+
+The request has a valid URL but the method (GET,PUT,POST,...) is not allowed.
+
+ 406: Not Acceptable
+
+The request does not have or has an unknown Accept Header set for this operation.
+
+ 415: Unsupported Media Type
+
+The request does not have or has an unknown Content-Type Header set for this operation.
+
+ServiceError
+
+In addition to setting the correct (error) Http status code, you can choose to write a ServiceError message on the response.
+
+Performance options
+
+This package has several options that affect the performance of your service. It is important to understand them and how you can change it.
+
+ restful.DefaultContainer.DoNotRecover(false)
+
+DoNotRecover controls whether panics will be caught to return HTTP 500.
+If set to false, the container will recover from panics.
+Default value is true
+
+ restful.SetCompressorProvider(NewBoundedCachedCompressors(20, 20))
+
+If content encoding is enabled then the default strategy for getting new gzip/zlib writers and readers is to use a sync.Pool.
+Because writers are expensive structures, performance is even more improved when using a preloaded cache. You can also inject your own implementation.
+
+Trouble shooting
+
+This package has the means to produce detail logging of the complete Http request matching process and filter invocation.
+Enabling this feature requires you to set an implementation of restful.StdLogger (e.g. log.Logger) instance such as:
+
+ restful.TraceLogger(log.New(os.Stdout, "[restful] ", log.LstdFlags|log.Lshortfile))
+
+Logging
+
+The restful.SetLogger() method allows you to override the logger used by the package. By default restful
+uses the standard library `log` package and logs to stdout. Different logging packages are supported as
+long as they conform to `StdLogger` interface defined in the `log` sub-package, writing an adapter for your
+preferred package is simple.
+
+Resources
+
+[project]: https://github.com/emicklei/go-restful
+
+[examples]: https://github.com/emicklei/go-restful/blob/master/examples
+
+[design]: http://ernestmicklei.com/2012/11/11/go-restful-api-design/
+
+[showcases]: https://github.com/emicklei/mora, https://github.com/emicklei/landskape
+
+(c) 2012-2015, http://ernestmicklei.com. MIT License
+*/
+package restful
diff --git a/vendor/github.com/emicklei/go-restful/v3/entity_accessors.go b/vendor/github.com/emicklei/go-restful/v3/entity_accessors.go
new file mode 100644
index 000000000..66dfc824f
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/entity_accessors.go
@@ -0,0 +1,162 @@
+package restful
+
+// Copyright 2015 Ernest Micklei. All rights reserved.
+// Use of this source code is governed by a license
+// that can be found in the LICENSE file.
+
+import (
+ "encoding/xml"
+ "strings"
+ "sync"
+)
+
+// EntityReaderWriter can read and write values using an encoding such as JSON,XML.
+type EntityReaderWriter interface {
+ // Read a serialized version of the value from the request.
+ // The Request may have a decompressing reader. Depends on Content-Encoding.
+ Read(req *Request, v interface{}) error
+
+ // Write a serialized version of the value on the response.
+ // The Response may have a compressing writer. Depends on Accept-Encoding.
+ // status should be a valid Http Status code
+ Write(resp *Response, status int, v interface{}) error
+}
+
+// entityAccessRegistry is a singleton
+var entityAccessRegistry = &entityReaderWriters{
+ protection: new(sync.RWMutex),
+ accessors: map[string]EntityReaderWriter{},
+}
+
+// entityReaderWriters associates MIME to an EntityReaderWriter
+type entityReaderWriters struct {
+ protection *sync.RWMutex
+ accessors map[string]EntityReaderWriter
+}
+
+func init() {
+ RegisterEntityAccessor(MIME_JSON, NewEntityAccessorJSON(MIME_JSON))
+ RegisterEntityAccessor(MIME_XML, NewEntityAccessorXML(MIME_XML))
+}
+
+// RegisterEntityAccessor add/overrides the ReaderWriter for encoding content with this MIME type.
+func RegisterEntityAccessor(mime string, erw EntityReaderWriter) {
+ entityAccessRegistry.protection.Lock()
+ defer entityAccessRegistry.protection.Unlock()
+ entityAccessRegistry.accessors[mime] = erw
+}
+
+// NewEntityAccessorJSON returns a new EntityReaderWriter for accessing JSON content.
+// This package is already initialized with such an accessor using the MIME_JSON contentType.
+func NewEntityAccessorJSON(contentType string) EntityReaderWriter {
+ return entityJSONAccess{ContentType: contentType}
+}
+
+// NewEntityAccessorXML returns a new EntityReaderWriter for accessing XML content.
+// This package is already initialized with such an accessor using the MIME_XML contentType.
+func NewEntityAccessorXML(contentType string) EntityReaderWriter {
+ return entityXMLAccess{ContentType: contentType}
+}
+
+// accessorAt returns the registered ReaderWriter for this MIME type.
+func (r *entityReaderWriters) accessorAt(mime string) (EntityReaderWriter, bool) {
+ r.protection.RLock()
+ defer r.protection.RUnlock()
+ er, ok := r.accessors[mime]
+ if !ok {
+ // retry with reverse lookup
+ // more expensive but we are in an exceptional situation anyway
+ for k, v := range r.accessors {
+ if strings.Contains(mime, k) {
+ return v, true
+ }
+ }
+ }
+ return er, ok
+}
+
+// entityXMLAccess is a EntityReaderWriter for XML encoding
+type entityXMLAccess struct {
+ // This is used for setting the Content-Type header when writing
+ ContentType string
+}
+
+// Read unmarshalls the value from XML
+func (e entityXMLAccess) Read(req *Request, v interface{}) error {
+ return xml.NewDecoder(req.Request.Body).Decode(v)
+}
+
+// Write marshalls the value to JSON and set the Content-Type Header.
+func (e entityXMLAccess) Write(resp *Response, status int, v interface{}) error {
+ return writeXML(resp, status, e.ContentType, v)
+}
+
+// writeXML marshalls the value to JSON and set the Content-Type Header.
+func writeXML(resp *Response, status int, contentType string, v interface{}) error {
+ if v == nil {
+ resp.WriteHeader(status)
+ // do not write a nil representation
+ return nil
+ }
+ if resp.prettyPrint {
+ // pretty output must be created and written explicitly
+ output, err := xml.MarshalIndent(v, " ", " ")
+ if err != nil {
+ return err
+ }
+ resp.Header().Set(HEADER_ContentType, contentType)
+ resp.WriteHeader(status)
+ _, err = resp.Write([]byte(xml.Header))
+ if err != nil {
+ return err
+ }
+ _, err = resp.Write(output)
+ return err
+ }
+ // not-so-pretty
+ resp.Header().Set(HEADER_ContentType, contentType)
+ resp.WriteHeader(status)
+ return xml.NewEncoder(resp).Encode(v)
+}
+
+// entityJSONAccess is a EntityReaderWriter for JSON encoding
+type entityJSONAccess struct {
+ // This is used for setting the Content-Type header when writing
+ ContentType string
+}
+
+// Read unmarshalls the value from JSON
+func (e entityJSONAccess) Read(req *Request, v interface{}) error {
+ decoder := NewDecoder(req.Request.Body)
+ decoder.UseNumber()
+ return decoder.Decode(v)
+}
+
+// Write marshalls the value to JSON and set the Content-Type Header.
+func (e entityJSONAccess) Write(resp *Response, status int, v interface{}) error {
+ return writeJSON(resp, status, e.ContentType, v)
+}
+
+// write marshalls the value to JSON and set the Content-Type Header.
+func writeJSON(resp *Response, status int, contentType string, v interface{}) error {
+ if v == nil {
+ resp.WriteHeader(status)
+ // do not write a nil representation
+ return nil
+ }
+ if resp.prettyPrint {
+ // pretty output must be created and written explicitly
+ output, err := MarshalIndent(v, "", " ")
+ if err != nil {
+ return err
+ }
+ resp.Header().Set(HEADER_ContentType, contentType)
+ resp.WriteHeader(status)
+ _, err = resp.Write(output)
+ return err
+ }
+ // not-so-pretty
+ resp.Header().Set(HEADER_ContentType, contentType)
+ resp.WriteHeader(status)
+ return NewEncoder(resp).Encode(v)
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/extensions.go b/vendor/github.com/emicklei/go-restful/v3/extensions.go
new file mode 100644
index 000000000..5023fa049
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/extensions.go
@@ -0,0 +1,21 @@
+package restful
+
+// Copyright 2021 Ernest Micklei. All rights reserved.
+// Use of this source code is governed by a license
+// that can be found in the LICENSE file.
+
+// ExtensionProperties provides storage of vendor extensions for entities
+type ExtensionProperties struct {
+ // Extensions vendor extensions used to describe extra functionality
+ // (https://swagger.io/docs/specification/2-0/swagger-extensions/)
+ Extensions map[string]interface{}
+}
+
+// AddExtension adds or updates a key=value pair to the extension map.
+func (ep *ExtensionProperties) AddExtension(key string, value interface{}) {
+ if ep.Extensions == nil {
+ ep.Extensions = map[string]interface{}{key: value}
+ } else {
+ ep.Extensions[key] = value
+ }
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/filter.go b/vendor/github.com/emicklei/go-restful/v3/filter.go
new file mode 100644
index 000000000..fd88c536c
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/filter.go
@@ -0,0 +1,37 @@
+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.
+
+// FilterChain is a request scoped object to process one or more filters before calling the target RouteFunction.
+type FilterChain struct {
+ Filters []FilterFunction // ordered list of FilterFunction
+ Index int // index into filters that is currently in progress
+ Target RouteFunction // function to call after passing all filters
+ ParameterDocs []*Parameter // the parameter docs for the route
+ Operation string // the name of the operation
+}
+
+// ProcessFilter passes the request,response pair through the next of Filters.
+// Each filter can decide to proceed to the next Filter or handle the Response itself.
+func (f *FilterChain) ProcessFilter(request *Request, response *Response) {
+ if f.Index < len(f.Filters) {
+ f.Index++
+ f.Filters[f.Index-1](request, response, f)
+ } else {
+ f.Target(request, response)
+ }
+}
+
+// FilterFunction definitions must call ProcessFilter on the FilterChain to pass on the control and eventually call the RouteFunction
+type FilterFunction func(*Request, *Response, *FilterChain)
+
+// NoBrowserCacheFilter is a filter function to set HTTP headers that disable browser caching
+// See examples/restful-no-cache-filter.go for usage
+func NoBrowserCacheFilter(req *Request, resp *Response, chain *FilterChain) {
+ resp.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1.
+ resp.Header().Set("Pragma", "no-cache") // HTTP 1.0.
+ resp.Header().Set("Expires", "0") // Proxies.
+ chain.ProcessFilter(req, resp)
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/filter_adapter.go b/vendor/github.com/emicklei/go-restful/v3/filter_adapter.go
new file mode 100644
index 000000000..c246512fc
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/filter_adapter.go
@@ -0,0 +1,21 @@
+package restful
+
+import (
+ "net/http"
+)
+
+// HttpMiddlewareHandler is a function that takes a http.Handler and returns a http.Handler
+type HttpMiddlewareHandler func(http.Handler) http.Handler
+
+// HttpMiddlewareHandlerToFilter converts a HttpMiddlewareHandler to a FilterFunction.
+func HttpMiddlewareHandlerToFilter(middleware HttpMiddlewareHandler) FilterFunction {
+ return func(req *Request, resp *Response, chain *FilterChain) {
+ next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ req.Request = r
+ resp.ResponseWriter = rw
+ chain.ProcessFilter(req, resp)
+ })
+
+ middleware(next).ServeHTTP(resp.ResponseWriter, req.Request)
+ }
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/json.go b/vendor/github.com/emicklei/go-restful/v3/json.go
new file mode 100644
index 000000000..871165166
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/json.go
@@ -0,0 +1,11 @@
+// +build !jsoniter
+
+package restful
+
+import "encoding/json"
+
+var (
+ MarshalIndent = json.MarshalIndent
+ NewDecoder = json.NewDecoder
+ NewEncoder = json.NewEncoder
+)
diff --git a/vendor/github.com/emicklei/go-restful/v3/jsoniter.go b/vendor/github.com/emicklei/go-restful/v3/jsoniter.go
new file mode 100644
index 000000000..11b8f8ae7
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/jsoniter.go
@@ -0,0 +1,12 @@
+// +build jsoniter
+
+package restful
+
+import "github.com/json-iterator/go"
+
+var (
+ json = jsoniter.ConfigCompatibleWithStandardLibrary
+ MarshalIndent = json.MarshalIndent
+ NewDecoder = json.NewDecoder
+ NewEncoder = json.NewEncoder
+)
diff --git a/vendor/github.com/emicklei/go-restful/v3/jsr311.go b/vendor/github.com/emicklei/go-restful/v3/jsr311.go
new file mode 100644
index 000000000..07a0c91e9
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/jsr311.go
@@ -0,0 +1,326 @@
+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"
+ "strings"
+)
+
+// 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
+}
+
+// ExtractParameters is used to obtain the path parameters from the route using the same matching
+// engine as the JSR 311 router.
+func (r RouterJSR311) ExtractParameters(route *Route, webService *WebService, urlPath string) map[string]string {
+ webServiceExpr := webService.pathExpr
+ webServiceMatches := webServiceExpr.Matcher.FindStringSubmatch(urlPath)
+ pathParameters := r.extractParams(webServiceExpr, webServiceMatches)
+ routeExpr := route.pathExpr
+ routeMatches := routeExpr.Matcher.FindStringSubmatch(webServiceMatches[len(webServiceMatches)-1])
+ routeParams := r.extractParams(routeExpr, routeMatches)
+ for key, value := range routeParams {
+ pathParameters[key] = value
+ }
+ return pathParameters
+}
+
+func (RouterJSR311) extractParams(pathExpr *pathExpression, matches []string) map[string]string {
+ params := map[string]string{}
+ for i := 1; i < len(matches); i++ {
+ if len(pathExpr.VarNames) >= i {
+ params[pathExpr.VarNames[i-1]] = matches[i]
+ }
+ }
+ return params
+}
+
+// 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) {
+ candidates := make([]*Route, 0, 8)
+ for i, each := range routes {
+ ok := true
+ for _, fn := range each.If {
+ if !fn(httpRequest) {
+ ok = false
+ break
+ }
+ }
+ if ok {
+ candidates = append(candidates, &routes[i])
+ }
+ }
+ if len(candidates) == 0 {
+ if trace {
+ traceLogger.Printf("no Route found (from %d) that passes conditional checks", len(routes))
+ }
+ return nil, NewError(http.StatusNotFound, "404: Not Found")
+ }
+
+ // http method
+ previous := candidates
+ candidates = candidates[:0]
+ for _, each := range previous {
+ if httpRequest.Method == each.Method {
+ candidates = append(candidates, each)
+ }
+ }
+ if len(candidates) == 0 {
+ if trace {
+ traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(previous), httpRequest.Method)
+ }
+ 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
+ contentType := httpRequest.Header.Get(HEADER_ContentType)
+ previous = candidates
+ candidates = candidates[:0]
+ for _, each := range previous {
+ if each.matchesContentType(contentType) {
+ candidates = append(candidates, each)
+ }
+ }
+ if len(candidates) == 0 {
+ if trace {
+ traceLogger.Printf("no Route found (from %d) that matches HTTP Content-Type: %s\n", len(previous), contentType)
+ }
+ if httpRequest.ContentLength > 0 {
+ return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type")
+ }
+ }
+
+ // accept
+ previous = candidates
+ candidates = candidates[:0]
+ accept := httpRequest.Header.Get(HEADER_Accept)
+ if len(accept) == 0 {
+ accept = "*/*"
+ }
+ for _, each := range previous {
+ if each.matchesAccept(accept) {
+ candidates = append(candidates, each)
+ }
+ }
+ if len(candidates) == 0 {
+ if trace {
+ traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(previous), accept)
+ }
+ available := []string{}
+ for _, candidate := range previous {
+ available = append(available, candidate.Produces...)
+ }
+ // if POST,PUT,PATCH without body
+ method, length := httpRequest.Method, httpRequest.Header.Get("Content-Length")
+ if (method == http.MethodPost ||
+ method == http.MethodPut ||
+ method == http.MethodPatch) && length == "" {
+ return nil, NewError(
+ http.StatusUnsupportedMediaType,
+ fmt.Sprintf("415: Unsupported Media Type\n\nAvailable representations: %s", strings.Join(available, ", ")),
+ )
+ }
+ 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
+}
+
+// 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
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/log/log.go b/vendor/github.com/emicklei/go-restful/v3/log/log.go
new file mode 100644
index 000000000..6cd44c7a5
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/log/log.go
@@ -0,0 +1,34 @@
+package log
+
+import (
+ stdlog "log"
+ "os"
+)
+
+// StdLogger corresponds to a minimal subset of the interface satisfied by stdlib log.Logger
+type StdLogger interface {
+ Print(v ...interface{})
+ Printf(format string, v ...interface{})
+}
+
+var Logger StdLogger
+
+func init() {
+ // default Logger
+ SetLogger(stdlog.New(os.Stderr, "[restful] ", stdlog.LstdFlags|stdlog.Lshortfile))
+}
+
+// SetLogger sets the logger for this package
+func SetLogger(customLogger StdLogger) {
+ Logger = customLogger
+}
+
+// Print delegates to the Logger
+func Print(v ...interface{}) {
+ Logger.Print(v...)
+}
+
+// Printf delegates to the Logger
+func Printf(format string, v ...interface{}) {
+ Logger.Printf(format, v...)
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/logger.go b/vendor/github.com/emicklei/go-restful/v3/logger.go
new file mode 100644
index 000000000..29202726f
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/logger.go
@@ -0,0 +1,32 @@
+package restful
+
+// Copyright 2014 Ernest Micklei. All rights reserved.
+// Use of this source code is governed by a license
+// that can be found in the LICENSE file.
+import (
+ "github.com/emicklei/go-restful/v3/log"
+)
+
+var trace bool = false
+var traceLogger log.StdLogger
+
+func init() {
+ traceLogger = log.Logger // use the package logger by default
+}
+
+// TraceLogger enables detailed logging of Http request matching and filter invocation. Default no logger is set.
+// You may call EnableTracing() directly to enable trace logging to the package-wide logger.
+func TraceLogger(logger log.StdLogger) {
+ traceLogger = logger
+ EnableTracing(logger != nil)
+}
+
+// SetLogger exposes the setter for the global logger on the top-level package
+func SetLogger(customLogger log.StdLogger) {
+ log.SetLogger(customLogger)
+}
+
+// EnableTracing can be used to Trace logging on and off.
+func EnableTracing(enabled bool) {
+ trace = enabled
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/mime.go b/vendor/github.com/emicklei/go-restful/v3/mime.go
new file mode 100644
index 000000000..33014471b
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/mime.go
@@ -0,0 +1,50 @@
+package restful
+
+import (
+ "strconv"
+ "strings"
+)
+
+type mime struct {
+ media string
+ quality float64
+}
+
+// insertMime adds a mime to a list and keeps it sorted by quality.
+func insertMime(l []mime, e mime) []mime {
+ for i, each := range l {
+ // if current mime has lower quality then insert before
+ if e.quality > each.quality {
+ left := append([]mime{}, l[0:i]...)
+ return append(append(left, e), l[i:]...)
+ }
+ }
+ return append(l, e)
+}
+
+const qFactorWeightingKey = "q"
+
+// sortedMimes returns a list of mime sorted (desc) by its specified quality.
+// e.g. text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
+func sortedMimes(accept string) (sorted []mime) {
+ for _, each := range strings.Split(accept, ",") {
+ typeAndQuality := strings.Split(strings.Trim(each, " "), ";")
+ if len(typeAndQuality) == 1 {
+ sorted = insertMime(sorted, mime{typeAndQuality[0], 1.0})
+ } else {
+ // take factor
+ qAndWeight := strings.Split(typeAndQuality[1], "=")
+ if len(qAndWeight) == 2 && strings.Trim(qAndWeight[0], " ") == qFactorWeightingKey {
+ f, err := strconv.ParseFloat(qAndWeight[1], 64)
+ if err != nil {
+ traceLogger.Printf("unable to parse quality in %s, %v", each, err)
+ } else {
+ sorted = insertMime(sorted, mime{typeAndQuality[0], f})
+ }
+ } else {
+ sorted = insertMime(sorted, mime{typeAndQuality[0], 1.0})
+ }
+ }
+ }
+ return
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/options_filter.go b/vendor/github.com/emicklei/go-restful/v3/options_filter.go
new file mode 100644
index 000000000..5c1b34251
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/options_filter.go
@@ -0,0 +1,34 @@
+package restful
+
+import "strings"
+
+// 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.
+
+// OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method
+// and provides the response with a set of allowed methods for the request URL Path.
+// As for any filter, you can also install it for a particular WebService within a Container.
+// Note: this filter is not needed when using CrossOriginResourceSharing (for CORS).
+func (c *Container) OPTIONSFilter(req *Request, resp *Response, chain *FilterChain) {
+ if "OPTIONS" != req.Request.Method {
+ chain.ProcessFilter(req, resp)
+ return
+ }
+
+ archs := req.Request.Header.Get(HEADER_AccessControlRequestHeaders)
+ methods := strings.Join(c.computeAllowedMethods(req), ",")
+ origin := req.Request.Header.Get(HEADER_Origin)
+
+ resp.AddHeader(HEADER_Allow, methods)
+ resp.AddHeader(HEADER_AccessControlAllowOrigin, origin)
+ resp.AddHeader(HEADER_AccessControlAllowHeaders, archs)
+ resp.AddHeader(HEADER_AccessControlAllowMethods, methods)
+}
+
+// OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method
+// and provides the response with a set of allowed methods for the request URL Path.
+// Note: this filter is not needed when using CrossOriginResourceSharing (for CORS).
+func OPTIONSFilter() FilterFunction {
+ return DefaultContainer.OPTIONSFilter
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/parameter.go b/vendor/github.com/emicklei/go-restful/v3/parameter.go
new file mode 100644
index 000000000..0b851bb43
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/parameter.go
@@ -0,0 +1,242 @@
+package restful
+
+import "sort"
+
+// 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.
+
+const (
+ // PathParameterKind = indicator of Request parameter type "path"
+ PathParameterKind = iota
+
+ // QueryParameterKind = indicator of Request parameter type "query"
+ QueryParameterKind
+
+ // BodyParameterKind = indicator of Request parameter type "body"
+ BodyParameterKind
+
+ // HeaderParameterKind = indicator of Request parameter type "header"
+ HeaderParameterKind
+
+ // FormParameterKind = indicator of Request parameter type "form"
+ FormParameterKind
+
+ // MultiPartFormParameterKind = indicator of Request parameter type "multipart/form-data"
+ MultiPartFormParameterKind
+
+ // CollectionFormatCSV comma separated values `foo,bar`
+ CollectionFormatCSV = CollectionFormat("csv")
+
+ // CollectionFormatSSV space separated values `foo bar`
+ CollectionFormatSSV = CollectionFormat("ssv")
+
+ // CollectionFormatTSV tab separated values `foo\tbar`
+ CollectionFormatTSV = CollectionFormat("tsv")
+
+ // CollectionFormatPipes pipe separated values `foo|bar`
+ CollectionFormatPipes = CollectionFormat("pipes")
+
+ // CollectionFormatMulti corresponds to multiple parameter instances instead of multiple values for a single
+ // instance `foo=bar&foo=baz`. This is valid only for QueryParameters and FormParameters
+ CollectionFormatMulti = CollectionFormat("multi")
+)
+
+type CollectionFormat string
+
+func (cf CollectionFormat) String() string {
+ return string(cf)
+}
+
+// Parameter is for documententing the parameter used in a Http Request
+// ParameterData kinds are Path,Query and Body
+type Parameter struct {
+ data *ParameterData
+}
+
+// ParameterData represents the state of a Parameter.
+// It is made public to make it accessible to e.g. the Swagger package.
+type ParameterData struct {
+ ExtensionProperties
+ Name, Description, DataType, DataFormat string
+ Kind int
+ Required bool
+ // AllowableValues is deprecated. Use PossibleValues instead
+ AllowableValues map[string]string
+ PossibleValues []string
+ AllowMultiple bool
+ AllowEmptyValue bool
+ DefaultValue string
+ CollectionFormat string
+ Pattern string
+ Minimum *float64
+ Maximum *float64
+ MinLength *int64
+ MaxLength *int64
+ MinItems *int64
+ MaxItems *int64
+ UniqueItems bool
+}
+
+// Data returns the state of the Parameter
+func (p *Parameter) Data() ParameterData {
+ return *p.data
+}
+
+// Kind returns the parameter type indicator (see const for valid values)
+func (p *Parameter) Kind() int {
+ return p.data.Kind
+}
+
+func (p *Parameter) bePath() *Parameter {
+ p.data.Kind = PathParameterKind
+ return p
+}
+func (p *Parameter) beQuery() *Parameter {
+ p.data.Kind = QueryParameterKind
+ return p
+}
+func (p *Parameter) beBody() *Parameter {
+ p.data.Kind = BodyParameterKind
+ return p
+}
+
+func (p *Parameter) beHeader() *Parameter {
+ p.data.Kind = HeaderParameterKind
+ return p
+}
+
+func (p *Parameter) beForm() *Parameter {
+ p.data.Kind = FormParameterKind
+ return p
+}
+
+func (p *Parameter) beMultiPartForm() *Parameter {
+ p.data.Kind = MultiPartFormParameterKind
+ return p
+}
+
+// Required sets the required field and returns the receiver
+func (p *Parameter) Required(required bool) *Parameter {
+ p.data.Required = required
+ return p
+}
+
+// AllowMultiple sets the allowMultiple field and returns the receiver
+func (p *Parameter) AllowMultiple(multiple bool) *Parameter {
+ p.data.AllowMultiple = multiple
+ return p
+}
+
+// AddExtension adds or updates a key=value pair to the extension map
+func (p *Parameter) AddExtension(key string, value interface{}) *Parameter {
+ p.data.AddExtension(key, value)
+ return p
+}
+
+// AllowEmptyValue sets the AllowEmptyValue field and returns the receiver
+func (p *Parameter) AllowEmptyValue(multiple bool) *Parameter {
+ p.data.AllowEmptyValue = multiple
+ return p
+}
+
+// AllowableValues is deprecated. Use PossibleValues instead. Both will be set.
+func (p *Parameter) AllowableValues(values map[string]string) *Parameter {
+ p.data.AllowableValues = values
+
+ allowableSortedKeys := make([]string, 0, len(values))
+ for k := range values {
+ allowableSortedKeys = append(allowableSortedKeys, k)
+ }
+ sort.Strings(allowableSortedKeys)
+
+ p.data.PossibleValues = make([]string, 0, len(values))
+ for _, k := range allowableSortedKeys {
+ p.data.PossibleValues = append(p.data.PossibleValues, values[k])
+ }
+ return p
+}
+
+// PossibleValues sets the possible values field and returns the receiver
+func (p *Parameter) PossibleValues(values []string) *Parameter {
+ p.data.PossibleValues = values
+ return p
+}
+
+// DataType sets the dataType field and returns the receiver
+func (p *Parameter) DataType(typeName string) *Parameter {
+ p.data.DataType = typeName
+ return p
+}
+
+// DataFormat sets the dataFormat field for Swagger UI
+func (p *Parameter) DataFormat(formatName string) *Parameter {
+ p.data.DataFormat = formatName
+ return p
+}
+
+// DefaultValue sets the default value field and returns the receiver
+func (p *Parameter) DefaultValue(stringRepresentation string) *Parameter {
+ p.data.DefaultValue = stringRepresentation
+ return p
+}
+
+// Description sets the description value field and returns the receiver
+func (p *Parameter) Description(doc string) *Parameter {
+ p.data.Description = doc
+ return p
+}
+
+// CollectionFormat sets the collection format for an array type
+func (p *Parameter) CollectionFormat(format CollectionFormat) *Parameter {
+ p.data.CollectionFormat = format.String()
+ return p
+}
+
+// Pattern sets the pattern field and returns the receiver
+func (p *Parameter) Pattern(pattern string) *Parameter {
+ p.data.Pattern = pattern
+ return p
+}
+
+// Minimum sets the minimum field and returns the receiver
+func (p *Parameter) Minimum(minimum float64) *Parameter {
+ p.data.Minimum = &minimum
+ return p
+}
+
+// Maximum sets the maximum field and returns the receiver
+func (p *Parameter) Maximum(maximum float64) *Parameter {
+ p.data.Maximum = &maximum
+ return p
+}
+
+// MinLength sets the minLength field and returns the receiver
+func (p *Parameter) MinLength(minLength int64) *Parameter {
+ p.data.MinLength = &minLength
+ return p
+}
+
+// MaxLength sets the maxLength field and returns the receiver
+func (p *Parameter) MaxLength(maxLength int64) *Parameter {
+ p.data.MaxLength = &maxLength
+ return p
+}
+
+// MinItems sets the minItems field and returns the receiver
+func (p *Parameter) MinItems(minItems int64) *Parameter {
+ p.data.MinItems = &minItems
+ return p
+}
+
+// MaxItems sets the maxItems field and returns the receiver
+func (p *Parameter) MaxItems(maxItems int64) *Parameter {
+ p.data.MaxItems = &maxItems
+ return p
+}
+
+// UniqueItems sets the uniqueItems field and returns the receiver
+func (p *Parameter) UniqueItems(uniqueItems bool) *Parameter {
+ p.data.UniqueItems = uniqueItems
+ return p
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/path_expression.go b/vendor/github.com/emicklei/go-restful/v3/path_expression.go
new file mode 100644
index 000000000..95a9a2545
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/path_expression.go
@@ -0,0 +1,74 @@
+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 (
+ "bytes"
+ "fmt"
+ "regexp"
+ "strings"
+)
+
+// PathExpression holds a compiled path expression (RegExp) needed to match against
+// Http request paths and to extract path parameter values.
+type pathExpression struct {
+ LiteralCount int // the number of literal characters (means those not resulting from template variable substitution)
+ VarNames []string // the names of parameters (enclosed by {}) in the path
+ VarCount int // the number of named parameters (enclosed by {}) in the path
+ Matcher *regexp.Regexp
+ Source string // Path as defined by the RouteBuilder
+ tokens []string
+}
+
+// NewPathExpression creates a PathExpression from the input URL path.
+// Returns an error if the path is invalid.
+func newPathExpression(path string) (*pathExpression, error) {
+ expression, literalCount, varNames, varCount, tokens := templateToRegularExpression(path)
+ compiled, err := regexp.Compile(expression)
+ if err != nil {
+ return nil, err
+ }
+ return &pathExpression{literalCount, varNames, varCount, compiled, expression, tokens}, nil
+}
+
+// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-370003.7.3
+func templateToRegularExpression(template string) (expression string, literalCount int, varNames []string, varCount int, tokens []string) {
+ var buffer bytes.Buffer
+ buffer.WriteString("^")
+ //tokens = strings.Split(template, "/")
+ tokens = tokenizePath(template)
+ for _, each := range tokens {
+ if each == "" {
+ continue
+ }
+ buffer.WriteString("/")
+ if strings.HasPrefix(each, "{") {
+ // check for regular expression in variable
+ colon := strings.Index(each, ":")
+ var varName string
+ if colon != -1 {
+ // extract expression
+ varName = strings.TrimSpace(each[1:colon])
+ paramExpr := strings.TrimSpace(each[colon+1 : len(each)-1])
+ if paramExpr == "*" { // special case
+ buffer.WriteString("(.*)")
+ } else {
+ buffer.WriteString(fmt.Sprintf("(%s)", paramExpr)) // between colon and closing moustache
+ }
+ } else {
+ // plain var
+ varName = strings.TrimSpace(each[1 : len(each)-1])
+ buffer.WriteString("([^/]+?)")
+ }
+ varNames = append(varNames, varName)
+ varCount += 1
+ } else {
+ literalCount += len(each)
+ encoded := each // TODO URI encode
+ buffer.WriteString(regexp.QuoteMeta(encoded))
+ }
+ }
+ return strings.TrimRight(buffer.String(), "/") + "(/.*)?$", literalCount, varNames, varCount, tokens
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/path_processor.go b/vendor/github.com/emicklei/go-restful/v3/path_processor.go
new file mode 100644
index 000000000..141573245
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/path_processor.go
@@ -0,0 +1,74 @@
+package restful
+
+import (
+ "bytes"
+ "strings"
+)
+
+// Copyright 2018 Ernest Micklei. All rights reserved.
+// Use of this source code is governed by a license
+// that can be found in the LICENSE file.
+
+// PathProcessor is extra behaviour that a Router can provide to extract path parameters from the path.
+// If a Router does not implement this interface then the default behaviour will be used.
+type PathProcessor interface {
+ // ExtractParameters gets the path parameters defined in the route and webService from the urlPath
+ ExtractParameters(route *Route, webService *WebService, urlPath string) map[string]string
+}
+
+type defaultPathProcessor struct{}
+
+// Extract the parameters from the request url path
+func (d defaultPathProcessor) ExtractParameters(r *Route, _ *WebService, urlPath string) map[string]string {
+ urlParts := tokenizePath(urlPath)
+ pathParameters := map[string]string{}
+ for i, key := range r.pathParts {
+ var value string
+ if i >= len(urlParts) {
+ value = ""
+ } else {
+ value = urlParts[i]
+ }
+ 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]
+ keyPart := key[1:colon]
+ if regPart == "*" {
+ pathParameters[keyPart] = untokenizePath(i, urlParts)
+ break
+ } else {
+ pathParameters[keyPart] = value
+ }
+ } else {
+ // without enclosing {}
+ 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]
+ }
+ }
+ }
+ return pathParameters
+}
+
+// Untokenize back into an URL path using the slash separator
+func untokenizePath(offset int, parts []string) string {
+ var buffer bytes.Buffer
+ for p := offset; p < len(parts); p++ {
+ buffer.WriteString(parts[p])
+ // do not end
+ if p < len(parts)-1 {
+ buffer.WriteString("/")
+ }
+ }
+ return buffer.String()
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/request.go b/vendor/github.com/emicklei/go-restful/v3/request.go
new file mode 100644
index 000000000..0020095e8
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/request.go
@@ -0,0 +1,133 @@
+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 (
+ "compress/zlib"
+ "net/http"
+)
+
+var defaultRequestContentType string
+
+// Request is a wrapper for a http Request that provides convenience methods
+type Request struct {
+ Request *http.Request
+ pathParameters map[string]string
+ attributes map[string]interface{} // for storing request-scoped values
+ selectedRoute *Route // is nil when no route was matched
+}
+
+func NewRequest(httpRequest *http.Request) *Request {
+ return &Request{
+ Request: httpRequest,
+ pathParameters: map[string]string{},
+ attributes: map[string]interface{}{},
+ } // empty parameters, attributes
+}
+
+// If ContentType is missing or */* is given then fall back to this type, otherwise
+// a "Unable to unmarshal content of type:" response is returned.
+// Valid values are restful.MIME_JSON and restful.MIME_XML
+// Example:
+//
+// restful.DefaultRequestContentType(restful.MIME_JSON)
+func DefaultRequestContentType(mime string) {
+ defaultRequestContentType = mime
+}
+
+// PathParameter accesses the Path parameter value by its name
+func (r *Request) PathParameter(name string) string {
+ return r.pathParameters[name]
+}
+
+// PathParameters accesses the Path parameter values
+func (r *Request) PathParameters() map[string]string {
+ return r.pathParameters
+}
+
+// QueryParameter returns the (first) Query parameter value by its name
+func (r *Request) QueryParameter(name string) string {
+ return r.Request.URL.Query().Get(name)
+}
+
+// QueryParameters returns the all the query parameters values by name
+func (r *Request) QueryParameters(name string) []string {
+ return r.Request.URL.Query()[name]
+}
+
+// BodyParameter parses the body of the request (once for typically a POST or a PUT) and returns the value of the given name or an error.
+func (r *Request) BodyParameter(name string) (string, error) {
+ err := r.Request.ParseForm()
+ if err != nil {
+ return "", err
+ }
+ return r.Request.PostFormValue(name), nil
+}
+
+// HeaderParameter returns the HTTP Header value of a Header name or empty if missing
+func (r *Request) HeaderParameter(name string) string {
+ return r.Request.Header.Get(name)
+}
+
+// ReadEntity checks the Accept header and reads the content into the entityPointer.
+func (r *Request) ReadEntity(entityPointer interface{}) (err error) {
+ contentType := r.Request.Header.Get(HEADER_ContentType)
+ contentEncoding := r.Request.Header.Get(HEADER_ContentEncoding)
+
+ // check if the request body needs decompression
+ if ENCODING_GZIP == contentEncoding {
+ gzipReader := currentCompressorProvider.AcquireGzipReader()
+ defer currentCompressorProvider.ReleaseGzipReader(gzipReader)
+ gzipReader.Reset(r.Request.Body)
+ r.Request.Body = gzipReader
+ } else if ENCODING_DEFLATE == contentEncoding {
+ zlibReader, err := zlib.NewReader(r.Request.Body)
+ if err != nil {
+ return err
+ }
+ r.Request.Body = zlibReader
+ }
+
+ // lookup the EntityReader, use defaultRequestContentType if needed and provided
+ entityReader, ok := entityAccessRegistry.accessorAt(contentType)
+ if !ok {
+ if len(defaultRequestContentType) != 0 {
+ entityReader, ok = entityAccessRegistry.accessorAt(defaultRequestContentType)
+ }
+ if !ok {
+ return NewError(http.StatusBadRequest, "Unable to unmarshal content of type:"+contentType)
+ }
+ }
+ return entityReader.Read(r, entityPointer)
+}
+
+// SetAttribute adds or replaces the attribute with the given value.
+func (r *Request) SetAttribute(name string, value interface{}) {
+ r.attributes[name] = value
+}
+
+// Attribute returns the value associated to the given name. Returns nil if absent.
+func (r Request) Attribute(name string) interface{} {
+ return r.attributes[name]
+}
+
+// SelectedRoutePath root path + route path that matched the request, e.g. /meetings/{id}/attendees
+// If no route was matched then return an empty string.
+func (r Request) SelectedRoutePath() string {
+ if r.selectedRoute == nil {
+ return ""
+ }
+ // skip creating an accessor
+ return r.selectedRoute.Path
+}
+
+// SelectedRoute returns a reader to access the selected Route by the container
+// Returns nil if no route was matched.
+func (r Request) SelectedRoute() RouteReader {
+ if r.selectedRoute == nil {
+ return nil
+ }
+ return routeAccessor{route: r.selectedRoute}
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/response.go b/vendor/github.com/emicklei/go-restful/v3/response.go
new file mode 100644
index 000000000..a41a92cc2
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/response.go
@@ -0,0 +1,259 @@
+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 (
+ "bufio"
+ "errors"
+ "net"
+ "net/http"
+)
+
+// DefaultResponseMimeType is DEPRECATED, use DefaultResponseContentType(mime)
+var DefaultResponseMimeType string
+
+//PrettyPrintResponses controls the indentation feature of XML and JSON serialization
+var PrettyPrintResponses = true
+
+// Response is a wrapper on the actual http ResponseWriter
+// It provides several convenience methods to prepare and write response content.
+type Response struct {
+ http.ResponseWriter
+ requestAccept string // mime-type what the Http Request says it wants to receive
+ routeProduces []string // mime-types what the Route says it can produce
+ statusCode int // HTTP status code that has been written explicitly (if zero then net/http has written 200)
+ contentLength int // number of bytes written for the response body
+ prettyPrint bool // controls the indentation feature of XML and JSON serialization. It is initialized using var PrettyPrintResponses.
+ err error // err property is kept when WriteError is called
+ hijacker http.Hijacker // if underlying ResponseWriter supports it
+}
+
+// NewResponse creates a new response based on a http ResponseWriter.
+func NewResponse(httpWriter http.ResponseWriter) *Response {
+ hijacker, _ := httpWriter.(http.Hijacker)
+ return &Response{ResponseWriter: httpWriter, routeProduces: []string{}, statusCode: http.StatusOK, prettyPrint: PrettyPrintResponses, hijacker: hijacker}
+}
+
+// DefaultResponseContentType set a default.
+// If Accept header matching fails, fall back to this type.
+// Valid values are restful.MIME_JSON and restful.MIME_XML
+// Example:
+// restful.DefaultResponseContentType(restful.MIME_JSON)
+func DefaultResponseContentType(mime string) {
+ DefaultResponseMimeType = mime
+}
+
+// InternalServerError writes the StatusInternalServerError header.
+// DEPRECATED, use WriteErrorString(http.StatusInternalServerError,reason)
+func (r Response) InternalServerError() Response {
+ r.WriteHeader(http.StatusInternalServerError)
+ return r
+}
+
+// Hijack implements the http.Hijacker interface. This expands
+// the Response to fulfill http.Hijacker if the underlying
+// http.ResponseWriter supports it.
+func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ if r.hijacker == nil {
+ return nil, nil, errors.New("http.Hijacker not implemented by underlying http.ResponseWriter")
+ }
+ return r.hijacker.Hijack()
+}
+
+// PrettyPrint changes whether this response must produce pretty (line-by-line, indented) JSON or XML output.
+func (r *Response) PrettyPrint(bePretty bool) {
+ r.prettyPrint = bePretty
+}
+
+// AddHeader is a shortcut for .Header().Add(header,value)
+func (r Response) AddHeader(header string, value string) Response {
+ r.Header().Add(header, value)
+ return r
+}
+
+// SetRequestAccepts tells the response what Mime-type(s) the HTTP request said it wants to accept. Exposed for testing.
+func (r *Response) SetRequestAccepts(mime string) {
+ r.requestAccept = mime
+}
+
+// EntityWriter returns the registered EntityWriter that the entity (requested resource)
+// can write according to what the request wants (Accept) and what the Route can produce or what the restful defaults say.
+// If called before WriteEntity and WriteHeader then a false return value can be used to write a 406: Not Acceptable.
+func (r *Response) EntityWriter() (EntityReaderWriter, bool) {
+ sorted := sortedMimes(r.requestAccept)
+ for _, eachAccept := range sorted {
+ for _, eachProduce := range r.routeProduces {
+ if eachProduce == eachAccept.media {
+ if w, ok := entityAccessRegistry.accessorAt(eachAccept.media); ok {
+ return w, true
+ }
+ }
+ }
+ if eachAccept.media == "*/*" {
+ for _, each := range r.routeProduces {
+ if w, ok := entityAccessRegistry.accessorAt(each); ok {
+ return w, true
+ }
+ }
+ }
+ }
+ // if requestAccept is empty
+ writer, ok := entityAccessRegistry.accessorAt(r.requestAccept)
+ if !ok {
+ // if not registered then fallback to the defaults (if set)
+ if DefaultResponseMimeType == MIME_JSON {
+ return entityAccessRegistry.accessorAt(MIME_JSON)
+ }
+ if DefaultResponseMimeType == MIME_XML {
+ return entityAccessRegistry.accessorAt(MIME_XML)
+ }
+ if DefaultResponseMimeType == MIME_ZIP {
+ return entityAccessRegistry.accessorAt(MIME_ZIP)
+ }
+ // Fallback to whatever the route says it can produce.
+ // https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
+ for _, each := range r.routeProduces {
+ if w, ok := entityAccessRegistry.accessorAt(each); ok {
+ return w, true
+ }
+ }
+ if trace {
+ traceLogger.Printf("no registered EntityReaderWriter found for %s", r.requestAccept)
+ }
+ }
+ return writer, ok
+}
+
+// WriteEntity calls WriteHeaderAndEntity with Http Status OK (200)
+func (r *Response) WriteEntity(value interface{}) error {
+ return r.WriteHeaderAndEntity(http.StatusOK, value)
+}
+
+// WriteHeaderAndEntity marshals the value using the representation denoted by the Accept Header and the registered EntityWriters.
+// If no Accept header is specified (or */*) then respond with the Content-Type as specified by the first in the Route.Produces.
+// If an Accept header is specified then respond with the Content-Type as specified by the first in the Route.Produces that is matched with the Accept header.
+// If the value is nil then no response is send except for the Http status. You may want to call WriteHeader(http.StatusNotFound) instead.
+// If there is no writer available that can represent the value in the requested MIME type then Http Status NotAcceptable is written.
+// Current implementation ignores any q-parameters in the Accept Header.
+// Returns an error if the value could not be written on the response.
+func (r *Response) WriteHeaderAndEntity(status int, value interface{}) error {
+ writer, ok := r.EntityWriter()
+ if !ok {
+ r.WriteHeader(http.StatusNotAcceptable)
+ return nil
+ }
+ return writer.Write(r, status, value)
+}
+
+// WriteAsXml is a convenience method for writing a value in xml (requires Xml tags on the value)
+// It uses the standard encoding/xml package for marshalling the value ; not using a registered EntityReaderWriter.
+func (r *Response) WriteAsXml(value interface{}) error {
+ return writeXML(r, http.StatusOK, MIME_XML, value)
+}
+
+// WriteHeaderAndXml is a convenience method for writing a status and value in xml (requires Xml tags on the value)
+// It uses the standard encoding/xml package for marshalling the value ; not using a registered EntityReaderWriter.
+func (r *Response) WriteHeaderAndXml(status int, value interface{}) error {
+ return writeXML(r, status, MIME_XML, value)
+}
+
+// WriteAsJson is a convenience method for writing a value in json.
+// It uses the standard encoding/json package for marshalling the value ; not using a registered EntityReaderWriter.
+func (r *Response) WriteAsJson(value interface{}) error {
+ return writeJSON(r, http.StatusOK, MIME_JSON, value)
+}
+
+// WriteJson is a convenience method for writing a value in Json with a given Content-Type.
+// It uses the standard encoding/json package for marshalling the value ; not using a registered EntityReaderWriter.
+func (r *Response) WriteJson(value interface{}, contentType string) error {
+ return writeJSON(r, http.StatusOK, contentType, value)
+}
+
+// WriteHeaderAndJson is a convenience method for writing the status and a value in Json with a given Content-Type.
+// It uses the standard encoding/json package for marshalling the value ; not using a registered EntityReaderWriter.
+func (r *Response) WriteHeaderAndJson(status int, value interface{}, contentType string) error {
+ return writeJSON(r, status, contentType, value)
+}
+
+// WriteError writes the http status and the error string on the response. err can be nil.
+// Return an error if writing was not successful.
+func (r *Response) WriteError(httpStatus int, err error) (writeErr error) {
+ r.err = err
+ if err == nil {
+ writeErr = r.WriteErrorString(httpStatus, "")
+ } else {
+ writeErr = r.WriteErrorString(httpStatus, err.Error())
+ }
+ return writeErr
+}
+
+// WriteServiceError is a convenience method for a responding with a status and a ServiceError
+func (r *Response) WriteServiceError(httpStatus int, err ServiceError) error {
+ r.err = err
+ return r.WriteHeaderAndEntity(httpStatus, err)
+}
+
+// WriteErrorString is a convenience method for an error status with the actual error
+func (r *Response) WriteErrorString(httpStatus int, errorReason string) error {
+ if r.err == nil {
+ // if not called from WriteError
+ r.err = errors.New(errorReason)
+ }
+ r.WriteHeader(httpStatus)
+ if _, err := r.Write([]byte(errorReason)); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Flush implements http.Flusher interface, which sends any buffered data to the client.
+func (r *Response) Flush() {
+ if f, ok := r.ResponseWriter.(http.Flusher); ok {
+ f.Flush()
+ } else if trace {
+ traceLogger.Printf("ResponseWriter %v doesn't support Flush", r)
+ }
+}
+
+// WriteHeader is overridden to remember the Status Code that has been written.
+// Changes to the Header of the response have no effect after this.
+func (r *Response) WriteHeader(httpStatus int) {
+ r.statusCode = httpStatus
+ r.ResponseWriter.WriteHeader(httpStatus)
+}
+
+// StatusCode returns the code that has been written using WriteHeader.
+func (r Response) StatusCode() int {
+ if 0 == r.statusCode {
+ // no status code has been written yet; assume OK
+ return http.StatusOK
+ }
+ return r.statusCode
+}
+
+// Write writes the data to the connection as part of an HTTP reply.
+// Write is part of http.ResponseWriter interface.
+func (r *Response) Write(bytes []byte) (int, error) {
+ written, err := r.ResponseWriter.Write(bytes)
+ r.contentLength += written
+ return written, err
+}
+
+// ContentLength returns the number of bytes written for the response content.
+// Note that this value is only correct if all data is written through the Response using its Write* methods.
+// Data written directly using the underlying http.ResponseWriter is not accounted for.
+func (r Response) ContentLength() int {
+ return r.contentLength
+}
+
+// CloseNotify is part of http.CloseNotifier interface
+func (r Response) CloseNotify() <-chan bool {
+ return r.ResponseWriter.(http.CloseNotifier).CloseNotify()
+}
+
+// Error returns the err created by WriteError
+func (r Response) Error() error {
+ return r.err
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/route.go b/vendor/github.com/emicklei/go-restful/v3/route.go
new file mode 100644
index 000000000..306c44be7
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/route.go
@@ -0,0 +1,191 @@
+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)
+
+// 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
+
+// Route binds a HTTP Method,Path,Consumes combination to a RouteFunction.
+type Route struct {
+ ExtensionProperties
+ Method string
+ Produces []string
+ Consumes []string
+ Path string // webservice root path + described path
+ Function RouteFunction
+ Filters []FilterFunction
+ If []RouteSelectionConditionFunction
+
+ // 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
+ DefaultResponse *ResponseError
+ ReadSample, WriteSample interface{} // structs that model an example request or response payload
+ WriteSamples []interface{} // if more than one return types is possible (oneof) then this will contain multiple values
+
+ // Extra information used to store custom information about the route.
+ Metadata map[string]interface{}
+
+ // marks a route as deprecated
+ Deprecated bool
+
+ //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
+func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request, pathParams map[string]string) (*Request, *Response) {
+ wrappedRequest := NewRequest(httpRequest)
+ wrappedRequest.pathParameters = pathParams
+ wrappedRequest.selectedRoute = r
+ wrappedResponse := NewResponse(httpWriter)
+ wrappedResponse.requestAccept = httpRequest.Header.Get(HEADER_Accept)
+ wrappedResponse.routeProduces = r.Produces
+ return wrappedRequest, wrappedResponse
+}
+
+func stringTrimSpaceCutset(r rune) bool {
+ return r == ' '
+}
+
+// Return whether the mimeType matches to what this Route can produce.
+func (r Route) matchesAccept(mimeTypesWithQuality string) bool {
+ remaining := mimeTypesWithQuality
+ for {
+ var mimeType string
+ if end := strings.Index(remaining, ","); end == -1 {
+ mimeType, remaining = remaining, ""
+ } else {
+ mimeType, remaining = remaining[:end], remaining[end+1:]
+ }
+ if quality := strings.Index(mimeType, ";"); quality != -1 {
+ mimeType = mimeType[:quality]
+ }
+ mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset)
+ if mimeType == "*/*" {
+ return true
+ }
+ for _, producibleType := range r.Produces {
+ if producibleType == "*/*" || producibleType == mimeType {
+ return true
+ }
+ }
+ if len(remaining) == 0 {
+ return false
+ }
+ }
+}
+
+// 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 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
+ }
+
+ remaining := mimeTypes
+ for {
+ var mimeType string
+ if end := strings.Index(remaining, ","); end == -1 {
+ mimeType, remaining = remaining, ""
+ } else {
+ mimeType, remaining = remaining[:end], remaining[end+1:]
+ }
+ if quality := strings.Index(mimeType, ";"); quality != -1 {
+ mimeType = mimeType[:quality]
+ }
+ mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset)
+ for _, consumeableType := range r.Consumes {
+ if consumeableType == "*/*" || consumeableType == mimeType {
+ return true
+ }
+ }
+ if len(remaining) == 0 {
+ return false
+ }
+ }
+}
+
+// Tokenize an URL path using the slash separator ; the result does not have empty tokens
+func tokenizePath(path string) []string {
+ if "/" == path {
+ return nil
+ }
+ if TrimRightSlashEnabled {
+ // 3.9.0
+ return strings.Split(strings.Trim(path, "/"), "/")
+ } else {
+ // 3.10.2
+ return strings.Split(strings.TrimLeft(path, "/"), "/")
+ }
+}
+
+// for debugging
+func (r *Route) String() string {
+ return r.Method + " " + r.Path
+}
+
+// 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
+}
+
+// TrimRightSlashEnabled controls whether
+// - path on route building is using path.Join
+// - the path of the incoming request is trimmed of its slash suffux.
+// Value of true matches the behavior of <= 3.9.0
+var TrimRightSlashEnabled = true
diff --git a/vendor/github.com/emicklei/go-restful/v3/route_builder.go b/vendor/github.com/emicklei/go-restful/v3/route_builder.go
new file mode 100644
index 000000000..75168c12e
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/route_builder.go
@@ -0,0 +1,389 @@
+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 (
+ "fmt"
+ "os"
+ "path"
+ "reflect"
+ "runtime"
+ "strings"
+ "sync/atomic"
+
+ "github.com/emicklei/go-restful/v3/log"
+)
+
+// 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
+ allowedMethodsWithoutContentType []string // see Route
+
+ typeNameHandleFunc TypeNameHandleFunction // required
+
+ // documentation
+ doc string
+ notes string
+ operation string
+ readSample interface{}
+ writeSamples []interface{}
+ parameters []*Parameter
+ errorMap map[int]ResponseError
+ defaultResponse *ResponseError
+ metadata map[string]interface{}
+ extensions map[string]interface{}
+ deprecated bool
+ contentEncodingEnabled *bool
+}
+
+// Do evaluates each argument with the RouteBuilder itself.
+// This allows you to follow DRY principles without breaking the fluent programming style.
+// Example:
+//
+// ws.Route(ws.DELETE("/{name}").To(t.deletePerson).Do(Returns200, Returns500))
+//
+// func Returns500(b *RouteBuilder) {
+// b.Returns(500, "Internal Server Error", restful.ServiceError{})
+// }
+func (b *RouteBuilder) Do(oneArgBlocks ...func(*RouteBuilder)) *RouteBuilder {
+ for _, each := range oneArgBlocks {
+ each(b)
+ }
+ return b
+}
+
+// To bind the route to a function.
+// If this route is matched with the incoming Http Request then call this function with the *Request,*Response pair. Required.
+func (b *RouteBuilder) To(function RouteFunction) *RouteBuilder {
+ b.function = function
+ return b
+}
+
+// Method specifies what HTTP method to match. Required.
+func (b *RouteBuilder) Method(method string) *RouteBuilder {
+ b.httpMethod = method
+ return b
+}
+
+// Produces specifies what MIME types can be produced ; the matched one will appear in the Content-Type Http header.
+func (b *RouteBuilder) Produces(mimeTypes ...string) *RouteBuilder {
+ b.produces = mimeTypes
+ return b
+}
+
+// Consumes specifies what MIME types can be consumes ; the Accept Http header must matched any of these
+func (b *RouteBuilder) Consumes(mimeTypes ...string) *RouteBuilder {
+ b.consumes = mimeTypes
+ return b
+}
+
+// Path specifies the relative (w.r.t WebService root path) URL path to match. Default is "/".
+func (b *RouteBuilder) Path(subPath string) *RouteBuilder {
+ b.currentPath = subPath
+ return b
+}
+
+// Doc tells what this route is all about. Optional.
+func (b *RouteBuilder) Doc(documentation string) *RouteBuilder {
+ b.doc = documentation
+ return b
+}
+
+// Notes is a verbose explanation of the operation behavior. Optional.
+func (b *RouteBuilder) Notes(notes string) *RouteBuilder {
+ b.notes = notes
+ return b
+}
+
+// Reads tells what resource type will be read from the request payload. Optional.
+// A parameter of type "body" is added ,required is set to true and the dataType is set to the qualified name of the sample's type.
+func (b *RouteBuilder) Reads(sample interface{}, optionalDescription ...string) *RouteBuilder {
+ fn := b.typeNameHandleFunc
+ if fn == nil {
+ fn = reflectTypeName
+ }
+ typeAsName := fn(sample)
+ description := ""
+ if len(optionalDescription) > 0 {
+ description = optionalDescription[0]
+ }
+ b.readSample = sample
+ bodyParameter := &Parameter{&ParameterData{Name: "body", Description: description}}
+ bodyParameter.beBody()
+ bodyParameter.Required(true)
+ bodyParameter.DataType(typeAsName)
+ b.Param(bodyParameter)
+ return b
+}
+
+// ParameterNamed returns a Parameter already known to the RouteBuilder. Returns nil if not.
+// Use this to modify or extend information for the Parameter (through its Data()).
+func (b RouteBuilder) ParameterNamed(name string) (p *Parameter) {
+ for _, each := range b.parameters {
+ if each.Data().Name == name {
+ return each
+ }
+ }
+ return p
+}
+
+// Writes tells which one of the resource types will be written as the response payload. Optional.
+func (b *RouteBuilder) Writes(samples ...interface{}) *RouteBuilder {
+ b.writeSamples = samples // oneof
+ return b
+}
+
+// Param allows you to document the parameters of the Route. It adds a new Parameter (does not check for duplicates).
+func (b *RouteBuilder) Param(parameter *Parameter) *RouteBuilder {
+ if b.parameters == nil {
+ b.parameters = []*Parameter{}
+ }
+ b.parameters = append(b.parameters, parameter)
+ return b
+}
+
+// Operation allows you to document what the actual method/function call is of the Route.
+// Unless called, the operation name is derived from the RouteFunction set using To(..).
+func (b *RouteBuilder) Operation(name string) *RouteBuilder {
+ b.operation = name
+ return b
+}
+
+// ReturnsError is deprecated, use Returns instead.
+func (b *RouteBuilder) ReturnsError(code int, message string, model interface{}) *RouteBuilder {
+ log.Print("ReturnsError is deprecated, use Returns instead.")
+ return b.Returns(code, message, model)
+}
+
+// Returns allows you to document what responses (errors or regular) can be expected.
+// The model parameter is optional ; either pass a struct instance or use nil if not applicable.
+func (b *RouteBuilder) Returns(code int, message string, model interface{}) *RouteBuilder {
+ err := ResponseError{
+ Code: code,
+ Message: message,
+ Model: model,
+ IsDefault: false, // this field is deprecated, use default response instead.
+ }
+ // lazy init because there is no NewRouteBuilder (yet)
+ if b.errorMap == nil {
+ b.errorMap = map[int]ResponseError{}
+ }
+ b.errorMap[code] = err
+ 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{
+ Message: message,
+ Model: model,
+ }
+ return b
+}
+
+// Metadata adds or updates a key=value pair to the metadata map.
+func (b *RouteBuilder) Metadata(key string, value interface{}) *RouteBuilder {
+ if b.metadata == nil {
+ b.metadata = map[string]interface{}{}
+ }
+ b.metadata[key] = value
+ return b
+}
+
+// AddExtension adds or updates a key=value pair to the extensions map.
+func (b *RouteBuilder) AddExtension(key string, value interface{}) *RouteBuilder {
+ if b.extensions == nil {
+ b.extensions = map[string]interface{}{}
+ }
+ b.extensions[key] = value
+ return b
+}
+
+// Deprecate sets the value of deprecated to true. Deprecated routes have a special UI treatment to warn against use
+func (b *RouteBuilder) Deprecate() *RouteBuilder {
+ b.deprecated = true
+ return b
+}
+
+// AllowedMethodsWithoutContentType overrides 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 {
+ ExtensionProperties
+ 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
+}
+
+// Filter appends a FilterFunction to the end of filters for this Route to build.
+func (b *RouteBuilder) Filter(filter FilterFunction) *RouteBuilder {
+ b.filters = append(b.filters, filter)
+ return b
+}
+
+// If sets a condition function that controls matching the Route based on custom logic.
+// The condition function is provided the HTTP request and should return true if the route
+// should be considered.
+//
+// Efficiency note: the condition function is called before checking the method, produces, and
+// consumes criteria, so that the correct HTTP status code can be returned.
+//
+// Lifecycle note: no filter functions have been called prior to calling the condition function,
+// so the condition function should not depend on any context that might be set up by container
+// or route filters.
+func (b *RouteBuilder) If(condition RouteSelectionConditionFunction) *RouteBuilder {
+ b.conditions = append(b.conditions, condition)
+ return b
+}
+
+// ContentEncodingEnabled allows you to override the Containers value for auto-compressing this route response.
+func (b *RouteBuilder) ContentEncodingEnabled(enabled bool) *RouteBuilder {
+ b.contentEncodingEnabled = &enabled
+ return b
+}
+
+// If no specific Route path then set to rootPath
+// If no specific Produces then set to rootProduces
+// If no specific Consumes then set to rootConsumes
+func (b *RouteBuilder) copyDefaults(rootProduces, rootConsumes []string) {
+ if len(b.produces) == 0 {
+ b.produces = rootProduces
+ }
+ if len(b.consumes) == 0 {
+ b.consumes = rootConsumes
+ }
+}
+
+// typeNameHandler sets the function that will convert types to strings in the parameter
+// and model definitions.
+func (b *RouteBuilder) typeNameHandler(handler TypeNameHandleFunction) *RouteBuilder {
+ b.typeNameHandleFunc = handler
+ return b
+}
+
+// Build creates a new Route using the specification details collected by the RouteBuilder
+func (b *RouteBuilder) Build() Route {
+ pathExpr, err := newPathExpression(b.currentPath)
+ if err != nil {
+ log.Printf("Invalid path:%s because:%v", b.currentPath, err)
+ os.Exit(1)
+ }
+ if b.function == nil {
+ log.Printf("No function specified for route:" + b.currentPath)
+ os.Exit(1)
+ }
+ operationName := b.operation
+ if len(operationName) == 0 && b.function != nil {
+ // extract from definition
+ 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,
+ WriteSamples: b.writeSamples,
+ Metadata: b.metadata,
+ Deprecated: b.deprecated,
+ contentEncodingEnabled: b.contentEncodingEnabled,
+ allowedMethodsWithoutContentType: b.allowedMethodsWithoutContentType,
+ }
+ // set WriteSample if one specified
+ if len(b.writeSamples) == 1 {
+ route.WriteSample = b.writeSamples[0]
+ }
+ route.Extensions = b.extensions
+ route.postBuild()
+ return route
+}
+
+// merge two paths using the current (package global) merge path strategy.
+func concatPath(rootPath, routePath string) string {
+
+ if TrimRightSlashEnabled {
+ return strings.TrimRight(rootPath, "/") + "/" + strings.TrimLeft(routePath, "/")
+ } else {
+ return path.Join(rootPath, routePath)
+ }
+}
+
+var anonymousFuncCount int32
+
+// nameOfFunction returns the short name of the function f for documentation.
+// It uses a runtime feature for debugging ; its value may change for later Go versions.
+func nameOfFunction(f interface{}) string {
+ fun := runtime.FuncForPC(reflect.ValueOf(f).Pointer())
+ tokenized := strings.Split(fun.Name(), ".")
+ last := tokenized[len(tokenized)-1]
+ last = strings.TrimSuffix(last, ")·fm") // < Go 1.5
+ last = strings.TrimSuffix(last, ")-fm") // Go 1.5
+ last = strings.TrimSuffix(last, "·fm") // < Go 1.5
+ last = strings.TrimSuffix(last, "-fm") // Go 1.5
+ if last == "func1" { // this could mean conflicts in API docs
+ val := atomic.AddInt32(&anonymousFuncCount, 1)
+ last = "func" + fmt.Sprintf("%d", val)
+ atomic.StoreInt32(&anonymousFuncCount, val)
+ }
+ return last
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/route_reader.go b/vendor/github.com/emicklei/go-restful/v3/route_reader.go
new file mode 100644
index 000000000..c9f4ee75f
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/route_reader.go
@@ -0,0 +1,66 @@
+package restful
+
+// Copyright 2021 Ernest Micklei. All rights reserved.
+// Use of this source code is governed by a license
+// that can be found in the LICENSE file.
+
+type RouteReader interface {
+ Method() string
+ Consumes() []string
+ Path() string
+ Doc() string
+ Notes() string
+ Operation() string
+ ParameterDocs() []*Parameter
+ // Returns a copy
+ Metadata() map[string]interface{}
+ Deprecated() bool
+}
+
+type routeAccessor struct {
+ route *Route
+}
+
+func (r routeAccessor) Method() string {
+ return r.route.Method
+}
+func (r routeAccessor) Consumes() []string {
+ return r.route.Consumes[:]
+}
+func (r routeAccessor) Path() string {
+ return r.route.Path
+}
+func (r routeAccessor) Doc() string {
+ return r.route.Doc
+}
+func (r routeAccessor) Notes() string {
+ return r.route.Notes
+}
+func (r routeAccessor) Operation() string {
+ return r.route.Operation
+}
+func (r routeAccessor) ParameterDocs() []*Parameter {
+ return r.route.ParameterDocs[:]
+}
+
+// Returns a copy
+func (r routeAccessor) Metadata() map[string]interface{} {
+ return copyMap(r.route.Metadata)
+}
+func (r routeAccessor) Deprecated() bool {
+ return r.route.Deprecated
+}
+
+// https://stackoverflow.com/questions/23057785/how-to-copy-a-map
+func copyMap(m map[string]interface{}) map[string]interface{} {
+ cp := make(map[string]interface{})
+ for k, v := range m {
+ vm, ok := v.(map[string]interface{})
+ if ok {
+ cp[k] = copyMap(vm)
+ } else {
+ cp[k] = v
+ }
+ }
+ return cp
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/router.go b/vendor/github.com/emicklei/go-restful/v3/router.go
new file mode 100644
index 000000000..19078af1c
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/router.go
@@ -0,0 +1,20 @@
+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"
+
+// A RouteSelector finds the best matching Route given the input HTTP Request
+// RouteSelectors can optionally also implement the PathProcessor interface to also calculate the
+// path parameters after the route has been selected.
+type RouteSelector interface {
+
+ // SelectRoute finds a Route given the input HTTP Request and a list of WebServices.
+ // It returns a selected Route and its containing WebService or an error indicating
+ // a problem.
+ SelectRoute(
+ webServices []*WebService,
+ httpRequest *http.Request) (selectedService *WebService, selected *Route, err error)
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/service_error.go b/vendor/github.com/emicklei/go-restful/v3/service_error.go
new file mode 100644
index 000000000..a41575469
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/service_error.go
@@ -0,0 +1,32 @@
+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 (
+ "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
+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)
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/web_service.go b/vendor/github.com/emicklei/go-restful/v3/web_service.go
new file mode 100644
index 000000000..789c4df25
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/web_service.go
@@ -0,0 +1,305 @@
+package restful
+
+import (
+ "errors"
+ "os"
+ "reflect"
+ "sync"
+
+ "github.com/emicklei/go-restful/v3/log"
+)
+
+// 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.
+
+// WebService holds a collection of Route values that bind a Http Method + URL Path to a function.
+type WebService struct {
+ rootPath string
+ pathExpr *pathExpression // cached compilation of rootPath as RegExp
+ routes []Route
+ produces []string
+ consumes []string
+ pathParameters []*Parameter
+ filters []FilterFunction
+ documentation string
+ apiVersion string
+
+ typeNameHandleFunc TypeNameHandleFunction
+
+ dynamicRoutes bool
+
+ // protects 'routes' if dynamic routes are enabled
+ routesLock sync.RWMutex
+}
+
+func (w *WebService) SetDynamicRoutes(enable bool) {
+ w.dynamicRoutes = enable
+}
+
+// TypeNameHandleFunction declares functions that can handle translating the name of a sample object
+// into the restful documentation for the service.
+type TypeNameHandleFunction func(sample interface{}) string
+
+// TypeNameHandler sets the function that will convert types to strings in the parameter
+// and model definitions. If not set, the web service will invoke
+// reflect.TypeOf(object).String().
+func (w *WebService) TypeNameHandler(handler TypeNameHandleFunction) *WebService {
+ w.typeNameHandleFunc = handler
+ return w
+}
+
+// reflectTypeName is the default TypeNameHandleFunction and for a given object
+// returns the name that Go identifies it with (e.g. "string" or "v1.Object") via
+// the reflection API.
+func reflectTypeName(sample interface{}) string {
+ return reflect.TypeOf(sample).String()
+}
+
+// compilePathExpression ensures that the path is compiled into a RegEx for those routers that need it.
+func (w *WebService) compilePathExpression() {
+ compiled, err := newPathExpression(w.rootPath)
+ if err != nil {
+ log.Printf("invalid path:%s because:%v", w.rootPath, err)
+ os.Exit(1)
+ }
+ w.pathExpr = compiled
+}
+
+// ApiVersion sets the API version for documentation purposes.
+func (w *WebService) ApiVersion(apiVersion string) *WebService {
+ w.apiVersion = apiVersion
+ return w
+}
+
+// Version returns the API version for documentation purposes.
+func (w *WebService) Version() string { return w.apiVersion }
+
+// Path specifies the root URL template path of the WebService.
+// All Routes will be relative to this path.
+func (w *WebService) Path(root string) *WebService {
+ w.rootPath = root
+ if len(w.rootPath) == 0 {
+ w.rootPath = "/"
+ }
+ w.compilePathExpression()
+ return w
+}
+
+// Param adds a PathParameter to document parameters used in the root path.
+func (w *WebService) Param(parameter *Parameter) *WebService {
+ if w.pathParameters == nil {
+ w.pathParameters = []*Parameter{}
+ }
+ w.pathParameters = append(w.pathParameters, parameter)
+ return w
+}
+
+// PathParameter creates a new Parameter of kind Path for documentation purposes.
+// It is initialized as required with string as its DataType.
+func (w *WebService) PathParameter(name, description string) *Parameter {
+ return PathParameter(name, description)
+}
+
+// PathParameter creates a new Parameter of kind Path for documentation purposes.
+// It is initialized as required with string as its DataType.
+func PathParameter(name, description string) *Parameter {
+ p := &Parameter{&ParameterData{Name: name, Description: description, Required: true, DataType: "string"}}
+ p.bePath()
+ return p
+}
+
+// QueryParameter creates a new Parameter of kind Query for documentation purposes.
+// It is initialized as not required with string as its DataType.
+func (w *WebService) QueryParameter(name, description string) *Parameter {
+ return QueryParameter(name, description)
+}
+
+// QueryParameter creates a new Parameter of kind Query for documentation purposes.
+// It is initialized as not required with string as its DataType.
+func QueryParameter(name, description string) *Parameter {
+ p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string", CollectionFormat: CollectionFormatCSV.String()}}
+ p.beQuery()
+ return p
+}
+
+// BodyParameter creates a new Parameter of kind Body for documentation purposes.
+// It is initialized as required without a DataType.
+func (w *WebService) BodyParameter(name, description string) *Parameter {
+ return BodyParameter(name, description)
+}
+
+// BodyParameter creates a new Parameter of kind Body for documentation purposes.
+// It is initialized as required without a DataType.
+func BodyParameter(name, description string) *Parameter {
+ p := &Parameter{&ParameterData{Name: name, Description: description, Required: true}}
+ p.beBody()
+ return p
+}
+
+// HeaderParameter creates a new Parameter of kind (Http) Header for documentation purposes.
+// It is initialized as not required with string as its DataType.
+func (w *WebService) HeaderParameter(name, description string) *Parameter {
+ return HeaderParameter(name, description)
+}
+
+// HeaderParameter creates a new Parameter of kind (Http) Header for documentation purposes.
+// It is initialized as not required with string as its DataType.
+func HeaderParameter(name, description string) *Parameter {
+ p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}}
+ p.beHeader()
+ return p
+}
+
+// FormParameter creates a new Parameter of kind Form (using application/x-www-form-urlencoded) for documentation purposes.
+// It is initialized as required with string as its DataType.
+func (w *WebService) FormParameter(name, description string) *Parameter {
+ return FormParameter(name, description)
+}
+
+// FormParameter creates a new Parameter of kind Form (using application/x-www-form-urlencoded) for documentation purposes.
+// It is initialized as required with string as its DataType.
+func FormParameter(name, description string) *Parameter {
+ p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}}
+ p.beForm()
+ return p
+}
+
+// MultiPartFormParameter creates a new Parameter of kind Form (using multipart/form-data) for documentation purposes.
+// It is initialized as required with string as its DataType.
+func (w *WebService) MultiPartFormParameter(name, description string) *Parameter {
+ return MultiPartFormParameter(name, description)
+}
+
+func MultiPartFormParameter(name, description string) *Parameter {
+ p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}}
+ p.beMultiPartForm()
+ return p
+}
+
+// Route creates a new Route using the RouteBuilder and add to the ordered list of Routes.
+func (w *WebService) Route(builder *RouteBuilder) *WebService {
+ w.routesLock.Lock()
+ defer w.routesLock.Unlock()
+ builder.copyDefaults(w.produces, w.consumes)
+ w.routes = append(w.routes, builder.Build())
+ return w
+}
+
+// RemoveRoute removes the specified route, looks for something that matches 'path' and 'method'
+func (w *WebService) RemoveRoute(path, method string) error {
+ if !w.dynamicRoutes {
+ return errors.New("dynamic routes are not enabled.")
+ }
+ w.routesLock.Lock()
+ defer w.routesLock.Unlock()
+ newRoutes := []Route{}
+ for _, route := range w.routes {
+ if route.Method == method && route.Path == path {
+ continue
+ }
+ newRoutes = append(newRoutes, route)
+ }
+ w.routes = newRoutes
+ return nil
+}
+
+// Method creates a new RouteBuilder and initialize its http method
+func (w *WebService) Method(httpMethod string) *RouteBuilder {
+ return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method(httpMethod)
+}
+
+// Produces specifies that this WebService can produce one or more MIME types.
+// Http requests must have one of these values set for the Accept header.
+func (w *WebService) Produces(contentTypes ...string) *WebService {
+ w.produces = contentTypes
+ return w
+}
+
+// Consumes specifies that this WebService can consume one or more MIME types.
+// Http requests must have one of these values set for the Content-Type header.
+func (w *WebService) Consumes(accepts ...string) *WebService {
+ w.consumes = accepts
+ return w
+}
+
+// Routes returns the Routes associated with this WebService
+func (w *WebService) Routes() []Route {
+ if !w.dynamicRoutes {
+ return w.routes
+ }
+ // Make a copy of the array to prevent concurrency problems
+ w.routesLock.RLock()
+ defer w.routesLock.RUnlock()
+ result := make([]Route, len(w.routes))
+ for ix := range w.routes {
+ result[ix] = w.routes[ix]
+ }
+ return result
+}
+
+// RootPath returns the RootPath associated with this WebService. Default "/"
+func (w *WebService) RootPath() string {
+ return w.rootPath
+}
+
+// PathParameters return the path parameter names for (shared among its Routes)
+func (w *WebService) PathParameters() []*Parameter {
+ return w.pathParameters
+}
+
+// Filter adds a filter function to the chain of filters applicable to all its Routes
+func (w *WebService) Filter(filter FilterFunction) *WebService {
+ w.filters = append(w.filters, filter)
+ return w
+}
+
+// Doc is used to set the documentation of this service.
+func (w *WebService) Doc(plainText string) *WebService {
+ w.documentation = plainText
+ return w
+}
+
+// Documentation returns it.
+func (w *WebService) Documentation() string {
+ return w.documentation
+}
+
+/*
+ Convenience methods
+*/
+
+// HEAD is a shortcut for .Method("HEAD").Path(subPath)
+func (w *WebService) HEAD(subPath string) *RouteBuilder {
+ return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("HEAD").Path(subPath)
+}
+
+// GET is a shortcut for .Method("GET").Path(subPath)
+func (w *WebService) GET(subPath string) *RouteBuilder {
+ return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("GET").Path(subPath)
+}
+
+// POST is a shortcut for .Method("POST").Path(subPath)
+func (w *WebService) POST(subPath string) *RouteBuilder {
+ return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("POST").Path(subPath)
+}
+
+// PUT is a shortcut for .Method("PUT").Path(subPath)
+func (w *WebService) PUT(subPath string) *RouteBuilder {
+ return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("PUT").Path(subPath)
+}
+
+// PATCH is a shortcut for .Method("PATCH").Path(subPath)
+func (w *WebService) PATCH(subPath string) *RouteBuilder {
+ return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("PATCH").Path(subPath)
+}
+
+// DELETE is a shortcut for .Method("DELETE").Path(subPath)
+func (w *WebService) DELETE(subPath string) *RouteBuilder {
+ return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("DELETE").Path(subPath)
+}
+
+// OPTIONS is a shortcut for .Method("OPTIONS").Path(subPath)
+func (w *WebService) OPTIONS(subPath string) *RouteBuilder {
+ return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("OPTIONS").Path(subPath)
+}
diff --git a/vendor/github.com/emicklei/go-restful/v3/web_service_container.go b/vendor/github.com/emicklei/go-restful/v3/web_service_container.go
new file mode 100644
index 000000000..c9d31b06c
--- /dev/null
+++ b/vendor/github.com/emicklei/go-restful/v3/web_service_container.go
@@ -0,0 +1,39 @@
+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"
+)
+
+// DefaultContainer is a restful.Container that uses http.DefaultServeMux
+var DefaultContainer *Container
+
+func init() {
+ DefaultContainer = NewContainer()
+ DefaultContainer.ServeMux = http.DefaultServeMux
+}
+
+// If set the true then panics will not be caught to return HTTP 500.
+// In that case, Route functions are responsible for handling any error situation.
+// Default value is false = recover from panics. This has performance implications.
+// OBSOLETE ; use restful.DefaultContainer.DoNotRecover(true)
+var DoNotRecover = false
+
+// Add registers a new WebService add it to the DefaultContainer.
+func Add(service *WebService) {
+ DefaultContainer.Add(service)
+}
+
+// Filter appends a container FilterFunction from the DefaultContainer.
+// These are called before dispatching a http.Request to a WebService.
+func Filter(filter FilterFunction) {
+ DefaultContainer.Filter(filter)
+}
+
+// RegisteredWebServices returns the collections of WebServices from the DefaultContainer
+func RegisteredWebServices() []*WebService {
+ return DefaultContainer.RegisteredWebServices()
+}