diff options
Diffstat (limited to 'vendor/github.com/go-openapi/runtime')
52 files changed, 6030 insertions, 0 deletions
diff --git a/vendor/github.com/go-openapi/runtime/.editorconfig b/vendor/github.com/go-openapi/runtime/.editorconfig new file mode 100644 index 000000000..3152da69a --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/runtime/.gitattributes b/vendor/github.com/go-openapi/runtime/.gitattributes new file mode 100644 index 000000000..d207b1802 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.gitattributes @@ -0,0 +1 @@ +*.go text eol=lf diff --git a/vendor/github.com/go-openapi/runtime/.gitignore b/vendor/github.com/go-openapi/runtime/.gitignore new file mode 100644 index 000000000..fea8b84ec --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.gitignore @@ -0,0 +1,5 @@ +secrets.yml +coverage.out +*.cov +*.out +playground diff --git a/vendor/github.com/go-openapi/runtime/.golangci.yml b/vendor/github.com/go-openapi/runtime/.golangci.yml new file mode 100644 index 000000000..b1aa7928a --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.golangci.yml @@ -0,0 +1,44 @@ +linters-settings: +  govet: +    # Using err repeatedly considered as shadowing. +    check-shadowing: false +  golint: +    min-confidence: 0 +  gocyclo: +    min-complexity: 30 +  maligned: +    suggest-new: true +  dupl: +    threshold: 100 +  goconst: +    min-len: 2 +    min-occurrences: 4 +linters: +  disable: +    - maligned +    - lll +    - gochecknoglobals +    - godox +    - gocognit +    - whitespace +    - wsl +    - funlen +    - gochecknoglobals +    - gochecknoinits +    - scopelint +    - wrapcheck +    - exhaustivestruct +    - exhaustive +    - nlreturn +    - testpackage +    - gci +    - gofumpt +    - goerr113 +    - gomnd +    - tparallel +    - nestif +    - godot +    - errorlint +    - noctx +    - interfacer +    - nilerr diff --git a/vendor/github.com/go-openapi/runtime/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/runtime/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9322b065e --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic +  address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a +  professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/runtime/LICENSE b/vendor/github.com/go-openapi/runtime/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/LICENSE @@ -0,0 +1,202 @@ + +                                 Apache License +                           Version 2.0, January 2004 +                        http://www.apache.org/licenses/ + +   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +   1. Definitions. + +      "License" shall mean the terms and conditions for use, reproduction, +      and distribution as defined by Sections 1 through 9 of this document. + +      "Licensor" shall mean the copyright owner or entity authorized by +      the copyright owner that is granting the License. + +      "Legal Entity" shall mean the union of the acting entity and all +      other entities that control, are controlled by, or are under common +      control with that entity. For the purposes of this definition, +      "control" means (i) the power, direct or indirect, to cause the +      direction or management of such entity, whether by contract or +      otherwise, or (ii) ownership of fifty percent (50%) or more of the +      outstanding shares, or (iii) beneficial ownership of such entity. + +      "You" (or "Your") shall mean an individual or Legal Entity +      exercising permissions granted by this License. + +      "Source" form shall mean the preferred form for making modifications, +      including but not limited to software source code, documentation +      source, and configuration files. + +      "Object" form shall mean any form resulting from mechanical +      transformation or translation of a Source form, including but +      not limited to compiled object code, generated documentation, +      and conversions to other media types. + +      "Work" shall mean the work of authorship, whether in Source or +      Object form, made available under the License, as indicated by a +      copyright notice that is included in or attached to the work +      (an example is provided in the Appendix below). + +      "Derivative Works" shall mean any work, whether in Source or Object +      form, that is based on (or derived from) the Work and for which the +      editorial revisions, annotations, elaborations, or other modifications +      represent, as a whole, an original work of authorship. For the purposes +      of this License, Derivative Works shall not include works that remain +      separable from, or merely link (or bind by name) to the interfaces of, +      the Work and Derivative Works thereof. + +      "Contribution" shall mean any work of authorship, including +      the original version of the Work and any modifications or additions +      to that Work or Derivative Works thereof, that is intentionally +      submitted to Licensor for inclusion in the Work by the copyright owner +      or by an individual or Legal Entity authorized to submit on behalf of +      the copyright owner. For the purposes of this definition, "submitted" +      means any form of electronic, verbal, or written communication sent +      to the Licensor or its representatives, including but not limited to +      communication on electronic mailing lists, source code control systems, +      and issue tracking systems that are managed by, or on behalf of, the +      Licensor for the purpose of discussing and improving the Work, but +      excluding communication that is conspicuously marked or otherwise +      designated in writing by the copyright owner as "Not a Contribution." + +      "Contributor" shall mean Licensor and any individual or Legal Entity +      on behalf of whom a Contribution has been received by Licensor and +      subsequently incorporated within the Work. + +   2. Grant of Copyright License. Subject to the terms and conditions of +      this License, each Contributor hereby grants to You a perpetual, +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable +      copyright license to reproduce, prepare Derivative Works of, +      publicly display, publicly perform, sublicense, and distribute the +      Work and such Derivative Works in Source or Object form. + +   3. Grant of Patent License. Subject to the terms and conditions of +      this License, each Contributor hereby grants to You a perpetual, +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable +      (except as stated in this section) patent license to make, have made, +      use, offer to sell, sell, import, and otherwise transfer the Work, +      where such license applies only to those patent claims licensable +      by such Contributor that are necessarily infringed by their +      Contribution(s) alone or by combination of their Contribution(s) +      with the Work to which such Contribution(s) was submitted. If You +      institute patent litigation against any entity (including a +      cross-claim or counterclaim in a lawsuit) alleging that the Work +      or a Contribution incorporated within the Work constitutes direct +      or contributory patent infringement, then any patent licenses +      granted to You under this License for that Work shall terminate +      as of the date such litigation is filed. + +   4. Redistribution. You may reproduce and distribute copies of the +      Work or Derivative Works thereof in any medium, with or without +      modifications, and in Source or Object form, provided that You +      meet the following conditions: + +      (a) You must give any other recipients of the Work or +          Derivative Works a copy of this License; and + +      (b) You must cause any modified files to carry prominent notices +          stating that You changed the files; and + +      (c) You must retain, in the Source form of any Derivative Works +          that You distribute, all copyright, patent, trademark, and +          attribution notices from the Source form of the Work, +          excluding those notices that do not pertain to any part of +          the Derivative Works; and + +      (d) If the Work includes a "NOTICE" text file as part of its +          distribution, then any Derivative Works that You distribute must +          include a readable copy of the attribution notices contained +          within such NOTICE file, excluding those notices that do not +          pertain to any part of the Derivative Works, in at least one +          of the following places: within a NOTICE text file distributed +          as part of the Derivative Works; within the Source form or +          documentation, if provided along with the Derivative Works; or, +          within a display generated by the Derivative Works, if and +          wherever such third-party notices normally appear. The contents +          of the NOTICE file are for informational purposes only and +          do not modify the License. You may add Your own attribution +          notices within Derivative Works that You distribute, alongside +          or as an addendum to the NOTICE text from the Work, provided +          that such additional attribution notices cannot be construed +          as modifying the License. + +      You may add Your own copyright statement to Your modifications and +      may provide additional or different license terms and conditions +      for use, reproduction, or distribution of Your modifications, or +      for any such Derivative Works as a whole, provided Your use, +      reproduction, and distribution of the Work otherwise complies with +      the conditions stated in this License. + +   5. Submission of Contributions. Unless You explicitly state otherwise, +      any Contribution intentionally submitted for inclusion in the Work +      by You to the Licensor shall be under the terms and conditions of +      this License, without any additional terms or conditions. +      Notwithstanding the above, nothing herein shall supersede or modify +      the terms of any separate license agreement you may have executed +      with Licensor regarding such Contributions. + +   6. Trademarks. This License does not grant permission to use the trade +      names, trademarks, service marks, or product names of the Licensor, +      except as required for reasonable and customary use in describing the +      origin of the Work and reproducing the content of the NOTICE file. + +   7. Disclaimer of Warranty. Unless required by applicable law or +      agreed to in writing, Licensor provides the Work (and each +      Contributor provides its Contributions) on an "AS IS" BASIS, +      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +      implied, including, without limitation, any warranties or conditions +      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +      PARTICULAR PURPOSE. You are solely responsible for determining the +      appropriateness of using or redistributing the Work and assume any +      risks associated with Your exercise of permissions under this License. + +   8. Limitation of Liability. In no event and under no legal theory, +      whether in tort (including negligence), contract, or otherwise, +      unless required by applicable law (such as deliberate and grossly +      negligent acts) or agreed to in writing, shall any Contributor be +      liable to You for damages, including any direct, indirect, special, +      incidental, or consequential damages of any character arising as a +      result of this License or out of the use or inability to use the +      Work (including but not limited to damages for loss of goodwill, +      work stoppage, computer failure or malfunction, or any and all +      other commercial damages or losses), even if such Contributor +      has been advised of the possibility of such damages. + +   9. Accepting Warranty or Additional Liability. While redistributing +      the Work or Derivative Works thereof, You may choose to offer, +      and charge a fee for, acceptance of support, warranty, indemnity, +      or other liability obligations and/or rights consistent with this +      License. However, in accepting such obligations, You may act only +      on Your own behalf and on Your sole responsibility, not on behalf +      of any other Contributor, and only if You agree to indemnify, +      defend, and hold each Contributor harmless for any liability +      incurred by, or claims asserted against, such Contributor by reason +      of your accepting any such warranty or additional liability. + +   END OF TERMS AND CONDITIONS + +   APPENDIX: How to apply the Apache License to your work. + +      To apply the Apache License to your work, attach the following +      boilerplate notice, with the fields enclosed by brackets "[]" +      replaced with your own identifying information. (Don't include +      the brackets!)  The text should be enclosed in the appropriate +      comment syntax for the file format. We also recommend that a +      file or class name and description of purpose be included on the +      same "printed page" as the copyright notice for easier +      identification within third-party archives. + +   Copyright [yyyy] [name of copyright owner] + +   Licensed under the Apache License, Version 2.0 (the "License"); +   you may not use this file except in compliance with the License. +   You may obtain a copy of the License at + +       http://www.apache.org/licenses/LICENSE-2.0 + +   Unless required by applicable law or agreed to in writing, software +   distributed under the License is distributed on an "AS IS" BASIS, +   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +   See the License for the specific language governing permissions and +   limitations under the License. diff --git a/vendor/github.com/go-openapi/runtime/README.md b/vendor/github.com/go-openapi/runtime/README.md new file mode 100644 index 000000000..5b1ec6494 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/README.md @@ -0,0 +1,7 @@ +# runtime [](https://travis-ci.org/go-openapi/runtime) [](https://codecov.io/gh/go-openapi/runtime) [](https://slackin.goswagger.io) + +[](https://raw.githubusercontent.com/go-openapi/runtime/master/LICENSE) [](http://godoc.org/github.com/go-openapi/runtime) + +# golang Open-API toolkit - runtime + +The runtime component for use in codegeneration or as untyped usage. diff --git a/vendor/github.com/go-openapi/runtime/bytestream.go b/vendor/github.com/go-openapi/runtime/bytestream.go new file mode 100644 index 000000000..6eb6ceb5c --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/bytestream.go @@ -0,0 +1,169 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( +	"bytes" +	"encoding" +	"errors" +	"fmt" +	"io" +	"reflect" + +	"github.com/go-openapi/swag" +) + +func defaultCloser() error { return nil } + +type byteStreamOpt func(opts *byteStreamOpts) + +// ClosesStream when the bytestream consumer or producer is finished +func ClosesStream(opts *byteStreamOpts) { +	opts.Close = true +} + +type byteStreamOpts struct { +	Close bool +} + +// ByteStreamConsumer creates a consumer for byte streams, +// takes a Writer/BinaryUnmarshaler interface or binary slice by reference, +// and reads from the provided reader +func ByteStreamConsumer(opts ...byteStreamOpt) Consumer { +	var vals byteStreamOpts +	for _, opt := range opts { +		opt(&vals) +	} + +	return ConsumerFunc(func(reader io.Reader, data interface{}) error { +		if reader == nil { +			return errors.New("ByteStreamConsumer requires a reader") // early exit +		} + +		close := defaultCloser +		if vals.Close { +			if cl, ok := reader.(io.Closer); ok { +				close = cl.Close +			} +		} +		//nolint:errcheck // closing a reader wouldn't fail. +		defer close() + +		if wrtr, ok := data.(io.Writer); ok { +			_, err := io.Copy(wrtr, reader) +			return err +		} + +		buf := new(bytes.Buffer) +		_, err := buf.ReadFrom(reader) +		if err != nil { +			return err +		} +		b := buf.Bytes() + +		if bu, ok := data.(encoding.BinaryUnmarshaler); ok { +			return bu.UnmarshalBinary(b) +		} + +		if data != nil { +			if str, ok := data.(*string); ok { +				*str = string(b) +				return nil +			} +		} + +		if t := reflect.TypeOf(data); data != nil && t.Kind() == reflect.Ptr { +			v := reflect.Indirect(reflect.ValueOf(data)) +			if t = v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { +				v.SetBytes(b) +				return nil +			} +		} + +		return fmt.Errorf("%v (%T) is not supported by the ByteStreamConsumer, %s", +			data, data, "can be resolved by supporting Writer/BinaryUnmarshaler interface") +	}) +} + +// ByteStreamProducer creates a producer for byte streams, +// takes a Reader/BinaryMarshaler interface or binary slice, +// and writes to a writer (essentially a pipe) +func ByteStreamProducer(opts ...byteStreamOpt) Producer { +	var vals byteStreamOpts +	for _, opt := range opts { +		opt(&vals) +	} +	return ProducerFunc(func(writer io.Writer, data interface{}) error { +		if writer == nil { +			return errors.New("ByteStreamProducer requires a writer") // early exit +		} +		close := defaultCloser +		if vals.Close { +			if cl, ok := writer.(io.Closer); ok { +				close = cl.Close +			} +		} +		//nolint:errcheck // TODO: closing a writer would fail. +		defer close() + +		if rc, ok := data.(io.ReadCloser); ok { +			defer rc.Close() +		} + +		if rdr, ok := data.(io.Reader); ok { +			_, err := io.Copy(writer, rdr) +			return err +		} + +		if bm, ok := data.(encoding.BinaryMarshaler); ok { +			bytes, err := bm.MarshalBinary() +			if err != nil { +				return err +			} + +			_, err = writer.Write(bytes) +			return err +		} + +		if data != nil { +			if str, ok := data.(string); ok { +				_, err := writer.Write([]byte(str)) +				return err +			} + +			if e, ok := data.(error); ok { +				_, err := writer.Write([]byte(e.Error())) +				return err +			} + +			v := reflect.Indirect(reflect.ValueOf(data)) +			if t := v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { +				_, err := writer.Write(v.Bytes()) +				return err +			} +			if t := v.Type(); t.Kind() == reflect.Struct || t.Kind() == reflect.Slice { +				b, err := swag.WriteJSON(data) +				if err != nil { +					return err +				} +				_, err = writer.Write(b) +				return err +			} +		} + +		return fmt.Errorf("%v (%T) is not supported by the ByteStreamProducer, %s", +			data, data, "can be resolved by supporting Reader/BinaryMarshaler interface") +	}) +} diff --git a/vendor/github.com/go-openapi/runtime/client_auth_info.go b/vendor/github.com/go-openapi/runtime/client_auth_info.go new file mode 100644 index 000000000..c6c97d9a7 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_auth_info.go @@ -0,0 +1,30 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import "github.com/go-openapi/strfmt" + +// A ClientAuthInfoWriterFunc converts a function to a request writer interface +type ClientAuthInfoWriterFunc func(ClientRequest, strfmt.Registry) error + +// AuthenticateRequest adds authentication data to the request +func (fn ClientAuthInfoWriterFunc) AuthenticateRequest(req ClientRequest, reg strfmt.Registry) error { +	return fn(req, reg) +} + +// A ClientAuthInfoWriter implementor knows how to write authentication info to a request +type ClientAuthInfoWriter interface { +	AuthenticateRequest(ClientRequest, strfmt.Registry) error +} diff --git a/vendor/github.com/go-openapi/runtime/client_operation.go b/vendor/github.com/go-openapi/runtime/client_operation.go new file mode 100644 index 000000000..fa21eacf3 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_operation.go @@ -0,0 +1,41 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( +	"context" +	"net/http" +) + +// ClientOperation represents the context for a swagger operation to be submitted to the transport +type ClientOperation struct { +	ID                 string +	Method             string +	PathPattern        string +	ProducesMediaTypes []string +	ConsumesMediaTypes []string +	Schemes            []string +	AuthInfo           ClientAuthInfoWriter +	Params             ClientRequestWriter +	Reader             ClientResponseReader +	Context            context.Context +	Client             *http.Client +} + +// A ClientTransport implementor knows how to submit Request objects to some destination +type ClientTransport interface { +	//Submit(string, RequestWriter, ResponseReader, AuthInfoWriter) (interface{}, error) +	Submit(*ClientOperation) (interface{}, error) +} diff --git a/vendor/github.com/go-openapi/runtime/client_request.go b/vendor/github.com/go-openapi/runtime/client_request.go new file mode 100644 index 000000000..d4d2b58f2 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_request.go @@ -0,0 +1,152 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( +	"io" +	"net/http" +	"net/url" +	"time" + +	"github.com/go-openapi/strfmt" +) + +// ClientRequestWriterFunc converts a function to a request writer interface +type ClientRequestWriterFunc func(ClientRequest, strfmt.Registry) error + +// WriteToRequest adds data to the request +func (fn ClientRequestWriterFunc) WriteToRequest(req ClientRequest, reg strfmt.Registry) error { +	return fn(req, reg) +} + +// ClientRequestWriter is an interface for things that know how to write to a request +type ClientRequestWriter interface { +	WriteToRequest(ClientRequest, strfmt.Registry) error +} + +// ClientRequest is an interface for things that know how to +// add information to a swagger client request +type ClientRequest interface { +	SetHeaderParam(string, ...string) error + +	GetHeaderParams() http.Header + +	SetQueryParam(string, ...string) error + +	SetFormParam(string, ...string) error + +	SetPathParam(string, string) error + +	GetQueryParams() url.Values + +	SetFileParam(string, ...NamedReadCloser) error + +	SetBodyParam(interface{}) error + +	SetTimeout(time.Duration) error + +	GetMethod() string + +	GetPath() string + +	GetBody() []byte + +	GetBodyParam() interface{} + +	GetFileParam() map[string][]NamedReadCloser +} + +// NamedReadCloser represents a named ReadCloser interface +type NamedReadCloser interface { +	io.ReadCloser +	Name() string +} + +// NamedReader creates a NamedReadCloser for use as file upload +func NamedReader(name string, rdr io.Reader) NamedReadCloser { +	rc, ok := rdr.(io.ReadCloser) +	if !ok { +		rc = io.NopCloser(rdr) +	} +	return &namedReadCloser{ +		name: name, +		cr:   rc, +	} +} + +type namedReadCloser struct { +	name string +	cr   io.ReadCloser +} + +func (n *namedReadCloser) Close() error { +	return n.cr.Close() +} +func (n *namedReadCloser) Read(p []byte) (int, error) { +	return n.cr.Read(p) +} +func (n *namedReadCloser) Name() string { +	return n.name +} + +type TestClientRequest struct { +	Headers http.Header +	Body    interface{} +} + +func (t *TestClientRequest) SetHeaderParam(name string, values ...string) error { +	if t.Headers == nil { +		t.Headers = make(http.Header) +	} +	t.Headers.Set(name, values[0]) +	return nil +} + +func (t *TestClientRequest) SetQueryParam(_ string, _ ...string) error { return nil } + +func (t *TestClientRequest) SetFormParam(_ string, _ ...string) error { return nil } + +func (t *TestClientRequest) SetPathParam(_ string, _ string) error { return nil } + +func (t *TestClientRequest) SetFileParam(_ string, _ ...NamedReadCloser) error { return nil } + +func (t *TestClientRequest) SetBodyParam(body interface{}) error { +	t.Body = body +	return nil +} + +func (t *TestClientRequest) SetTimeout(time.Duration) error { +	return nil +} + +func (t *TestClientRequest) GetQueryParams() url.Values { return nil } + +func (t *TestClientRequest) GetMethod() string { return "" } + +func (t *TestClientRequest) GetPath() string { return "" } + +func (t *TestClientRequest) GetBody() []byte { return nil } + +func (t *TestClientRequest) GetBodyParam() interface{} { +	return t.Body +} + +func (t *TestClientRequest) GetFileParam() map[string][]NamedReadCloser { +	return nil +} + +func (t *TestClientRequest) GetHeaderParams() http.Header { +	return t.Headers +} diff --git a/vendor/github.com/go-openapi/runtime/client_response.go b/vendor/github.com/go-openapi/runtime/client_response.go new file mode 100644 index 000000000..0d1691149 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_response.go @@ -0,0 +1,110 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( +	"encoding/json" +	"fmt" +	"io" +) + +// A ClientResponse represents a client response +// This bridges between responses obtained from different transports +type ClientResponse interface { +	Code() int +	Message() string +	GetHeader(string) string +	GetHeaders(string) []string +	Body() io.ReadCloser +} + +// A ClientResponseReaderFunc turns a function into a ClientResponseReader interface implementation +type ClientResponseReaderFunc func(ClientResponse, Consumer) (interface{}, error) + +// ReadResponse reads the response +func (read ClientResponseReaderFunc) ReadResponse(resp ClientResponse, consumer Consumer) (interface{}, error) { +	return read(resp, consumer) +} + +// A ClientResponseReader is an interface for things want to read a response. +// An application of this is to create structs from response values +type ClientResponseReader interface { +	ReadResponse(ClientResponse, Consumer) (interface{}, error) +} + +// NewAPIError creates a new API error +func NewAPIError(opName string, payload interface{}, code int) *APIError { +	return &APIError{ +		OperationName: opName, +		Response:      payload, +		Code:          code, +	} +} + +// APIError wraps an error model and captures the status code +type APIError struct { +	OperationName string +	Response      interface{} +	Code          int +} + +func (o *APIError) Error() string { +	var resp []byte +	if err, ok := o.Response.(error); ok { +		resp = []byte("'" + err.Error() + "'") +	} else { +		resp, _ = json.Marshal(o.Response) +	} +	return fmt.Sprintf("%s (status %d): %s", o.OperationName, o.Code, resp) +} + +func (o *APIError) String() string { +	return o.Error() +} + +// IsSuccess returns true when this elapse o k response returns a 2xx status code +func (o *APIError) IsSuccess() bool { +	return o.Code/100 == 2 +} + +// IsRedirect returns true when this elapse o k response returns a 3xx status code +func (o *APIError) IsRedirect() bool { +	return o.Code/100 == 3 +} + +// IsClientError returns true when this elapse o k response returns a 4xx status code +func (o *APIError) IsClientError() bool { +	return o.Code/100 == 4 +} + +// IsServerError returns true when this elapse o k response returns a 5xx status code +func (o *APIError) IsServerError() bool { +	return o.Code/100 == 5 +} + +// IsCode returns true when this elapse o k response returns a 4xx status code +func (o *APIError) IsCode(code int) bool { +	return o.Code == code +} + +// A ClientResponseStatus is a common interface implemented by all responses on the generated code +// You can use this to treat any client response based on status code +type ClientResponseStatus interface { +	IsSuccess() bool +	IsRedirect() bool +	IsClientError() bool +	IsServerError() bool +	IsCode(int) bool +} diff --git a/vendor/github.com/go-openapi/runtime/constants.go b/vendor/github.com/go-openapi/runtime/constants.go new file mode 100644 index 000000000..515969242 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/constants.go @@ -0,0 +1,49 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +const ( +	// HeaderContentType represents a http content-type header, it's value is supposed to be a mime type +	HeaderContentType = "Content-Type" + +	// HeaderTransferEncoding represents a http transfer-encoding header. +	HeaderTransferEncoding = "Transfer-Encoding" + +	// HeaderAccept the Accept header +	HeaderAccept = "Accept" +	// HeaderAuthorization the Authorization header +	HeaderAuthorization = "Authorization" + +	charsetKey = "charset" + +	// DefaultMime the default fallback mime type +	DefaultMime = "application/octet-stream" +	// JSONMime the json mime type +	JSONMime = "application/json" +	// YAMLMime the yaml mime type +	YAMLMime = "application/x-yaml" +	// XMLMime the xml mime type +	XMLMime = "application/xml" +	// TextMime the text mime type +	TextMime = "text/plain" +	// HTMLMime the html mime type +	HTMLMime = "text/html" +	// CSVMime the csv mime type +	CSVMime = "text/csv" +	// MultipartFormMime the multipart form mime type +	MultipartFormMime = "multipart/form-data" +	// URLencodedFormMime the url encoded form mime type +	URLencodedFormMime = "application/x-www-form-urlencoded" +) diff --git a/vendor/github.com/go-openapi/runtime/csv.go b/vendor/github.com/go-openapi/runtime/csv.go new file mode 100644 index 000000000..d807bd915 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/csv.go @@ -0,0 +1,77 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( +	"bytes" +	"encoding/csv" +	"errors" +	"io" +) + +// CSVConsumer creates a new CSV consumer +func CSVConsumer() Consumer { +	return ConsumerFunc(func(reader io.Reader, data interface{}) error { +		if reader == nil { +			return errors.New("CSVConsumer requires a reader") +		} + +		csvReader := csv.NewReader(reader) +		writer, ok := data.(io.Writer) +		if !ok { +			return errors.New("data type must be io.Writer") +		} +		csvWriter := csv.NewWriter(writer) +		records, err := csvReader.ReadAll() +		if err != nil { +			return err +		} +		for _, r := range records { +			if err := csvWriter.Write(r); err != nil { +				return err +			} +		} +		csvWriter.Flush() +		return nil +	}) +} + +// CSVProducer creates a new CSV producer +func CSVProducer() Producer { +	return ProducerFunc(func(writer io.Writer, data interface{}) error { +		if writer == nil { +			return errors.New("CSVProducer requires a writer") +		} + +		dataBytes, ok := data.([]byte) +		if !ok { +			return errors.New("data type must be byte array") +		} + +		csvReader := csv.NewReader(bytes.NewBuffer(dataBytes)) +		records, err := csvReader.ReadAll() +		if err != nil { +			return err +		} +		csvWriter := csv.NewWriter(writer) +		for _, r := range records { +			if err := csvWriter.Write(r); err != nil { +				return err +			} +		} +		csvWriter.Flush() +		return nil +	}) +} diff --git a/vendor/github.com/go-openapi/runtime/discard.go b/vendor/github.com/go-openapi/runtime/discard.go new file mode 100644 index 000000000..0d390cfd6 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/discard.go @@ -0,0 +1,9 @@ +package runtime + +import "io" + +// DiscardConsumer does absolutely nothing, it's a black hole. +var DiscardConsumer = ConsumerFunc(func(_ io.Reader, _ interface{}) error { return nil }) + +// DiscardProducer does absolutely nothing, it's a black hole. +var DiscardProducer = ProducerFunc(func(_ io.Writer, _ interface{}) error { return nil }) diff --git a/vendor/github.com/go-openapi/runtime/file.go b/vendor/github.com/go-openapi/runtime/file.go new file mode 100644 index 000000000..397d8a459 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/file.go @@ -0,0 +1,19 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import "github.com/go-openapi/swag" + +type File = swag.File diff --git a/vendor/github.com/go-openapi/runtime/headers.go b/vendor/github.com/go-openapi/runtime/headers.go new file mode 100644 index 000000000..4d111db4f --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/headers.go @@ -0,0 +1,45 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( +	"mime" +	"net/http" + +	"github.com/go-openapi/errors" +) + +// ContentType parses a content type header +func ContentType(headers http.Header) (string, string, error) { +	ct := headers.Get(HeaderContentType) +	orig := ct +	if ct == "" { +		ct = DefaultMime +	} +	if ct == "" { +		return "", "", nil +	} + +	mt, opts, err := mime.ParseMediaType(ct) +	if err != nil { +		return "", "", errors.NewParseError(HeaderContentType, "header", orig, err) +	} + +	if cs, ok := opts[charsetKey]; ok { +		return mt, cs, nil +	} + +	return mt, "", nil +} diff --git a/vendor/github.com/go-openapi/runtime/interfaces.go b/vendor/github.com/go-openapi/runtime/interfaces.go new file mode 100644 index 000000000..e33412868 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/interfaces.go @@ -0,0 +1,112 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( +	"context" +	"io" +	"net/http" + +	"github.com/go-openapi/strfmt" +) + +// OperationHandlerFunc an adapter for a function to the OperationHandler interface +type OperationHandlerFunc func(interface{}) (interface{}, error) + +// Handle implements the operation handler interface +func (s OperationHandlerFunc) Handle(data interface{}) (interface{}, error) { +	return s(data) +} + +// OperationHandler a handler for a swagger operation +type OperationHandler interface { +	Handle(interface{}) (interface{}, error) +} + +// ConsumerFunc represents a function that can be used as a consumer +type ConsumerFunc func(io.Reader, interface{}) error + +// Consume consumes the reader into the data parameter +func (fn ConsumerFunc) Consume(reader io.Reader, data interface{}) error { +	return fn(reader, data) +} + +// Consumer implementations know how to bind the values on the provided interface to +// data provided by the request body +type Consumer interface { +	// Consume performs the binding of request values +	Consume(io.Reader, interface{}) error +} + +// ProducerFunc represents a function that can be used as a producer +type ProducerFunc func(io.Writer, interface{}) error + +// Produce produces the response for the provided data +func (f ProducerFunc) Produce(writer io.Writer, data interface{}) error { +	return f(writer, data) +} + +// Producer implementations know how to turn the provided interface into a valid +// HTTP response +type Producer interface { +	// Produce writes to the http response +	Produce(io.Writer, interface{}) error +} + +// AuthenticatorFunc turns a function into an authenticator +type AuthenticatorFunc func(interface{}) (bool, interface{}, error) + +// Authenticate authenticates the request with the provided data +func (f AuthenticatorFunc) Authenticate(params interface{}) (bool, interface{}, error) { +	return f(params) +} + +// Authenticator represents an authentication strategy +// implementations of Authenticator know how to authenticate the +// request data and translate that into a valid principal object or an error +type Authenticator interface { +	Authenticate(interface{}) (bool, interface{}, error) +} + +// AuthorizerFunc turns a function into an authorizer +type AuthorizerFunc func(*http.Request, interface{}) error + +// Authorize authorizes the processing of the request for the principal +func (f AuthorizerFunc) Authorize(r *http.Request, principal interface{}) error { +	return f(r, principal) +} + +// Authorizer represents an authorization strategy +// implementations of Authorizer know how to authorize the principal object +// using the request data and returns error if unauthorized +type Authorizer interface { +	Authorize(*http.Request, interface{}) error +} + +// Validatable types implementing this interface allow customizing their validation +// this will be used instead of the reflective validation based on the spec document. +// the implementations are assumed to have been generated by the swagger tool so they should +// contain all the validations obtained from the spec +type Validatable interface { +	Validate(strfmt.Registry) error +} + +// ContextValidatable types implementing this interface allow customizing their validation +// this will be used instead of the reflective validation based on the spec document. +// the implementations are assumed to have been generated by the swagger tool so they should +// contain all the context validations obtained from the spec +type ContextValidatable interface { +	ContextValidate(context.Context, strfmt.Registry) error +} diff --git a/vendor/github.com/go-openapi/runtime/json.go b/vendor/github.com/go-openapi/runtime/json.go new file mode 100644 index 000000000..5a690559c --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/json.go @@ -0,0 +1,38 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( +	"encoding/json" +	"io" +) + +// JSONConsumer creates a new JSON consumer +func JSONConsumer() Consumer { +	return ConsumerFunc(func(reader io.Reader, data interface{}) error { +		dec := json.NewDecoder(reader) +		dec.UseNumber() // preserve number formats +		return dec.Decode(data) +	}) +} + +// JSONProducer creates a new JSON producer +func JSONProducer() Producer { +	return ProducerFunc(func(writer io.Writer, data interface{}) error { +		enc := json.NewEncoder(writer) +		enc.SetEscapeHTML(false) +		return enc.Encode(data) +	}) +} diff --git a/vendor/github.com/go-openapi/runtime/logger/logger.go b/vendor/github.com/go-openapi/runtime/logger/logger.go new file mode 100644 index 000000000..6f4debcc1 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/logger/logger.go @@ -0,0 +1,20 @@ +package logger + +import "os" + +type Logger interface { +	Printf(format string, args ...interface{}) +	Debugf(format string, args ...interface{}) +} + +func DebugEnabled() bool { +	d := os.Getenv("SWAGGER_DEBUG") +	if d != "" && d != "false" && d != "0" { +		return true +	} +	d = os.Getenv("DEBUG") +	if d != "" && d != "false" && d != "0" { +		return true +	} +	return false +} diff --git a/vendor/github.com/go-openapi/runtime/logger/standard.go b/vendor/github.com/go-openapi/runtime/logger/standard.go new file mode 100644 index 000000000..f7e67ebb9 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/logger/standard.go @@ -0,0 +1,22 @@ +package logger + +import ( +	"fmt" +	"os" +) + +type StandardLogger struct{} + +func (StandardLogger) Printf(format string, args ...interface{}) { +	if len(format) == 0 || format[len(format)-1] != '\n' { +		format += "\n" +	} +	fmt.Fprintf(os.Stderr, format, args...) +} + +func (StandardLogger) Debugf(format string, args ...interface{}) { +	if len(format) == 0 || format[len(format)-1] != '\n' { +		format += "\n" +	} +	fmt.Fprintf(os.Stderr, format, args...) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/context.go b/vendor/github.com/go-openapi/runtime/middleware/context.go new file mode 100644 index 000000000..d21ae4e87 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/context.go @@ -0,0 +1,635 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package middleware + +import ( +	stdContext "context" +	"fmt" +	"net/http" +	"strings" +	"sync" + +	"github.com/go-openapi/analysis" +	"github.com/go-openapi/errors" +	"github.com/go-openapi/loads" +	"github.com/go-openapi/spec" +	"github.com/go-openapi/strfmt" + +	"github.com/go-openapi/runtime" +	"github.com/go-openapi/runtime/logger" +	"github.com/go-openapi/runtime/middleware/untyped" +	"github.com/go-openapi/runtime/security" +) + +// Debug when true turns on verbose logging +var Debug = logger.DebugEnabled() +var Logger logger.Logger = logger.StandardLogger{} + +func debugLog(format string, args ...interface{}) { +	if Debug { +		Logger.Printf(format, args...) +	} +} + +// A Builder can create middlewares +type Builder func(http.Handler) http.Handler + +// PassthroughBuilder returns the handler, aka the builder identity function +func PassthroughBuilder(handler http.Handler) http.Handler { return handler } + +// RequestBinder is an interface for types to implement +// when they want to be able to bind from a request +type RequestBinder interface { +	BindRequest(*http.Request, *MatchedRoute) error +} + +// Responder is an interface for types to implement +// when they want to be considered for writing HTTP responses +type Responder interface { +	WriteResponse(http.ResponseWriter, runtime.Producer) +} + +// ResponderFunc wraps a func as a Responder interface +type ResponderFunc func(http.ResponseWriter, runtime.Producer) + +// WriteResponse writes to the response +func (fn ResponderFunc) WriteResponse(rw http.ResponseWriter, pr runtime.Producer) { +	fn(rw, pr) +} + +// Context is a type safe wrapper around an untyped request context +// used throughout to store request context with the standard context attached +// to the http.Request +type Context struct { +	spec     *loads.Document +	analyzer *analysis.Spec +	api      RoutableAPI +	router   Router +} + +type routableUntypedAPI struct { +	api             *untyped.API +	hlock           *sync.Mutex +	handlers        map[string]map[string]http.Handler +	defaultConsumes string +	defaultProduces string +} + +func newRoutableUntypedAPI(spec *loads.Document, api *untyped.API, context *Context) *routableUntypedAPI { +	var handlers map[string]map[string]http.Handler +	if spec == nil || api == nil { +		return nil +	} +	analyzer := analysis.New(spec.Spec()) +	for method, hls := range analyzer.Operations() { +		um := strings.ToUpper(method) +		for path, op := range hls { +			schemes := analyzer.SecurityRequirementsFor(op) + +			if oh, ok := api.OperationHandlerFor(method, path); ok { +				if handlers == nil { +					handlers = make(map[string]map[string]http.Handler) +				} +				if b, ok := handlers[um]; !ok || b == nil { +					handlers[um] = make(map[string]http.Handler) +				} + +				var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +					// lookup route info in the context +					route, rCtx, _ := context.RouteInfo(r) +					if rCtx != nil { +						r = rCtx +					} + +					// bind and validate the request using reflection +					var bound interface{} +					var validation error +					bound, r, validation = context.BindAndValidate(r, route) +					if validation != nil { +						context.Respond(w, r, route.Produces, route, validation) +						return +					} + +					// actually handle the request +					result, err := oh.Handle(bound) +					if err != nil { +						// respond with failure +						context.Respond(w, r, route.Produces, route, err) +						return +					} + +					// respond with success +					context.Respond(w, r, route.Produces, route, result) +				}) + +				if len(schemes) > 0 { +					handler = newSecureAPI(context, handler) +				} +				handlers[um][path] = handler +			} +		} +	} + +	return &routableUntypedAPI{ +		api:             api, +		hlock:           new(sync.Mutex), +		handlers:        handlers, +		defaultProduces: api.DefaultProduces, +		defaultConsumes: api.DefaultConsumes, +	} +} + +func (r *routableUntypedAPI) HandlerFor(method, path string) (http.Handler, bool) { +	r.hlock.Lock() +	paths, ok := r.handlers[strings.ToUpper(method)] +	if !ok { +		r.hlock.Unlock() +		return nil, false +	} +	handler, ok := paths[path] +	r.hlock.Unlock() +	return handler, ok +} +func (r *routableUntypedAPI) ServeErrorFor(operationID string) func(http.ResponseWriter, *http.Request, error) { +	return r.api.ServeError +} +func (r *routableUntypedAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer { +	return r.api.ConsumersFor(mediaTypes) +} +func (r *routableUntypedAPI) ProducersFor(mediaTypes []string) map[string]runtime.Producer { +	return r.api.ProducersFor(mediaTypes) +} +func (r *routableUntypedAPI) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator { +	return r.api.AuthenticatorsFor(schemes) +} +func (r *routableUntypedAPI) Authorizer() runtime.Authorizer { +	return r.api.Authorizer() +} +func (r *routableUntypedAPI) Formats() strfmt.Registry { +	return r.api.Formats() +} + +func (r *routableUntypedAPI) DefaultProduces() string { +	return r.defaultProduces +} + +func (r *routableUntypedAPI) DefaultConsumes() string { +	return r.defaultConsumes +} + +// NewRoutableContext creates a new context for a routable API +func NewRoutableContext(spec *loads.Document, routableAPI RoutableAPI, routes Router) *Context { +	var an *analysis.Spec +	if spec != nil { +		an = analysis.New(spec.Spec()) +	} + +	return NewRoutableContextWithAnalyzedSpec(spec, an, routableAPI, routes) +} + +// NewRoutableContextWithAnalyzedSpec is like NewRoutableContext but takes in input the analysed spec too +func NewRoutableContextWithAnalyzedSpec(spec *loads.Document, an *analysis.Spec, routableAPI RoutableAPI, routes Router) *Context { +	// Either there are no spec doc and analysis, or both of them. +	if !((spec == nil && an == nil) || (spec != nil && an != nil)) { +		panic(errors.New(http.StatusInternalServerError, "routable context requires either both spec doc and analysis, or none of them")) +	} + +	ctx := &Context{spec: spec, api: routableAPI, analyzer: an, router: routes} +	return ctx +} + +// NewContext creates a new context wrapper +func NewContext(spec *loads.Document, api *untyped.API, routes Router) *Context { +	var an *analysis.Spec +	if spec != nil { +		an = analysis.New(spec.Spec()) +	} +	ctx := &Context{spec: spec, analyzer: an} +	ctx.api = newRoutableUntypedAPI(spec, api, ctx) +	ctx.router = routes +	return ctx +} + +// Serve serves the specified spec with the specified api registrations as a http.Handler +func Serve(spec *loads.Document, api *untyped.API) http.Handler { +	return ServeWithBuilder(spec, api, PassthroughBuilder) +} + +// ServeWithBuilder serves the specified spec with the specified api registrations as a http.Handler that is decorated +// by the Builder +func ServeWithBuilder(spec *loads.Document, api *untyped.API, builder Builder) http.Handler { +	context := NewContext(spec, api, nil) +	return context.APIHandler(builder) +} + +type contextKey int8 + +const ( +	_ contextKey = iota +	ctxContentType +	ctxResponseFormat +	ctxMatchedRoute +	ctxBoundParams +	ctxSecurityPrincipal +	ctxSecurityScopes +) + +// MatchedRouteFrom request context value. +func MatchedRouteFrom(req *http.Request) *MatchedRoute { +	mr := req.Context().Value(ctxMatchedRoute) +	if mr == nil { +		return nil +	} +	if res, ok := mr.(*MatchedRoute); ok { +		return res +	} +	return nil +} + +// SecurityPrincipalFrom request context value. +func SecurityPrincipalFrom(req *http.Request) interface{} { +	return req.Context().Value(ctxSecurityPrincipal) +} + +// SecurityScopesFrom request context value. +func SecurityScopesFrom(req *http.Request) []string { +	rs := req.Context().Value(ctxSecurityScopes) +	if res, ok := rs.([]string); ok { +		return res +	} +	return nil +} + +type contentTypeValue struct { +	MediaType string +	Charset   string +} + +// BasePath returns the base path for this API +func (c *Context) BasePath() string { +	return c.spec.BasePath() +} + +// RequiredProduces returns the accepted content types for responses +func (c *Context) RequiredProduces() []string { +	return c.analyzer.RequiredProduces() +} + +// BindValidRequest binds a params object to a request but only when the request is valid +// if the request is not valid an error will be returned +func (c *Context) BindValidRequest(request *http.Request, route *MatchedRoute, binder RequestBinder) error { +	var res []error +	var requestContentType string + +	// check and validate content type, select consumer +	if runtime.HasBody(request) { +		ct, _, err := runtime.ContentType(request.Header) +		if err != nil { +			res = append(res, err) +		} else { +			if err := validateContentType(route.Consumes, ct); err != nil { +				res = append(res, err) +			} +			if len(res) == 0 { +				cons, ok := route.Consumers[ct] +				if !ok { +					res = append(res, errors.New(500, "no consumer registered for %s", ct)) +				} else { +					route.Consumer = cons +					requestContentType = ct +				} +			} +		} +	} + +	// check and validate the response format +	if len(res) == 0 { +		// if the route does not provide Produces and a default contentType could not be identified +		// based on a body, typical for GET and DELETE requests, then default contentType to. +		if len(route.Produces) == 0 && requestContentType == "" { +			requestContentType = "*/*" +		} + +		if str := NegotiateContentType(request, route.Produces, requestContentType); str == "" { +			res = append(res, errors.InvalidResponseFormat(request.Header.Get(runtime.HeaderAccept), route.Produces)) +		} +	} + +	// now bind the request with the provided binder +	// it's assumed the binder will also validate the request and return an error if the +	// request is invalid +	if binder != nil && len(res) == 0 { +		if err := binder.BindRequest(request, route); err != nil { +			return err +		} +	} + +	if len(res) > 0 { +		return errors.CompositeValidationError(res...) +	} +	return nil +} + +// ContentType gets the parsed value of a content type +// Returns the media type, its charset and a shallow copy of the request +// when its context doesn't contain the content type value, otherwise it returns +// the same request +// Returns the error that runtime.ContentType may retunrs. +func (c *Context) ContentType(request *http.Request) (string, string, *http.Request, error) { +	var rCtx = request.Context() + +	if v, ok := rCtx.Value(ctxContentType).(*contentTypeValue); ok { +		return v.MediaType, v.Charset, request, nil +	} + +	mt, cs, err := runtime.ContentType(request.Header) +	if err != nil { +		return "", "", nil, err +	} +	rCtx = stdContext.WithValue(rCtx, ctxContentType, &contentTypeValue{mt, cs}) +	return mt, cs, request.WithContext(rCtx), nil +} + +// LookupRoute looks a route up and returns true when it is found +func (c *Context) LookupRoute(request *http.Request) (*MatchedRoute, bool) { +	if route, ok := c.router.Lookup(request.Method, request.URL.EscapedPath()); ok { +		return route, ok +	} +	return nil, false +} + +// RouteInfo tries to match a route for this request +// Returns the matched route, a shallow copy of the request if its context +// contains the matched router, otherwise the same request, and a bool to +// indicate if it the request matches one of the routes, if it doesn't +// then it returns false and nil for the other two return values +func (c *Context) RouteInfo(request *http.Request) (*MatchedRoute, *http.Request, bool) { +	var rCtx = request.Context() + +	if v, ok := rCtx.Value(ctxMatchedRoute).(*MatchedRoute); ok { +		return v, request, ok +	} + +	if route, ok := c.LookupRoute(request); ok { +		rCtx = stdContext.WithValue(rCtx, ctxMatchedRoute, route) +		return route, request.WithContext(rCtx), ok +	} + +	return nil, nil, false +} + +// ResponseFormat negotiates the response content type +// Returns the response format and a shallow copy of the request if its context +// doesn't contain the response format, otherwise the same request +func (c *Context) ResponseFormat(r *http.Request, offers []string) (string, *http.Request) { +	var rCtx = r.Context() + +	if v, ok := rCtx.Value(ctxResponseFormat).(string); ok { +		debugLog("[%s %s] found response format %q in context", r.Method, r.URL.Path, v) +		return v, r +	} + +	format := NegotiateContentType(r, offers, "") +	if format != "" { +		debugLog("[%s %s] set response format %q in context", r.Method, r.URL.Path, format) +		r = r.WithContext(stdContext.WithValue(rCtx, ctxResponseFormat, format)) +	} +	debugLog("[%s %s] negotiated response format %q", r.Method, r.URL.Path, format) +	return format, r +} + +// AllowedMethods gets the allowed methods for the path of this request +func (c *Context) AllowedMethods(request *http.Request) []string { +	return c.router.OtherMethods(request.Method, request.URL.EscapedPath()) +} + +// ResetAuth removes the current principal from the request context +func (c *Context) ResetAuth(request *http.Request) *http.Request { +	rctx := request.Context() +	rctx = stdContext.WithValue(rctx, ctxSecurityPrincipal, nil) +	rctx = stdContext.WithValue(rctx, ctxSecurityScopes, nil) +	return request.WithContext(rctx) +} + +// Authorize authorizes the request +// Returns the principal object and a shallow copy of the request when its +// context doesn't contain the principal, otherwise the same request or an error +// (the last) if one of the authenticators returns one or an Unauthenticated error +func (c *Context) Authorize(request *http.Request, route *MatchedRoute) (interface{}, *http.Request, error) { +	if route == nil || !route.HasAuth() { +		return nil, nil, nil +	} + +	var rCtx = request.Context() +	if v := rCtx.Value(ctxSecurityPrincipal); v != nil { +		return v, request, nil +	} + +	applies, usr, err := route.Authenticators.Authenticate(request, route) +	if !applies || err != nil || !route.Authenticators.AllowsAnonymous() && usr == nil { +		if err != nil { +			return nil, nil, err +		} +		return nil, nil, errors.Unauthenticated("invalid credentials") +	} +	if route.Authorizer != nil { +		if err := route.Authorizer.Authorize(request, usr); err != nil { +			if _, ok := err.(errors.Error); ok { +				return nil, nil, err +			} + +			return nil, nil, errors.New(http.StatusForbidden, err.Error()) +		} +	} + +	rCtx = request.Context() + +	rCtx = stdContext.WithValue(rCtx, ctxSecurityPrincipal, usr) +	rCtx = stdContext.WithValue(rCtx, ctxSecurityScopes, route.Authenticator.AllScopes()) +	return usr, request.WithContext(rCtx), nil +} + +// BindAndValidate binds and validates the request +// Returns the validation map and a shallow copy of the request when its context +// doesn't contain the validation, otherwise it returns the same request or an +// CompositeValidationError error +func (c *Context) BindAndValidate(request *http.Request, matched *MatchedRoute) (interface{}, *http.Request, error) { +	var rCtx = request.Context() + +	if v, ok := rCtx.Value(ctxBoundParams).(*validation); ok { +		debugLog("got cached validation (valid: %t)", len(v.result) == 0) +		if len(v.result) > 0 { +			return v.bound, request, errors.CompositeValidationError(v.result...) +		} +		return v.bound, request, nil +	} +	result := validateRequest(c, request, matched) +	rCtx = stdContext.WithValue(rCtx, ctxBoundParams, result) +	request = request.WithContext(rCtx) +	if len(result.result) > 0 { +		return result.bound, request, errors.CompositeValidationError(result.result...) +	} +	debugLog("no validation errors found") +	return result.bound, request, nil +} + +// NotFound the default not found responder for when no route has been matched yet +func (c *Context) NotFound(rw http.ResponseWriter, r *http.Request) { +	c.Respond(rw, r, []string{c.api.DefaultProduces()}, nil, errors.NotFound("not found")) +} + +// Respond renders the response after doing some content negotiation +func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []string, route *MatchedRoute, data interface{}) { +	debugLog("responding to %s %s with produces: %v", r.Method, r.URL.Path, produces) +	offers := []string{} +	for _, mt := range produces { +		if mt != c.api.DefaultProduces() { +			offers = append(offers, mt) +		} +	} +	// the default producer is last so more specific producers take precedence +	offers = append(offers, c.api.DefaultProduces()) +	debugLog("offers: %v", offers) + +	var format string +	format, r = c.ResponseFormat(r, offers) +	rw.Header().Set(runtime.HeaderContentType, format) + +	if resp, ok := data.(Responder); ok { +		producers := route.Producers +		// producers contains keys with normalized format, if a format has MIME type parameter such as `text/plain; charset=utf-8` +		// then you must provide `text/plain` to get the correct producer. HOWEVER, format here is not normalized. +		prod, ok := producers[normalizeOffer(format)] +		if !ok { +			prods := c.api.ProducersFor(normalizeOffers([]string{c.api.DefaultProduces()})) +			pr, ok := prods[c.api.DefaultProduces()] +			if !ok { +				panic(errors.New(http.StatusInternalServerError, "can't find a producer for "+format)) +			} +			prod = pr +		} +		resp.WriteResponse(rw, prod) +		return +	} + +	if err, ok := data.(error); ok { +		if format == "" { +			rw.Header().Set(runtime.HeaderContentType, runtime.JSONMime) +		} + +		if realm := security.FailedBasicAuth(r); realm != "" { +			rw.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", realm)) +		} + +		if route == nil || route.Operation == nil { +			c.api.ServeErrorFor("")(rw, r, err) +			return +		} +		c.api.ServeErrorFor(route.Operation.ID)(rw, r, err) +		return +	} + +	if route == nil || route.Operation == nil { +		rw.WriteHeader(200) +		if r.Method == "HEAD" { +			return +		} +		producers := c.api.ProducersFor(normalizeOffers(offers)) +		prod, ok := producers[format] +		if !ok { +			panic(errors.New(http.StatusInternalServerError, "can't find a producer for "+format)) +		} +		if err := prod.Produce(rw, data); err != nil { +			panic(err) // let the recovery middleware deal with this +		} +		return +	} + +	if _, code, ok := route.Operation.SuccessResponse(); ok { +		rw.WriteHeader(code) +		if code == 204 || r.Method == "HEAD" { +			return +		} + +		producers := route.Producers +		prod, ok := producers[format] +		if !ok { +			if !ok { +				prods := c.api.ProducersFor(normalizeOffers([]string{c.api.DefaultProduces()})) +				pr, ok := prods[c.api.DefaultProduces()] +				if !ok { +					panic(errors.New(http.StatusInternalServerError, "can't find a producer for "+format)) +				} +				prod = pr +			} +		} +		if err := prod.Produce(rw, data); err != nil { +			panic(err) // let the recovery middleware deal with this +		} +		return +	} + +	c.api.ServeErrorFor(route.Operation.ID)(rw, r, errors.New(http.StatusInternalServerError, "can't produce response")) +} + +func (c *Context) APIHandlerSwaggerUI(builder Builder) http.Handler { +	b := builder +	if b == nil { +		b = PassthroughBuilder +	} + +	var title string +	sp := c.spec.Spec() +	if sp != nil && sp.Info != nil && sp.Info.Title != "" { +		title = sp.Info.Title +	} + +	swaggerUIOpts := SwaggerUIOpts{ +		BasePath: c.BasePath(), +		Title:    title, +	} + +	return Spec("", c.spec.Raw(), SwaggerUI(swaggerUIOpts, c.RoutesHandler(b))) +} + +// APIHandler returns a handler to serve the API, this includes a swagger spec, router and the contract defined in the swagger spec +func (c *Context) APIHandler(builder Builder) http.Handler { +	b := builder +	if b == nil { +		b = PassthroughBuilder +	} + +	var title string +	sp := c.spec.Spec() +	if sp != nil && sp.Info != nil && sp.Info.Title != "" { +		title = sp.Info.Title +	} + +	redocOpts := RedocOpts{ +		BasePath: c.BasePath(), +		Title:    title, +	} + +	return Spec("", c.spec.Raw(), Redoc(redocOpts, c.RoutesHandler(b))) +} + +// RoutesHandler returns a handler to serve the API, just the routes and the contract defined in the swagger spec +func (c *Context) RoutesHandler(builder Builder) http.Handler { +	b := builder +	if b == nil { +		b = PassthroughBuilder +	} +	return NewRouter(c, b(NewOperationExecutor(c))) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/denco/LICENSE b/vendor/github.com/go-openapi/runtime/middleware/denco/LICENSE new file mode 100644 index 000000000..e65039ad8 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/denco/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Naoya Inada <naoina@kuune.org> + +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. diff --git a/vendor/github.com/go-openapi/runtime/middleware/denco/README.md b/vendor/github.com/go-openapi/runtime/middleware/denco/README.md new file mode 100644 index 000000000..30109e17d --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/denco/README.md @@ -0,0 +1,180 @@ +# Denco [](https://travis-ci.org/naoina/denco) + +The fast and flexible HTTP request router for [Go](http://golang.org). + +Denco is based on Double-Array implementation of [Kocha-urlrouter](https://github.com/naoina/kocha-urlrouter). +However, Denco is optimized and some features added. + +## Features + +* Fast (See [go-http-routing-benchmark](https://github.com/naoina/go-http-routing-benchmark)) +* [URL patterns](#url-patterns) (`/foo/:bar` and `/foo/*wildcard`) +* Small (but enough) URL router API +* HTTP request multiplexer like `http.ServeMux` + +## Installation + +    go get -u github.com/go-openapi/runtime/middleware/denco + +## Using as HTTP request multiplexer + +```go +package main + +import ( +    "fmt" +    "log" +    "net/http" + +    "github.com/go-openapi/runtime/middleware/denco" +) + +func Index(w http.ResponseWriter, r *http.Request, params denco.Params) { +    fmt.Fprintf(w, "Welcome to Denco!\n") +} + +func User(w http.ResponseWriter, r *http.Request, params denco.Params) { +    fmt.Fprintf(w, "Hello %s!\n", params.Get("name")) +} + +func main() { +    mux := denco.NewMux() +    handler, err := mux.Build([]denco.Handler{ +        mux.GET("/", Index), +        mux.GET("/user/:name", User), +        mux.POST("/user/:name", User), +    }) +    if err != nil { +        panic(err) +    } +    log.Fatal(http.ListenAndServe(":8080", handler)) +} +``` + +## Using as URL router + +```go +package main + +import ( +	"fmt" + +	"github.com/go-openapi/runtime/middleware/denco" +) + +type route struct { +	name string +} + +func main() { +	router := denco.New() +	router.Build([]denco.Record{ +		{"/", &route{"root"}}, +		{"/user/:id", &route{"user"}}, +		{"/user/:name/:id", &route{"username"}}, +		{"/static/*filepath", &route{"static"}}, +	}) + +	data, params, found := router.Lookup("/") +	// print `&main.route{name:"root"}, denco.Params(nil), true`. +	fmt.Printf("%#v, %#v, %#v\n", data, params, found) + +	data, params, found = router.Lookup("/user/hoge") +	// print `&main.route{name:"user"}, denco.Params{denco.Param{Name:"id", Value:"hoge"}}, true`. +	fmt.Printf("%#v, %#v, %#v\n", data, params, found) + +	data, params, found = router.Lookup("/user/hoge/7") +	// print `&main.route{name:"username"}, denco.Params{denco.Param{Name:"name", Value:"hoge"}, denco.Param{Name:"id", Value:"7"}}, true`. +	fmt.Printf("%#v, %#v, %#v\n", data, params, found) + +	data, params, found = router.Lookup("/static/path/to/file") +	// print `&main.route{name:"static"}, denco.Params{denco.Param{Name:"filepath", Value:"path/to/file"}}, true`. +	fmt.Printf("%#v, %#v, %#v\n", data, params, found) +} +``` + +See [Godoc](http://godoc.org/github.com/go-openapi/runtime/middleware/denco) for more details. + +## Getting the value of path parameter + +You can get the value of path parameter by 2 ways. + +1. Using [`denco.Params.Get`](http://godoc.org/github.com/go-openapi/runtime/middleware/denco#Params.Get) method +2. Find by loop + +```go +package main + +import ( +    "fmt" + +    "github.com/go-openapi/runtime/middleware/denco" +) + +func main() { +    router := denco.New() +    if err := router.Build([]denco.Record{ +        {"/user/:name/:id", "route1"}, +    }); err != nil { +        panic(err) +    } + +    // 1. Using denco.Params.Get method. +    _, params, _ := router.Lookup("/user/alice/1") +    name := params.Get("name") +    if name != "" { +        fmt.Printf("Hello %s.\n", name) // prints "Hello alice.". +    } + +    // 2. Find by loop. +    for _, param := range params { +        if param.Name == "name" { +            fmt.Printf("Hello %s.\n", name) // prints "Hello alice.". +        } +    } +} +``` + +## URL patterns + +Denco's route matching strategy is "most nearly matching". + +When routes `/:name` and `/alice` have been built, URI `/alice` matches the route `/alice`, not `/:name`. +Because URI `/alice` is more match with the route `/alice` than `/:name`. + +For more example, when routes below have been built: + +``` +/user/alice +/user/:name +/user/:name/:id +/user/alice/:id +/user/:id/bob +``` + +Routes matching are: + +``` +/user/alice      => "/user/alice" (no match with "/user/:name") +/user/bob        => "/user/:name" +/user/naoina/1   => "/user/:name/1" +/user/alice/1    => "/user/alice/:id" (no match with "/user/:name/:id") +/user/1/bob      => "/user/:id/bob"   (no match with "/user/:name/:id") +/user/alice/bob  => "/user/alice/:id" (no match with "/user/:name/:id" and "/user/:id/bob") +``` + +## Limitation + +Denco has some limitations below. + +* Number of param records (such as `/:name`) must be less than 2^22 +* Number of elements of internal slice must be less than 2^22 + +## Benchmarks + +    cd $GOPATH/github.com/go-openapi/runtime/middleware/denco +    go test -bench . -benchmem + +## License + +Denco is licensed under the MIT License. diff --git a/vendor/github.com/go-openapi/runtime/middleware/denco/router.go b/vendor/github.com/go-openapi/runtime/middleware/denco/router.go new file mode 100644 index 000000000..5d2691ec3 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/denco/router.go @@ -0,0 +1,460 @@ +// Package denco provides fast URL router. +package denco + +import ( +	"fmt" +	"sort" +	"strings" +) + +const ( +	// ParamCharacter is a special character for path parameter. +	ParamCharacter = ':' + +	// WildcardCharacter is a special character for wildcard path parameter. +	WildcardCharacter = '*' + +	// TerminationCharacter is a special character for end of path. +	TerminationCharacter = '#' + +	// SeparatorCharacter separates path segments. +	SeparatorCharacter = '/' + +	// PathParamCharacter indicates a RESTCONF path param +	PathParamCharacter = '=' + +	// MaxSize is max size of records and internal slice. +	MaxSize = (1 << 22) - 1 +) + +// Router represents a URL router. +type Router struct { +	// SizeHint expects the maximum number of path parameters in records to Build. +	// SizeHint will be used to determine the capacity of the memory to allocate. +	// By default, SizeHint will be determined from given records to Build. +	SizeHint int + +	static map[string]interface{} +	param  *doubleArray +} + +// New returns a new Router. +func New() *Router { +	return &Router{ +		SizeHint: -1, +		static:   make(map[string]interface{}), +		param:    newDoubleArray(), +	} +} + +// Lookup returns data and path parameters that associated with path. +// params is a slice of the Param that arranged in the order in which parameters appeared. +// e.g. when built routing path is "/path/to/:id/:name" and given path is "/path/to/1/alice". params order is [{"id": "1"}, {"name": "alice"}], not [{"name": "alice"}, {"id": "1"}]. +func (rt *Router) Lookup(path string) (data interface{}, params Params, found bool) { +	if data, found := rt.static[path]; found { +		return data, nil, true +	} +	if len(rt.param.node) == 1 { +		return nil, nil, false +	} +	nd, params, found := rt.param.lookup(path, make([]Param, 0, rt.SizeHint), 1) +	if !found { +		return nil, nil, false +	} +	for i := 0; i < len(params); i++ { +		params[i].Name = nd.paramNames[i] +	} +	return nd.data, params, true +} + +// Build builds URL router from records. +func (rt *Router) Build(records []Record) error { +	statics, params := makeRecords(records) +	if len(params) > MaxSize { +		return fmt.Errorf("denco: too many records") +	} +	if rt.SizeHint < 0 { +		rt.SizeHint = 0 +		for _, p := range params { +			size := 0 +			for _, k := range p.Key { +				if k == ParamCharacter || k == WildcardCharacter { +					size++ +				} +			} +			if size > rt.SizeHint { +				rt.SizeHint = size +			} +		} +	} +	for _, r := range statics { +		rt.static[r.Key] = r.Value +	} +	if err := rt.param.build(params, 1, 0, make(map[int]struct{})); err != nil { +		return err +	} +	return nil +} + +// Param represents name and value of path parameter. +type Param struct { +	Name  string +	Value string +} + +// Params represents the name and value of path parameters. +type Params []Param + +// Get gets the first value associated with the given name. +// If there are no values associated with the key, Get returns "". +func (ps Params) Get(name string) string { +	for _, p := range ps { +		if p.Name == name { +			return p.Value +		} +	} +	return "" +} + +type doubleArray struct { +	bc   []baseCheck +	node []*node +} + +func newDoubleArray() *doubleArray { +	return &doubleArray{ +		bc:   []baseCheck{0}, +		node: []*node{nil}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node. +	} +} + +// baseCheck contains BASE, CHECK and Extra flags. +// From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK. +// +//  BASE (22bit) | Extra flags (2bit) | CHECK (8bit) +// |----------------------|--|--------| +// 32                    10  8         0 +type baseCheck uint32 + +func (bc baseCheck) Base() int { +	return int(bc >> 10) +} + +func (bc *baseCheck) SetBase(base int) { +	*bc |= baseCheck(base) << 10 +} + +func (bc baseCheck) Check() byte { +	return byte(bc) +} + +func (bc *baseCheck) SetCheck(check byte) { +	*bc |= baseCheck(check) +} + +func (bc baseCheck) IsEmpty() bool { +	return bc&0xfffffcff == 0 +} + +func (bc baseCheck) IsSingleParam() bool { +	return bc¶mTypeSingle == paramTypeSingle +} + +func (bc baseCheck) IsWildcardParam() bool { +	return bc¶mTypeWildcard == paramTypeWildcard +} + +func (bc baseCheck) IsAnyParam() bool { +	return bc¶mTypeAny != 0 +} + +func (bc *baseCheck) SetSingleParam() { +	*bc |= (1 << 8) +} + +func (bc *baseCheck) SetWildcardParam() { +	*bc |= (1 << 9) +} + +const ( +	paramTypeSingle   = 0x0100 +	paramTypeWildcard = 0x0200 +	paramTypeAny      = 0x0300 +) + +func (da *doubleArray) lookup(path string, params []Param, idx int) (*node, []Param, bool) { +	indices := make([]uint64, 0, 1) +	for i := 0; i < len(path); i++ { +		if da.bc[idx].IsAnyParam() { +			indices = append(indices, (uint64(i)<<32)|(uint64(idx)&0xffffffff)) +		} +		c := path[i] +		if idx = nextIndex(da.bc[idx].Base(), c); idx >= len(da.bc) || da.bc[idx].Check() != c { +			goto BACKTRACKING +		} +	} +	if next := nextIndex(da.bc[idx].Base(), TerminationCharacter); next < len(da.bc) && da.bc[next].Check() == TerminationCharacter { +		return da.node[da.bc[next].Base()], params, true +	} +BACKTRACKING: +	for j := len(indices) - 1; j >= 0; j-- { +		i, idx := int(indices[j]>>32), int(indices[j]&0xffffffff) +		if da.bc[idx].IsSingleParam() { +			idx := nextIndex(da.bc[idx].Base(), ParamCharacter) +			if idx >= len(da.bc) { +				break +			} +			next := NextSeparator(path, i) +			params := append(params, Param{Value: path[i:next]}) +			if nd, params, found := da.lookup(path[next:], params, idx); found { +				return nd, params, true +			} +		} +		if da.bc[idx].IsWildcardParam() { +			idx := nextIndex(da.bc[idx].Base(), WildcardCharacter) +			params := append(params, Param{Value: path[i:]}) +			return da.node[da.bc[idx].Base()], params, true +		} +	} +	return nil, nil, false +} + +// build builds double-array from records. +func (da *doubleArray) build(srcs []*record, idx, depth int, usedBase map[int]struct{}) error { +	sort.Stable(recordSlice(srcs)) +	base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase) +	if err != nil { +		return err +	} +	if leaf != nil { +		nd, err := makeNode(leaf) +		if err != nil { +			return err +		} +		da.bc[idx].SetBase(len(da.node)) +		da.node = append(da.node, nd) +	} +	for _, sib := range siblings { +		da.setCheck(nextIndex(base, sib.c), sib.c) +	} +	for _, sib := range siblings { +		records := srcs[sib.start:sib.end] +		switch sib.c { +		case ParamCharacter: +			for _, r := range records { +				next := NextSeparator(r.Key, depth+1) +				name := r.Key[depth+1 : next] +				r.paramNames = append(r.paramNames, name) +				r.Key = r.Key[next:] +			} +			da.bc[idx].SetSingleParam() +			if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil { +				return err +			} +		case WildcardCharacter: +			r := records[0] +			name := r.Key[depth+1 : len(r.Key)-1] +			r.paramNames = append(r.paramNames, name) +			r.Key = "" +			da.bc[idx].SetWildcardParam() +			if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil { +				return err +			} +		default: +			if err := da.build(records, nextIndex(base, sib.c), depth+1, usedBase); err != nil { +				return err +			} +		} +	} +	return nil +} + +// setBase sets BASE. +func (da *doubleArray) setBase(i, base int) { +	da.bc[i].SetBase(base) +} + +// setCheck sets CHECK. +func (da *doubleArray) setCheck(i int, check byte) { +	da.bc[i].SetCheck(check) +} + +// findEmptyIndex returns an index of unused BASE/CHECK node. +func (da *doubleArray) findEmptyIndex(start int) int { +	i := start +	for ; i < len(da.bc); i++ { +		if da.bc[i].IsEmpty() { +			break +		} +	} +	return i +} + +// findBase returns good BASE. +func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) { +	for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) { +		base = nextIndex(idx, firstChar) +		if _, used := usedBase[base]; used { +			continue +		} +		i := 0 +		for ; i < len(siblings); i++ { +			next := nextIndex(base, siblings[i].c) +			if len(da.bc) <= next { +				da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...) +			} +			if !da.bc[next].IsEmpty() { +				break +			} +		} +		if i == len(siblings) { +			break +		} +	} +	usedBase[base] = struct{}{} +	return base +} + +func (da *doubleArray) arrange(records []*record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) { +	siblings, leaf, err = makeSiblings(records, depth) +	if err != nil { +		return -1, nil, nil, err +	} +	if len(siblings) < 1 { +		return -1, nil, leaf, nil +	} +	base = da.findBase(siblings, idx, usedBase) +	if base > MaxSize { +		return -1, nil, nil, fmt.Errorf("denco: too many elements of internal slice") +	} +	da.setBase(idx, base) +	return base, siblings, leaf, err +} + +// node represents a node of Double-Array. +type node struct { +	data interface{} + +	// Names of path parameters. +	paramNames []string +} + +// makeNode returns a new node from record. +func makeNode(r *record) (*node, error) { +	dups := make(map[string]bool) +	for _, name := range r.paramNames { +		if dups[name] { +			return nil, fmt.Errorf("denco: path parameter `%v' is duplicated in the key `%v'", name, r.Key) +		} +		dups[name] = true +	} +	return &node{data: r.Value, paramNames: r.paramNames}, nil +} + +// sibling represents an intermediate data of build for Double-Array. +type sibling struct { +	// An index of start of duplicated characters. +	start int + +	// An index of end of duplicated characters. +	end int + +	// A character of sibling. +	c byte +} + +// nextIndex returns a next index of array of BASE/CHECK. +func nextIndex(base int, c byte) int { +	return base ^ int(c) +} + +// makeSiblings returns slice of sibling. +func makeSiblings(records []*record, depth int) (sib []sibling, leaf *record, err error) { +	var ( +		pc byte +		n  int +	) +	for i, r := range records { +		if len(r.Key) <= depth { +			leaf = r +			continue +		} +		c := r.Key[depth] +		switch { +		case pc < c: +			sib = append(sib, sibling{start: i, c: c}) +		case pc == c: +			continue +		default: +			return nil, nil, fmt.Errorf("denco: BUG: routing table hasn't been sorted") +		} +		if n > 0 { +			sib[n-1].end = i +		} +		pc = c +		n++ +	} +	if n == 0 { +		return nil, leaf, nil +	} +	sib[n-1].end = len(records) +	return sib, leaf, nil +} + +// Record represents a record data for router construction. +type Record struct { +	// Key for router construction. +	Key string + +	// Result value for Key. +	Value interface{} +} + +// NewRecord returns a new Record. +func NewRecord(key string, value interface{}) Record { +	return Record{ +		Key:   key, +		Value: value, +	} +} + +// record represents a record that use to build the Double-Array. +type record struct { +	Record +	paramNames []string +} + +// makeRecords returns the records that use to build Double-Arrays. +func makeRecords(srcs []Record) (statics, params []*record) { +	termChar := string(TerminationCharacter) +	paramPrefix := string(SeparatorCharacter) + string(ParamCharacter) +	wildcardPrefix := string(SeparatorCharacter) + string(WildcardCharacter) +	restconfPrefix := string(PathParamCharacter) + string(ParamCharacter) +	for _, r := range srcs { +		if strings.Contains(r.Key, paramPrefix) || strings.Contains(r.Key, wildcardPrefix) ||strings.Contains(r.Key, restconfPrefix){ +			r.Key += termChar +			params = append(params, &record{Record: r}) +		} else { +			statics = append(statics, &record{Record: r}) +		} +	} +	return statics, params +} + +// recordSlice represents a slice of Record for sort and implements the sort.Interface. +type recordSlice []*record + +// Len implements the sort.Interface.Len. +func (rs recordSlice) Len() int { +	return len(rs) +} + +// Less implements the sort.Interface.Less. +func (rs recordSlice) Less(i, j int) bool { +	return rs[i].Key < rs[j].Key +} + +// Swap implements the sort.Interface.Swap. +func (rs recordSlice) Swap(i, j int) { +	rs[i], rs[j] = rs[j], rs[i] +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/denco/server.go b/vendor/github.com/go-openapi/runtime/middleware/denco/server.go new file mode 100644 index 000000000..0886713c1 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/denco/server.go @@ -0,0 +1,106 @@ +package denco + +import ( +	"net/http" +) + +// Mux represents a multiplexer for HTTP request. +type Mux struct{} + +// NewMux returns a new Mux. +func NewMux() *Mux { +	return &Mux{} +} + +// GET is shorthand of Mux.Handler("GET", path, handler). +func (m *Mux) GET(path string, handler HandlerFunc) Handler { +	return m.Handler("GET", path, handler) +} + +// POST is shorthand of Mux.Handler("POST", path, handler). +func (m *Mux) POST(path string, handler HandlerFunc) Handler { +	return m.Handler("POST", path, handler) +} + +// PUT is shorthand of Mux.Handler("PUT", path, handler). +func (m *Mux) PUT(path string, handler HandlerFunc) Handler { +	return m.Handler("PUT", path, handler) +} + +// HEAD is shorthand of Mux.Handler("HEAD", path, handler). +func (m *Mux) HEAD(path string, handler HandlerFunc) Handler { +	return m.Handler("HEAD", path, handler) +} + +// Handler returns a handler for HTTP method. +func (m *Mux) Handler(method, path string, handler HandlerFunc) Handler { +	return Handler{ +		Method: method, +		Path:   path, +		Func:   handler, +	} +} + +// Build builds a http.Handler. +func (m *Mux) Build(handlers []Handler) (http.Handler, error) { +	recordMap := make(map[string][]Record) +	for _, h := range handlers { +		recordMap[h.Method] = append(recordMap[h.Method], NewRecord(h.Path, h.Func)) +	} +	mux := newServeMux() +	for m, records := range recordMap { +		router := New() +		if err := router.Build(records); err != nil { +			return nil, err +		} +		mux.routers[m] = router +	} +	return mux, nil +} + +// Handler represents a handler of HTTP request. +type Handler struct { +	// Method is an HTTP method. +	Method string + +	// Path is a routing path for handler. +	Path string + +	// Func is a function of handler of HTTP request. +	Func HandlerFunc +} + +// The HandlerFunc type is aliased to type of handler function. +type HandlerFunc func(w http.ResponseWriter, r *http.Request, params Params) + +type serveMux struct { +	routers map[string]*Router +} + +func newServeMux() *serveMux { +	return &serveMux{ +		routers: make(map[string]*Router), +	} +} + +// ServeHTTP implements http.Handler interface. +func (mux *serveMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { +	handler, params := mux.handler(r.Method, r.URL.Path) +	handler(w, r, params) +} + +func (mux *serveMux) handler(method, path string) (HandlerFunc, []Param) { +	if router, found := mux.routers[method]; found { +		if handler, params, found := router.Lookup(path); found { +			return handler.(HandlerFunc), params +		} +	} +	return NotFound, nil +} + +// NotFound replies to the request with an HTTP 404 not found error. +// NotFound is called when unknown HTTP method or a handler not found. +// If you want to use the your own NotFound handler, please overwrite this variable. +var NotFound = func(w http.ResponseWriter, r *http.Request, _ Params) { +	http.NotFound(w, r) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/denco/util.go b/vendor/github.com/go-openapi/runtime/middleware/denco/util.go new file mode 100644 index 000000000..edc1f6ab8 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/denco/util.go @@ -0,0 +1,12 @@ +package denco + +// NextSeparator returns an index of next separator in path. +func NextSeparator(path string, start int) int { +	for start < len(path) { +		if c := path[start]; c == '/' || c == TerminationCharacter { +			break +		} +		start++ +	} +	return start +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/doc.go b/vendor/github.com/go-openapi/runtime/middleware/doc.go new file mode 100644 index 000000000..eaf90606a --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/doc.go @@ -0,0 +1,62 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*Package middleware provides the library with helper functions for serving swagger APIs. + +Pseudo middleware handler + +  import ( +  	"net/http" + +  	"github.com/go-openapi/errors" +  ) + +  func newCompleteMiddleware(ctx *Context) http.Handler { +  	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { +  		// use context to lookup routes +  		if matched, ok := ctx.RouteInfo(r); ok { + +  			if matched.NeedsAuth() { +  				if _, err := ctx.Authorize(r, matched); err != nil { +  					ctx.Respond(rw, r, matched.Produces, matched, err) +  					return +  				} +  			} + +  			bound, validation := ctx.BindAndValidate(r, matched) +  			if validation != nil { +  				ctx.Respond(rw, r, matched.Produces, matched, validation) +  				return +  			} + +  			result, err := matched.Handler.Handle(bound) +  			if err != nil { +  				ctx.Respond(rw, r, matched.Produces, matched, err) +  				return +  			} + +  			ctx.Respond(rw, r, matched.Produces, matched, result) +  			return +  		} + +  		// Not found, check if it exists in the other methods first +  		if others := ctx.AllowedMethods(r); len(others) > 0 { +  			ctx.Respond(rw, r, ctx.spec.RequiredProduces(), nil, errors.MethodNotAllowed(r.Method, others)) +  			return +  		} +  		ctx.Respond(rw, r, ctx.spec.RequiredProduces(), nil, errors.NotFound("path %s was not found", r.URL.Path)) +  	}) +  } +*/ +package middleware diff --git a/vendor/github.com/go-openapi/runtime/middleware/go18.go b/vendor/github.com/go-openapi/runtime/middleware/go18.go new file mode 100644 index 000000000..75c762c09 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/go18.go @@ -0,0 +1,9 @@ +// +build go1.8 + +package middleware + +import "net/url" + +func pathUnescape(path string) (string, error) { +	return url.PathUnescape(path) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/header/header.go b/vendor/github.com/go-openapi/runtime/middleware/header/header.go new file mode 100644 index 000000000..e069743e3 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/header/header.go @@ -0,0 +1,329 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd. + +// this file was taken from the github.com/golang/gddo repository + +// Package header provides functions for parsing HTTP headers. +package header + +import ( +	"net/http" +	"strings" +	"time" +) + +// Octet types from RFC 2616. +var octetTypes [256]octetType + +type octetType byte + +const ( +	isToken octetType = 1 << iota +	isSpace +) + +func init() { +	// OCTET      = <any 8-bit sequence of data> +	// CHAR       = <any US-ASCII character (octets 0 - 127)> +	// CTL        = <any US-ASCII control character (octets 0 - 31) and DEL (127)> +	// CR         = <US-ASCII CR, carriage return (13)> +	// LF         = <US-ASCII LF, linefeed (10)> +	// SP         = <US-ASCII SP, space (32)> +	// HT         = <US-ASCII HT, horizontal-tab (9)> +	// <">        = <US-ASCII double-quote mark (34)> +	// CRLF       = CR LF +	// LWS        = [CRLF] 1*( SP | HT ) +	// TEXT       = <any OCTET except CTLs, but including LWS> +	// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> +	//              | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT +	// token      = 1*<any CHAR except CTLs or separators> +	// qdtext     = <any TEXT except <">> + +	for c := 0; c < 256; c++ { +		var t octetType +		isCtl := c <= 31 || c == 127 +		isChar := 0 <= c && c <= 127 +		isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) +		if strings.ContainsRune(" \t\r\n", rune(c)) { +			t |= isSpace +		} +		if isChar && !isCtl && !isSeparator { +			t |= isToken +		} +		octetTypes[c] = t +	} +} + +// Copy returns a shallow copy of the header. +func Copy(header http.Header) http.Header { +	h := make(http.Header) +	for k, vs := range header { +		h[k] = vs +	} +	return h +} + +var timeLayouts = []string{"Mon, 02 Jan 2006 15:04:05 GMT", time.RFC850, time.ANSIC} + +// ParseTime parses the header as time. The zero value is returned if the +// header is not present or there is an error parsing the +// header. +func ParseTime(header http.Header, key string) time.Time { +	if s := header.Get(key); s != "" { +		for _, layout := range timeLayouts { +			if t, err := time.Parse(layout, s); err == nil { +				return t.UTC() +			} +		} +	} +	return time.Time{} +} + +// ParseList parses a comma separated list of values. Commas are ignored in +// quoted strings. Quoted values are not unescaped or unquoted. Whitespace is +// trimmed. +func ParseList(header http.Header, key string) []string { +	var result []string +	for _, s := range header[http.CanonicalHeaderKey(key)] { +		begin := 0 +		end := 0 +		escape := false +		quote := false +		for i := 0; i < len(s); i++ { +			b := s[i] +			switch { +			case escape: +				escape = false +				end = i + 1 +			case quote: +				switch b { +				case '\\': +					escape = true +				case '"': +					quote = false +				} +				end = i + 1 +			case b == '"': +				quote = true +				end = i + 1 +			case octetTypes[b]&isSpace != 0: +				if begin == end { +					begin = i + 1 +					end = begin +				} +			case b == ',': +				if begin < end { +					result = append(result, s[begin:end]) +				} +				begin = i + 1 +				end = begin +			default: +				end = i + 1 +			} +		} +		if begin < end { +			result = append(result, s[begin:end]) +		} +	} +	return result +} + +// ParseValueAndParams parses a comma separated list of values with optional +// semicolon separated name-value pairs. Content-Type and Content-Disposition +// headers are in this format. +func ParseValueAndParams(header http.Header, key string) (string, map[string]string) { +	return parseValueAndParams(header.Get(key)) +} + +func parseValueAndParams(s string) (value string, params map[string]string) { +	params = make(map[string]string) +	value, s = expectTokenSlash(s) +	if value == "" { +		return +	} +	value = strings.ToLower(value) +	s = skipSpace(s) +	for strings.HasPrefix(s, ";") { +		var pkey string +		pkey, s = expectToken(skipSpace(s[1:])) +		if pkey == "" { +			return +		} +		if !strings.HasPrefix(s, "=") { +			return +		} +		var pvalue string +		pvalue, s = expectTokenOrQuoted(s[1:]) +		if pvalue == "" { +			return +		} +		pkey = strings.ToLower(pkey) +		params[pkey] = pvalue +		s = skipSpace(s) +	} +	return +} + +// AcceptSpec ... +type AcceptSpec struct { +	Value string +	Q     float64 +} + +// ParseAccept2 ... +func ParseAccept2(header http.Header, key string) (specs []AcceptSpec) { +	for _, en := range ParseList(header, key) { +		v, p := parseValueAndParams(en) +		var spec AcceptSpec +		spec.Value = v +		spec.Q = 1.0 +		if p != nil { +			if q, ok := p["q"]; ok { +				spec.Q, _ = expectQuality(q) +			} +		} +		if spec.Q < 0.0 { +			continue +		} +		specs = append(specs, spec) +	} + +	return +} + +// ParseAccept parses Accept* headers. +func ParseAccept(header http.Header, key string) (specs []AcceptSpec) { +loop: +	for _, s := range header[key] { +		for { +			var spec AcceptSpec +			spec.Value, s = expectTokenSlash(s) +			if spec.Value == "" { +				continue loop +			} +			spec.Q = 1.0 +			s = skipSpace(s) +			if strings.HasPrefix(s, ";") { +				s = skipSpace(s[1:]) +				for !strings.HasPrefix(s, "q=") && s != "" && !strings.HasPrefix(s, ",") { +					s = skipSpace(s[1:]) +				} +				if strings.HasPrefix(s, "q=") { +					spec.Q, s = expectQuality(s[2:]) +					if spec.Q < 0.0 { +						continue loop +					} +				} +			} +			specs = append(specs, spec) +			s = skipSpace(s) +			if !strings.HasPrefix(s, ",") { +				continue loop +			} +			s = skipSpace(s[1:]) +		} +	} +	return +} + +func skipSpace(s string) (rest string) { +	i := 0 +	for ; i < len(s); i++ { +		if octetTypes[s[i]]&isSpace == 0 { +			break +		} +	} +	return s[i:] +} + +func expectToken(s string) (token, rest string) { +	i := 0 +	for ; i < len(s); i++ { +		if octetTypes[s[i]]&isToken == 0 { +			break +		} +	} +	return s[:i], s[i:] +} + +func expectTokenSlash(s string) (token, rest string) { +	i := 0 +	for ; i < len(s); i++ { +		b := s[i] +		if (octetTypes[b]&isToken == 0) && b != '/' { +			break +		} +	} +	return s[:i], s[i:] +} + +func expectQuality(s string) (q float64, rest string) { +	switch { +	case len(s) == 0: +		return -1, "" +	case s[0] == '0': +		// q is already 0 +		s = s[1:] +	case s[0] == '1': +		s = s[1:] +		q = 1 +	case s[0] == '.': +		// q is already 0 +	default: +		return -1, "" +	} +	if !strings.HasPrefix(s, ".") { +		return q, s +	} +	s = s[1:] +	i := 0 +	n := 0 +	d := 1 +	for ; i < len(s); i++ { +		b := s[i] +		if b < '0' || b > '9' { +			break +		} +		n = n*10 + int(b) - '0' +		d *= 10 +	} +	return q + float64(n)/float64(d), s[i:] +} + +func expectTokenOrQuoted(s string) (value string, rest string) { +	if !strings.HasPrefix(s, "\"") { +		return expectToken(s) +	} +	s = s[1:] +	for i := 0; i < len(s); i++ { +		switch s[i] { +		case '"': +			return s[:i], s[i+1:] +		case '\\': +			p := make([]byte, len(s)-1) +			j := copy(p, s[:i]) +			escape := true +			for i = i + 1; i < len(s); i++ { +				b := s[i] +				switch { +				case escape: +					escape = false +					p[j] = b +					j++ +				case b == '\\': +					escape = true +				case b == '"': +					return string(p[:j]), s[i+1:] +				default: +					p[j] = b +					j++ +				} +			} +			return "", "" +		} +	} +	return "", "" +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/negotiate.go b/vendor/github.com/go-openapi/runtime/middleware/negotiate.go new file mode 100644 index 000000000..a9b6f27d3 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/negotiate.go @@ -0,0 +1,98 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd. + +// this file was taken from the github.com/golang/gddo repository + +package middleware + +import ( +	"net/http" +	"strings" + +	"github.com/go-openapi/runtime/middleware/header" +) + +// NegotiateContentEncoding returns the best offered content encoding for the +// request's Accept-Encoding header. If two offers match with equal weight and +// then the offer earlier in the list is preferred. If no offers are +// acceptable, then "" is returned. +func NegotiateContentEncoding(r *http.Request, offers []string) string { +	bestOffer := "identity" +	bestQ := -1.0 +	specs := header.ParseAccept(r.Header, "Accept-Encoding") +	for _, offer := range offers { +		for _, spec := range specs { +			if spec.Q > bestQ && +				(spec.Value == "*" || spec.Value == offer) { +				bestQ = spec.Q +				bestOffer = offer +			} +		} +	} +	if bestQ == 0 { +		bestOffer = "" +	} +	return bestOffer +} + +// NegotiateContentType returns the best offered content type for the request's +// Accept header. If two offers match with equal weight, then the more specific +// offer is preferred.  For example, text/* trumps */*. If two offers match +// with equal weight and specificity, then the offer earlier in the list is +// preferred. If no offers match, then defaultOffer is returned. +func NegotiateContentType(r *http.Request, offers []string, defaultOffer string) string { +	bestOffer := defaultOffer +	bestQ := -1.0 +	bestWild := 3 +	specs := header.ParseAccept(r.Header, "Accept") +	for _, rawOffer := range offers { +		offer := normalizeOffer(rawOffer) +		// No Accept header: just return the first offer. +		if len(specs) == 0 { +			return rawOffer +		} +		for _, spec := range specs { +			switch { +			case spec.Q == 0.0: +				// ignore +			case spec.Q < bestQ: +				// better match found +			case spec.Value == "*/*": +				if spec.Q > bestQ || bestWild > 2 { +					bestQ = spec.Q +					bestWild = 2 +					bestOffer = rawOffer +				} +			case strings.HasSuffix(spec.Value, "/*"): +				if strings.HasPrefix(offer, spec.Value[:len(spec.Value)-1]) && +					(spec.Q > bestQ || bestWild > 1) { +					bestQ = spec.Q +					bestWild = 1 +					bestOffer = rawOffer +				} +			default: +				if spec.Value == offer && +					(spec.Q > bestQ || bestWild > 0) { +					bestQ = spec.Q +					bestWild = 0 +					bestOffer = rawOffer +				} +			} +		} +	} +	return bestOffer +} + +func normalizeOffers(orig []string) (norm []string) { +	for _, o := range orig { +		norm = append(norm, normalizeOffer(o)) +	} +	return +} + +func normalizeOffer(orig string) string { +	return strings.SplitN(orig, ";", 2)[0] +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/not_implemented.go b/vendor/github.com/go-openapi/runtime/middleware/not_implemented.go new file mode 100644 index 000000000..bc6942a0f --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/not_implemented.go @@ -0,0 +1,67 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package middleware + +import ( +	"net/http" + +	"github.com/go-openapi/runtime" +) + +type errorResp struct { +	code     int +	response interface{} +	headers  http.Header +} + +func (e *errorResp) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { +	for k, v := range e.headers { +		for _, val := range v { +			rw.Header().Add(k, val) +		} +	} +	if e.code > 0 { +		rw.WriteHeader(e.code) +	} else { +		rw.WriteHeader(http.StatusInternalServerError) +	} +	if err := producer.Produce(rw, e.response); err != nil { +		Logger.Printf("failed to write error response: %v", err) +	} +} + +// NotImplemented the error response when the response is not implemented +func NotImplemented(message string) Responder { +	return Error(http.StatusNotImplemented, message) +} + +// Error creates a generic responder for returning errors, the data will be serialized +// with the matching producer for the request +func Error(code int, data interface{}, headers ...http.Header) Responder { +	var hdr http.Header +	for _, h := range headers { +		for k, v := range h { +			if hdr == nil { +				hdr = make(http.Header) +			} +			hdr[k] = v +		} +	} +	return &errorResp{ +		code:     code, +		response: data, +		headers:  hdr, +	} +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/operation.go b/vendor/github.com/go-openapi/runtime/middleware/operation.go new file mode 100644 index 000000000..1175a63cf --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/operation.go @@ -0,0 +1,30 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package middleware + +import "net/http" + +// NewOperationExecutor creates a context aware middleware that handles the operations after routing +func NewOperationExecutor(ctx *Context) http.Handler { +	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { +		// use context to lookup routes +		route, rCtx, _ := ctx.RouteInfo(r) +		if rCtx != nil { +			r = rCtx +		} + +		route.Handler.ServeHTTP(rw, r) +	}) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/parameter.go b/vendor/github.com/go-openapi/runtime/middleware/parameter.go new file mode 100644 index 000000000..9aaf65958 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/parameter.go @@ -0,0 +1,485 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package middleware + +import ( +	"encoding" +	"encoding/base64" +	"fmt" +	"io" +	"net/http" +	"reflect" +	"strconv" + +	"github.com/go-openapi/errors" +	"github.com/go-openapi/spec" +	"github.com/go-openapi/strfmt" +	"github.com/go-openapi/swag" +	"github.com/go-openapi/validate" + +	"github.com/go-openapi/runtime" +) + +const defaultMaxMemory = 32 << 20 + +var textUnmarshalType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() + +func newUntypedParamBinder(param spec.Parameter, spec *spec.Swagger, formats strfmt.Registry) *untypedParamBinder { +	binder := new(untypedParamBinder) +	binder.Name = param.Name +	binder.parameter = ¶m +	binder.formats = formats +	if param.In != "body" { +		binder.validator = validate.NewParamValidator(¶m, formats) +	} else { +		binder.validator = validate.NewSchemaValidator(param.Schema, spec, param.Name, formats) +	} + +	return binder +} + +type untypedParamBinder struct { +	parameter *spec.Parameter +	formats   strfmt.Registry +	Name      string +	validator validate.EntityValidator +} + +func (p *untypedParamBinder) Type() reflect.Type { +	return p.typeForSchema(p.parameter.Type, p.parameter.Format, p.parameter.Items) +} + +func (p *untypedParamBinder) typeForSchema(tpe, format string, items *spec.Items) reflect.Type { +	switch tpe { +	case "boolean": +		return reflect.TypeOf(true) + +	case "string": +		if tt, ok := p.formats.GetType(format); ok { +			return tt +		} +		return reflect.TypeOf("") + +	case "integer": +		switch format { +		case "int8": +			return reflect.TypeOf(int8(0)) +		case "int16": +			return reflect.TypeOf(int16(0)) +		case "int32": +			return reflect.TypeOf(int32(0)) +		case "int64": +			return reflect.TypeOf(int64(0)) +		default: +			return reflect.TypeOf(int64(0)) +		} + +	case "number": +		switch format { +		case "float": +			return reflect.TypeOf(float32(0)) +		case "double": +			return reflect.TypeOf(float64(0)) +		} + +	case "array": +		if items == nil { +			return nil +		} +		itemsType := p.typeForSchema(items.Type, items.Format, items.Items) +		if itemsType == nil { +			return nil +		} +		return reflect.MakeSlice(reflect.SliceOf(itemsType), 0, 0).Type() + +	case "file": +		return reflect.TypeOf(&runtime.File{}).Elem() + +	case "object": +		return reflect.TypeOf(map[string]interface{}{}) +	} +	return nil +} + +func (p *untypedParamBinder) allowsMulti() bool { +	return p.parameter.In == "query" || p.parameter.In == "formData" +} + +func (p *untypedParamBinder) readValue(values runtime.Gettable, target reflect.Value) ([]string, bool, bool, error) { +	name, in, cf, tpe := p.parameter.Name, p.parameter.In, p.parameter.CollectionFormat, p.parameter.Type +	if tpe == "array" { +		if cf == "multi" { +			if !p.allowsMulti() { +				return nil, false, false, errors.InvalidCollectionFormat(name, in, cf) +			} +			vv, hasKey, _ := values.GetOK(name) +			return vv, false, hasKey, nil +		} + +		v, hk, hv := values.GetOK(name) +		if !hv { +			return nil, false, hk, nil +		} +		d, c, e := p.readFormattedSliceFieldValue(v[len(v)-1], target) +		return d, c, hk, e +	} + +	vv, hk, _ := values.GetOK(name) +	return vv, false, hk, nil +} + +func (p *untypedParamBinder) Bind(request *http.Request, routeParams RouteParams, consumer runtime.Consumer, target reflect.Value) error { +	// fmt.Println("binding", p.name, "as", p.Type()) +	switch p.parameter.In { +	case "query": +		data, custom, hasKey, err := p.readValue(runtime.Values(request.URL.Query()), target) +		if err != nil { +			return err +		} +		if custom { +			return nil +		} + +		return p.bindValue(data, hasKey, target) + +	case "header": +		data, custom, hasKey, err := p.readValue(runtime.Values(request.Header), target) +		if err != nil { +			return err +		} +		if custom { +			return nil +		} +		return p.bindValue(data, hasKey, target) + +	case "path": +		data, custom, hasKey, err := p.readValue(routeParams, target) +		if err != nil { +			return err +		} +		if custom { +			return nil +		} +		return p.bindValue(data, hasKey, target) + +	case "formData": +		var err error +		var mt string + +		mt, _, e := runtime.ContentType(request.Header) +		if e != nil { +			// because of the interface conversion go thinks the error is not nil +			// so we first check for nil and then set the err var if it's not nil +			err = e +		} + +		if err != nil { +			return errors.InvalidContentType("", []string{"multipart/form-data", "application/x-www-form-urlencoded"}) +		} + +		if mt != "multipart/form-data" && mt != "application/x-www-form-urlencoded" { +			return errors.InvalidContentType(mt, []string{"multipart/form-data", "application/x-www-form-urlencoded"}) +		} + +		if mt == "multipart/form-data" { +			if err = request.ParseMultipartForm(defaultMaxMemory); err != nil { +				return errors.NewParseError(p.Name, p.parameter.In, "", err) +			} +		} + +		if err = request.ParseForm(); err != nil { +			return errors.NewParseError(p.Name, p.parameter.In, "", err) +		} + +		if p.parameter.Type == "file" { +			file, header, ffErr := request.FormFile(p.parameter.Name) +			if ffErr != nil { +				if p.parameter.Required { +					return errors.NewParseError(p.Name, p.parameter.In, "", ffErr) +				} else { +					return nil +				} +			} +			target.Set(reflect.ValueOf(runtime.File{Data: file, Header: header})) +			return nil +		} + +		if request.MultipartForm != nil { +			data, custom, hasKey, rvErr := p.readValue(runtime.Values(request.MultipartForm.Value), target) +			if rvErr != nil { +				return rvErr +			} +			if custom { +				return nil +			} +			return p.bindValue(data, hasKey, target) +		} +		data, custom, hasKey, err := p.readValue(runtime.Values(request.PostForm), target) +		if err != nil { +			return err +		} +		if custom { +			return nil +		} +		return p.bindValue(data, hasKey, target) + +	case "body": +		newValue := reflect.New(target.Type()) +		if !runtime.HasBody(request) { +			if p.parameter.Default != nil { +				target.Set(reflect.ValueOf(p.parameter.Default)) +			} + +			return nil +		} +		if err := consumer.Consume(request.Body, newValue.Interface()); err != nil { +			if err == io.EOF && p.parameter.Default != nil { +				target.Set(reflect.ValueOf(p.parameter.Default)) +				return nil +			} +			tpe := p.parameter.Type +			if p.parameter.Format != "" { +				tpe = p.parameter.Format +			} +			return errors.InvalidType(p.Name, p.parameter.In, tpe, nil) +		} +		target.Set(reflect.Indirect(newValue)) +		return nil +	default: +		return errors.New(500, fmt.Sprintf("invalid parameter location %q", p.parameter.In)) +	} +} + +func (p *untypedParamBinder) bindValue(data []string, hasKey bool, target reflect.Value) error { +	if p.parameter.Type == "array" { +		return p.setSliceFieldValue(target, p.parameter.Default, data, hasKey) +	} +	var d string +	if len(data) > 0 { +		d = data[len(data)-1] +	} +	return p.setFieldValue(target, p.parameter.Default, d, hasKey) +} + +func (p *untypedParamBinder) setFieldValue(target reflect.Value, defaultValue interface{}, data string, hasKey bool) error { +	tpe := p.parameter.Type +	if p.parameter.Format != "" { +		tpe = p.parameter.Format +	} + +	if (!hasKey || (!p.parameter.AllowEmptyValue && data == "")) && p.parameter.Required && p.parameter.Default == nil { +		return errors.Required(p.Name, p.parameter.In, data) +	} + +	ok, err := p.tryUnmarshaler(target, defaultValue, data) +	if err != nil { +		return errors.InvalidType(p.Name, p.parameter.In, tpe, data) +	} +	if ok { +		return nil +	} + +	defVal := reflect.Zero(target.Type()) +	if defaultValue != nil { +		defVal = reflect.ValueOf(defaultValue) +	} + +	if tpe == "byte" { +		if data == "" { +			if target.CanSet() { +				target.SetBytes(defVal.Bytes()) +			} +			return nil +		} + +		b, err := base64.StdEncoding.DecodeString(data) +		if err != nil { +			b, err = base64.URLEncoding.DecodeString(data) +			if err != nil { +				return errors.InvalidType(p.Name, p.parameter.In, tpe, data) +			} +		} +		if target.CanSet() { +			target.SetBytes(b) +		} +		return nil +	} + +	switch target.Kind() { +	case reflect.Bool: +		if data == "" { +			if target.CanSet() { +				target.SetBool(defVal.Bool()) +			} +			return nil +		} +		b, err := swag.ConvertBool(data) +		if err != nil { +			return err +		} +		if target.CanSet() { +			target.SetBool(b) +		} +	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +		if data == "" { +			if target.CanSet() { +				rd := defVal.Convert(reflect.TypeOf(int64(0))) +				target.SetInt(rd.Int()) +			} +			return nil +		} +		i, err := strconv.ParseInt(data, 10, 64) +		if err != nil { +			return errors.InvalidType(p.Name, p.parameter.In, tpe, data) +		} +		if target.OverflowInt(i) { +			return errors.InvalidType(p.Name, p.parameter.In, tpe, data) +		} +		if target.CanSet() { +			target.SetInt(i) +		} + +	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: +		if data == "" { +			if target.CanSet() { +				rd := defVal.Convert(reflect.TypeOf(uint64(0))) +				target.SetUint(rd.Uint()) +			} +			return nil +		} +		u, err := strconv.ParseUint(data, 10, 64) +		if err != nil { +			return errors.InvalidType(p.Name, p.parameter.In, tpe, data) +		} +		if target.OverflowUint(u) { +			return errors.InvalidType(p.Name, p.parameter.In, tpe, data) +		} +		if target.CanSet() { +			target.SetUint(u) +		} + +	case reflect.Float32, reflect.Float64: +		if data == "" { +			if target.CanSet() { +				rd := defVal.Convert(reflect.TypeOf(float64(0))) +				target.SetFloat(rd.Float()) +			} +			return nil +		} +		f, err := strconv.ParseFloat(data, 64) +		if err != nil { +			return errors.InvalidType(p.Name, p.parameter.In, tpe, data) +		} +		if target.OverflowFloat(f) { +			return errors.InvalidType(p.Name, p.parameter.In, tpe, data) +		} +		if target.CanSet() { +			target.SetFloat(f) +		} + +	case reflect.String: +		value := data +		if value == "" { +			value = defVal.String() +		} +		// validate string +		if target.CanSet() { +			target.SetString(value) +		} + +	case reflect.Ptr: +		if data == "" && defVal.Kind() == reflect.Ptr { +			if target.CanSet() { +				target.Set(defVal) +			} +			return nil +		} +		newVal := reflect.New(target.Type().Elem()) +		if err := p.setFieldValue(reflect.Indirect(newVal), defVal, data, hasKey); err != nil { +			return err +		} +		if target.CanSet() { +			target.Set(newVal) +		} + +	default: +		return errors.InvalidType(p.Name, p.parameter.In, tpe, data) +	} +	return nil +} + +func (p *untypedParamBinder) tryUnmarshaler(target reflect.Value, defaultValue interface{}, data string) (bool, error) { +	if !target.CanSet() { +		return false, nil +	} +	// When a type implements encoding.TextUnmarshaler we'll use that instead of reflecting some more +	if reflect.PtrTo(target.Type()).Implements(textUnmarshalType) { +		if defaultValue != nil && len(data) == 0 { +			target.Set(reflect.ValueOf(defaultValue)) +			return true, nil +		} +		value := reflect.New(target.Type()) +		if err := value.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(data)); err != nil { +			return true, err +		} +		target.Set(reflect.Indirect(value)) +		return true, nil +	} +	return false, nil +} + +func (p *untypedParamBinder) readFormattedSliceFieldValue(data string, target reflect.Value) ([]string, bool, error) { +	ok, err := p.tryUnmarshaler(target, p.parameter.Default, data) +	if err != nil { +		return nil, true, err +	} +	if ok { +		return nil, true, nil +	} + +	return swag.SplitByFormat(data, p.parameter.CollectionFormat), false, nil +} + +func (p *untypedParamBinder) setSliceFieldValue(target reflect.Value, defaultValue interface{}, data []string, hasKey bool) error { +	sz := len(data) +	if (!hasKey || (!p.parameter.AllowEmptyValue && (sz == 0 || (sz == 1 && data[0] == "")))) && p.parameter.Required && defaultValue == nil { +		return errors.Required(p.Name, p.parameter.In, data) +	} + +	defVal := reflect.Zero(target.Type()) +	if defaultValue != nil { +		defVal = reflect.ValueOf(defaultValue) +	} + +	if !target.CanSet() { +		return nil +	} +	if sz == 0 { +		target.Set(defVal) +		return nil +	} + +	value := reflect.MakeSlice(reflect.SliceOf(target.Type().Elem()), sz, sz) + +	for i := 0; i < sz; i++ { +		if err := p.setFieldValue(value.Index(i), nil, data[i], hasKey); err != nil { +			return err +		} +	} + +	target.Set(value) + +	return nil +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/pre_go18.go b/vendor/github.com/go-openapi/runtime/middleware/pre_go18.go new file mode 100644 index 000000000..03385251e --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/pre_go18.go @@ -0,0 +1,9 @@ +// +build !go1.8 + +package middleware + +import "net/url" + +func pathUnescape(path string) (string, error) { +	return url.QueryUnescape(path) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/rapidoc.go b/vendor/github.com/go-openapi/runtime/middleware/rapidoc.go new file mode 100644 index 000000000..4be330d6d --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/rapidoc.go @@ -0,0 +1,90 @@ +package middleware + +import ( +	"bytes" +	"fmt" +	"html/template" +	"net/http" +	"path" +) + +// RapiDocOpts configures the RapiDoc middlewares +type RapiDocOpts struct { +	// BasePath for the UI path, defaults to: / +	BasePath string +	// Path combines with BasePath for the full UI path, defaults to: docs +	Path string +	// SpecURL the url to find the spec for +	SpecURL string +	// RapiDocURL for the js that generates the rapidoc site, defaults to: https://cdn.jsdelivr.net/npm/rapidoc/bundles/rapidoc.standalone.js +	RapiDocURL string +	// Title for the documentation site, default to: API documentation +	Title string +} + +// EnsureDefaults in case some options are missing +func (r *RapiDocOpts) EnsureDefaults() { +	if r.BasePath == "" { +		r.BasePath = "/" +	} +	if r.Path == "" { +		r.Path = "docs" +	} +	if r.SpecURL == "" { +		r.SpecURL = "/swagger.json" +	} +	if r.RapiDocURL == "" { +		r.RapiDocURL = rapidocLatest +	} +	if r.Title == "" { +		r.Title = "API documentation" +	} +} + +// RapiDoc creates a middleware to serve a documentation site for a swagger spec. +// This allows for altering the spec before starting the http listener. +// +func RapiDoc(opts RapiDocOpts, next http.Handler) http.Handler { +	opts.EnsureDefaults() + +	pth := path.Join(opts.BasePath, opts.Path) +	tmpl := template.Must(template.New("rapidoc").Parse(rapidocTemplate)) + +	buf := bytes.NewBuffer(nil) +	_ = tmpl.Execute(buf, opts) +	b := buf.Bytes() + +	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { +		if r.URL.Path == pth { +			rw.Header().Set("Content-Type", "text/html; charset=utf-8") +			rw.WriteHeader(http.StatusOK) + +			_, _ = rw.Write(b) +			return +		} + +		if next == nil { +			rw.Header().Set("Content-Type", "text/plain") +			rw.WriteHeader(http.StatusNotFound) +			_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth))) +			return +		} +		next.ServeHTTP(rw, r) +	}) +} + +const ( +	rapidocLatest   = "https://unpkg.com/rapidoc/dist/rapidoc-min.js" +	rapidocTemplate = `<!doctype html> +<html> +<head> +  <title>{{ .Title }}</title> +  <meta charset="utf-8"> <!-- Important: rapi-doc uses utf8 charecters --> +  <script type="module" src="{{ .RapiDocURL }}"></script> +</head> +<body> +  <rapi-doc spec-url="{{ .SpecURL }}"></rapi-doc> +</body> +</html> +` +) diff --git a/vendor/github.com/go-openapi/runtime/middleware/redoc.go b/vendor/github.com/go-openapi/runtime/middleware/redoc.go new file mode 100644 index 000000000..019c85429 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/redoc.go @@ -0,0 +1,103 @@ +package middleware + +import ( +	"bytes" +	"fmt" +	"html/template" +	"net/http" +	"path" +) + +// RedocOpts configures the Redoc middlewares +type RedocOpts struct { +	// BasePath for the UI path, defaults to: / +	BasePath string +	// Path combines with BasePath for the full UI path, defaults to: docs +	Path string +	// SpecURL the url to find the spec for +	SpecURL string +	// RedocURL for the js that generates the redoc site, defaults to: https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js +	RedocURL string +	// Title for the documentation site, default to: API documentation +	Title string +} + +// EnsureDefaults in case some options are missing +func (r *RedocOpts) EnsureDefaults() { +	if r.BasePath == "" { +		r.BasePath = "/" +	} +	if r.Path == "" { +		r.Path = "docs" +	} +	if r.SpecURL == "" { +		r.SpecURL = "/swagger.json" +	} +	if r.RedocURL == "" { +		r.RedocURL = redocLatest +	} +	if r.Title == "" { +		r.Title = "API documentation" +	} +} + +// Redoc creates a middleware to serve a documentation site for a swagger spec. +// This allows for altering the spec before starting the http listener. +// +func Redoc(opts RedocOpts, next http.Handler) http.Handler { +	opts.EnsureDefaults() + +	pth := path.Join(opts.BasePath, opts.Path) +	tmpl := template.Must(template.New("redoc").Parse(redocTemplate)) + +	buf := bytes.NewBuffer(nil) +	_ = tmpl.Execute(buf, opts) +	b := buf.Bytes() + +	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { +		if r.URL.Path == pth { +			rw.Header().Set("Content-Type", "text/html; charset=utf-8") +			rw.WriteHeader(http.StatusOK) + +			_, _ = rw.Write(b) +			return +		} + +		if next == nil { +			rw.Header().Set("Content-Type", "text/plain") +			rw.WriteHeader(http.StatusNotFound) +			_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth))) +			return +		} +		next.ServeHTTP(rw, r) +	}) +} + +const ( +	redocLatest   = "https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js" +	redocTemplate = `<!DOCTYPE html> +<html> +  <head> +    <title>{{ .Title }}</title> +		<!-- needed for adaptive design --> +		<meta charset="utf-8"/> +		<meta name="viewport" content="width=device-width, initial-scale=1"> +		<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet"> + +    <!-- +    ReDoc doesn't change outer page styles +    --> +    <style> +      body { +        margin: 0; +        padding: 0; +      } +    </style> +  </head> +  <body> +    <redoc spec-url='{{ .SpecURL }}'></redoc> +    <script src="{{ .RedocURL }}"> </script> +  </body> +</html> +` +) diff --git a/vendor/github.com/go-openapi/runtime/middleware/request.go b/vendor/github.com/go-openapi/runtime/middleware/request.go new file mode 100644 index 000000000..760c37861 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/request.go @@ -0,0 +1,104 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package middleware + +import ( +	"net/http" +	"reflect" + +	"github.com/go-openapi/errors" +	"github.com/go-openapi/spec" +	"github.com/go-openapi/strfmt" + +	"github.com/go-openapi/runtime" +) + +// UntypedRequestBinder binds and validates the data from a http request +type UntypedRequestBinder struct { +	Spec         *spec.Swagger +	Parameters   map[string]spec.Parameter +	Formats      strfmt.Registry +	paramBinders map[string]*untypedParamBinder +} + +// NewUntypedRequestBinder creates a new binder for reading a request. +func NewUntypedRequestBinder(parameters map[string]spec.Parameter, spec *spec.Swagger, formats strfmt.Registry) *UntypedRequestBinder { +	binders := make(map[string]*untypedParamBinder) +	for fieldName, param := range parameters { +		binders[fieldName] = newUntypedParamBinder(param, spec, formats) +	} +	return &UntypedRequestBinder{ +		Parameters:   parameters, +		paramBinders: binders, +		Spec:         spec, +		Formats:      formats, +	} +} + +// Bind perform the databinding and validation +func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RouteParams, consumer runtime.Consumer, data interface{}) error { +	val := reflect.Indirect(reflect.ValueOf(data)) +	isMap := val.Kind() == reflect.Map +	var result []error +	debugLog("binding %d parameters for %s %s", len(o.Parameters), request.Method, request.URL.EscapedPath()) +	for fieldName, param := range o.Parameters { +		binder := o.paramBinders[fieldName] +		debugLog("binding parameter %s for %s %s", fieldName, request.Method, request.URL.EscapedPath()) +		var target reflect.Value +		if !isMap { +			binder.Name = fieldName +			target = val.FieldByName(fieldName) +		} + +		if isMap { +			tpe := binder.Type() +			if tpe == nil { +				if param.Schema.Type.Contains("array") { +					tpe = reflect.TypeOf([]interface{}{}) +				} else { +					tpe = reflect.TypeOf(map[string]interface{}{}) +				} +			} +			target = reflect.Indirect(reflect.New(tpe)) +		} + +		if !target.IsValid() { +			result = append(result, errors.New(500, "parameter name %q is an unknown field", binder.Name)) +			continue +		} + +		if err := binder.Bind(request, routeParams, consumer, target); err != nil { +			result = append(result, err) +			continue +		} + +		if binder.validator != nil { +			rr := binder.validator.Validate(target.Interface()) +			if rr != nil && rr.HasErrors() { +				result = append(result, rr.AsError()) +			} +		} + +		if isMap { +			val.SetMapIndex(reflect.ValueOf(param.Name), target) +		} +	} + +	if len(result) > 0 { +		return errors.CompositeValidationError(result...) +	} + +	return nil +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/router.go b/vendor/github.com/go-openapi/runtime/middleware/router.go new file mode 100644 index 000000000..5052031c8 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/router.go @@ -0,0 +1,488 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package middleware + +import ( +	"fmt" +	"net/http" +	fpath "path" +	"regexp" +	"strings" + +	"github.com/go-openapi/runtime/security" +	"github.com/go-openapi/swag" + +	"github.com/go-openapi/analysis" +	"github.com/go-openapi/errors" +	"github.com/go-openapi/loads" +	"github.com/go-openapi/spec" +	"github.com/go-openapi/strfmt" + +	"github.com/go-openapi/runtime" +	"github.com/go-openapi/runtime/middleware/denco" +) + +// RouteParam is a object to capture route params in a framework agnostic way. +// implementations of the muxer should use these route params to communicate with the +// swagger framework +type RouteParam struct { +	Name  string +	Value string +} + +// RouteParams the collection of route params +type RouteParams []RouteParam + +// Get gets the value for the route param for the specified key +func (r RouteParams) Get(name string) string { +	vv, _, _ := r.GetOK(name) +	if len(vv) > 0 { +		return vv[len(vv)-1] +	} +	return "" +} + +// GetOK gets the value but also returns booleans to indicate if a key or value +// is present. This aids in validation and satisfies an interface in use there +// +// The returned values are: data, has key, has value +func (r RouteParams) GetOK(name string) ([]string, bool, bool) { +	for _, p := range r { +		if p.Name == name { +			return []string{p.Value}, true, p.Value != "" +		} +	} +	return nil, false, false +} + +// NewRouter creates a new context aware router middleware +func NewRouter(ctx *Context, next http.Handler) http.Handler { +	if ctx.router == nil { +		ctx.router = DefaultRouter(ctx.spec, ctx.api) +	} + +	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { +		if _, rCtx, ok := ctx.RouteInfo(r); ok { +			next.ServeHTTP(rw, rCtx) +			return +		} + +		// Not found, check if it exists in the other methods first +		if others := ctx.AllowedMethods(r); len(others) > 0 { +			ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.MethodNotAllowed(r.Method, others)) +			return +		} + +		ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.NotFound("path %s was not found", r.URL.EscapedPath())) +	}) +} + +// RoutableAPI represents an interface for things that can serve +// as a provider of implementations for the swagger router +type RoutableAPI interface { +	HandlerFor(string, string) (http.Handler, bool) +	ServeErrorFor(string) func(http.ResponseWriter, *http.Request, error) +	ConsumersFor([]string) map[string]runtime.Consumer +	ProducersFor([]string) map[string]runtime.Producer +	AuthenticatorsFor(map[string]spec.SecurityScheme) map[string]runtime.Authenticator +	Authorizer() runtime.Authorizer +	Formats() strfmt.Registry +	DefaultProduces() string +	DefaultConsumes() string +} + +// Router represents a swagger aware router +type Router interface { +	Lookup(method, path string) (*MatchedRoute, bool) +	OtherMethods(method, path string) []string +} + +type defaultRouteBuilder struct { +	spec     *loads.Document +	analyzer *analysis.Spec +	api      RoutableAPI +	records  map[string][]denco.Record +} + +type defaultRouter struct { +	spec    *loads.Document +	routers map[string]*denco.Router +} + +func newDefaultRouteBuilder(spec *loads.Document, api RoutableAPI) *defaultRouteBuilder { +	return &defaultRouteBuilder{ +		spec:     spec, +		analyzer: analysis.New(spec.Spec()), +		api:      api, +		records:  make(map[string][]denco.Record), +	} +} + +// DefaultRouter creates a default implemenation of the router +func DefaultRouter(spec *loads.Document, api RoutableAPI) Router { +	builder := newDefaultRouteBuilder(spec, api) +	if spec != nil { +		for method, paths := range builder.analyzer.Operations() { +			for path, operation := range paths { +				fp := fpath.Join(spec.BasePath(), path) +				debugLog("adding route %s %s %q", method, fp, operation.ID) +				builder.AddRoute(method, fp, operation) +			} +		} +	} +	return builder.Build() +} + +// RouteAuthenticator is an authenticator that can compose several authenticators together. +// It also knows when it contains an authenticator that allows for anonymous pass through. +// Contains a group of 1 or more authenticators that have a logical AND relationship +type RouteAuthenticator struct { +	Authenticator  map[string]runtime.Authenticator +	Schemes        []string +	Scopes         map[string][]string +	allScopes      []string +	commonScopes   []string +	allowAnonymous bool +} + +func (ra *RouteAuthenticator) AllowsAnonymous() bool { +	return ra.allowAnonymous +} + +// AllScopes returns a list of unique scopes that is the combination +// of all the scopes in the requirements +func (ra *RouteAuthenticator) AllScopes() []string { +	return ra.allScopes +} + +// CommonScopes returns a list of unique scopes that are common in all the +// scopes in the requirements +func (ra *RouteAuthenticator) CommonScopes() []string { +	return ra.commonScopes +} + +// Authenticate Authenticator interface implementation +func (ra *RouteAuthenticator) Authenticate(req *http.Request, route *MatchedRoute) (bool, interface{}, error) { +	if ra.allowAnonymous { +		route.Authenticator = ra +		return true, nil, nil +	} +	// iterate in proper order +	var lastResult interface{} +	for _, scheme := range ra.Schemes { +		if authenticator, ok := ra.Authenticator[scheme]; ok { +			applies, princ, err := authenticator.Authenticate(&security.ScopedAuthRequest{ +				Request:        req, +				RequiredScopes: ra.Scopes[scheme], +			}) +			if !applies { +				return false, nil, nil +			} +			if err != nil { +				route.Authenticator = ra +				return true, nil, err +			} +			lastResult = princ +		} +	} +	route.Authenticator = ra +	return true, lastResult, nil +} + +func stringSliceUnion(slices ...[]string) []string { +	unique := make(map[string]struct{}) +	var result []string +	for _, slice := range slices { +		for _, entry := range slice { +			if _, ok := unique[entry]; ok { +				continue +			} +			unique[entry] = struct{}{} +			result = append(result, entry) +		} +	} +	return result +} + +func stringSliceIntersection(slices ...[]string) []string { +	unique := make(map[string]int) +	var intersection []string + +	total := len(slices) +	var emptyCnt int +	for _, slice := range slices { +		if len(slice) == 0 { +			emptyCnt++ +			continue +		} + +		for _, entry := range slice { +			unique[entry]++ +			if unique[entry] == total-emptyCnt { // this entry appeared in all the non-empty slices +				intersection = append(intersection, entry) +			} +		} +	} + +	return intersection +} + +// RouteAuthenticators represents a group of authenticators that represent a logical OR +type RouteAuthenticators []RouteAuthenticator + +// AllowsAnonymous returns true when there is an authenticator that means optional auth +func (ras RouteAuthenticators) AllowsAnonymous() bool { +	for _, ra := range ras { +		if ra.AllowsAnonymous() { +			return true +		} +	} +	return false +} + +// Authenticate method implemention so this collection can be used as authenticator +func (ras RouteAuthenticators) Authenticate(req *http.Request, route *MatchedRoute) (bool, interface{}, error) { +	var lastError error +	var allowsAnon bool +	var anonAuth RouteAuthenticator + +	for _, ra := range ras { +		if ra.AllowsAnonymous() { +			anonAuth = ra +			allowsAnon = true +			continue +		} +		applies, usr, err := ra.Authenticate(req, route) +		if !applies || err != nil || usr == nil { +			if err != nil { +				lastError = err +			} +			continue +		} +		return applies, usr, nil +	} + +	if allowsAnon && lastError == nil { +		route.Authenticator = &anonAuth +		return true, nil, lastError +	} +	return lastError != nil, nil, lastError +} + +type routeEntry struct { +	PathPattern    string +	BasePath       string +	Operation      *spec.Operation +	Consumes       []string +	Consumers      map[string]runtime.Consumer +	Produces       []string +	Producers      map[string]runtime.Producer +	Parameters     map[string]spec.Parameter +	Handler        http.Handler +	Formats        strfmt.Registry +	Binder         *UntypedRequestBinder +	Authenticators RouteAuthenticators +	Authorizer     runtime.Authorizer +} + +// MatchedRoute represents the route that was matched in this request +type MatchedRoute struct { +	routeEntry +	Params        RouteParams +	Consumer      runtime.Consumer +	Producer      runtime.Producer +	Authenticator *RouteAuthenticator +} + +// HasAuth returns true when the route has a security requirement defined +func (m *MatchedRoute) HasAuth() bool { +	return len(m.Authenticators) > 0 +} + +// NeedsAuth returns true when the request still +// needs to perform authentication +func (m *MatchedRoute) NeedsAuth() bool { +	return m.HasAuth() && m.Authenticator == nil +} + +func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) { +	mth := strings.ToUpper(method) +	debugLog("looking up route for %s %s", method, path) +	if Debug { +		if len(d.routers) == 0 { +			debugLog("there are no known routers") +		} +		for meth := range d.routers { +			debugLog("got a router for %s", meth) +		} +	} +	if router, ok := d.routers[mth]; ok { +		if m, rp, ok := router.Lookup(fpath.Clean(path)); ok && m != nil { +			if entry, ok := m.(*routeEntry); ok { +				debugLog("found a route for %s %s with %d parameters", method, path, len(entry.Parameters)) +				var params RouteParams +				for _, p := range rp { +					v, err := pathUnescape(p.Value) +					if err != nil { +						debugLog("failed to escape %q: %v", p.Value, err) +						v = p.Value +					} +					// a workaround to handle fragment/composing parameters until they are supported in denco router +					// check if this parameter is a fragment within a path segment +					if xpos := strings.Index(entry.PathPattern, fmt.Sprintf("{%s}", p.Name)) + len(p.Name) + 2; xpos < len(entry.PathPattern) && entry.PathPattern[xpos] != '/' { +						// extract fragment parameters +						ep := strings.Split(entry.PathPattern[xpos:], "/")[0] +						pnames, pvalues := decodeCompositParams(p.Name, v, ep, nil, nil) +						for i, pname := range pnames { +							params = append(params, RouteParam{Name: pname, Value: pvalues[i]}) +						} +					} else { +						// use the parameter directly +						params = append(params, RouteParam{Name: p.Name, Value: v}) +					} +				} +				return &MatchedRoute{routeEntry: *entry, Params: params}, true +			} +		} else { +			debugLog("couldn't find a route by path for %s %s", method, path) +		} +	} else { +		debugLog("couldn't find a route by method for %s %s", method, path) +	} +	return nil, false +} + +func (d *defaultRouter) OtherMethods(method, path string) []string { +	mn := strings.ToUpper(method) +	var methods []string +	for k, v := range d.routers { +		if k != mn { +			if _, _, ok := v.Lookup(fpath.Clean(path)); ok { +				methods = append(methods, k) +				continue +			} +		} +	} +	return methods +} + +// convert swagger parameters per path segment into a denco parameter as multiple parameters per segment are not supported in denco +var pathConverter = regexp.MustCompile(`{(.+?)}([^/]*)`) + +func decodeCompositParams(name string, value string, pattern string, names []string, values []string) ([]string, []string) { +	pleft := strings.Index(pattern, "{") +	names = append(names, name) +	if pleft < 0 { +		if strings.HasSuffix(value, pattern) { +			values = append(values, value[:len(value)-len(pattern)]) +		} else { +			values = append(values, "") +		} +	} else { +		toskip := pattern[:pleft] +		pright := strings.Index(pattern, "}") +		vright := strings.Index(value, toskip) +		if vright >= 0 { +			values = append(values, value[:vright]) +		} else { +			values = append(values, "") +			value = "" +		} +		return decodeCompositParams(pattern[pleft+1:pright], value[vright+len(toskip):], pattern[pright+1:], names, values) +	} +	return names, values +} + +func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Operation) { +	mn := strings.ToUpper(method) + +	bp := fpath.Clean(d.spec.BasePath()) +	if len(bp) > 0 && bp[len(bp)-1] == '/' { +		bp = bp[:len(bp)-1] +	} + +	debugLog("operation: %#v", *operation) +	if handler, ok := d.api.HandlerFor(method, strings.TrimPrefix(path, bp)); ok { +		consumes := d.analyzer.ConsumesFor(operation) +		produces := d.analyzer.ProducesFor(operation) +		parameters := d.analyzer.ParamsFor(method, strings.TrimPrefix(path, bp)) + +		// add API defaults if not part of the spec +		if defConsumes := d.api.DefaultConsumes(); defConsumes != "" && !swag.ContainsStringsCI(consumes, defConsumes) { +			consumes = append(consumes, defConsumes) +		} + +		if defProduces := d.api.DefaultProduces(); defProduces != "" && !swag.ContainsStringsCI(produces, defProduces) { +			produces = append(produces, defProduces) +		} + +		record := denco.NewRecord(pathConverter.ReplaceAllString(path, ":$1"), &routeEntry{ +			BasePath:       bp, +			PathPattern:    path, +			Operation:      operation, +			Handler:        handler, +			Consumes:       consumes, +			Produces:       produces, +			Consumers:      d.api.ConsumersFor(normalizeOffers(consumes)), +			Producers:      d.api.ProducersFor(normalizeOffers(produces)), +			Parameters:     parameters, +			Formats:        d.api.Formats(), +			Binder:         NewUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()), +			Authenticators: d.buildAuthenticators(operation), +			Authorizer:     d.api.Authorizer(), +		}) +		d.records[mn] = append(d.records[mn], record) +	} +} + +func (d *defaultRouteBuilder) buildAuthenticators(operation *spec.Operation) RouteAuthenticators { +	requirements := d.analyzer.SecurityRequirementsFor(operation) +	var auths []RouteAuthenticator +	for _, reqs := range requirements { +		var schemes []string +		scopes := make(map[string][]string, len(reqs)) +		var scopeSlices [][]string +		for _, req := range reqs { +			schemes = append(schemes, req.Name) +			scopes[req.Name] = req.Scopes +			scopeSlices = append(scopeSlices, req.Scopes) +		} + +		definitions := d.analyzer.SecurityDefinitionsForRequirements(reqs) +		authenticators := d.api.AuthenticatorsFor(definitions) +		auths = append(auths, RouteAuthenticator{ +			Authenticator:  authenticators, +			Schemes:        schemes, +			Scopes:         scopes, +			allScopes:      stringSliceUnion(scopeSlices...), +			commonScopes:   stringSliceIntersection(scopeSlices...), +			allowAnonymous: len(reqs) == 1 && reqs[0].Name == "", +		}) +	} +	return auths +} + +func (d *defaultRouteBuilder) Build() *defaultRouter { +	routers := make(map[string]*denco.Router) +	for method, records := range d.records { +		router := denco.New() +		_ = router.Build(records) +		routers[method] = router +	} +	return &defaultRouter{ +		spec:    d.spec, +		routers: routers, +	} +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/security.go b/vendor/github.com/go-openapi/runtime/middleware/security.go new file mode 100644 index 000000000..2b061caef --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/security.go @@ -0,0 +1,39 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package middleware + +import "net/http" + +func newSecureAPI(ctx *Context, next http.Handler) http.Handler { +	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { +		route, rCtx, _ := ctx.RouteInfo(r) +		if rCtx != nil { +			r = rCtx +		} +		if route != nil && !route.NeedsAuth() { +			next.ServeHTTP(rw, r) +			return +		} + +		_, rCtx, err := ctx.Authorize(r, route) +		if err != nil { +			ctx.Respond(rw, r, route.Produces, route, err) +			return +		} +		r = rCtx + +		next.ServeHTTP(rw, r) +	}) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/spec.go b/vendor/github.com/go-openapi/runtime/middleware/spec.go new file mode 100644 index 000000000..f02914298 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/spec.go @@ -0,0 +1,48 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package middleware + +import ( +	"net/http" +	"path" +) + +// Spec creates a middleware to serve a swagger spec. +// This allows for altering the spec before starting the http listener. +// This can be useful if you want to serve the swagger spec from another path than /swagger.json +// +func Spec(basePath string, b []byte, next http.Handler) http.Handler { +	if basePath == "" { +		basePath = "/" +	} +	pth := path.Join(basePath, "swagger.json") + +	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { +		if r.URL.Path == pth { +			rw.Header().Set("Content-Type", "application/json") +			rw.WriteHeader(http.StatusOK) +			//#nosec +			_, _ = rw.Write(b) +			return +		} + +		if next == nil { +			rw.Header().Set("Content-Type", "application/json") +			rw.WriteHeader(http.StatusNotFound) +			return +		} +		next.ServeHTTP(rw, r) +	}) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/swaggerui.go b/vendor/github.com/go-openapi/runtime/middleware/swaggerui.go new file mode 100644 index 000000000..b4dea29e4 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/swaggerui.go @@ -0,0 +1,168 @@ +package middleware + +import ( +	"bytes" +	"fmt" +	"html/template" +	"net/http" +	"path" +) + +// SwaggerUIOpts configures the Swaggerui middlewares +type SwaggerUIOpts struct { +	// BasePath for the UI path, defaults to: / +	BasePath string +	// Path combines with BasePath for the full UI path, defaults to: docs +	Path string +	// SpecURL the url to find the spec for +	SpecURL string +	// OAuthCallbackURL the url called after OAuth2 login +	OAuthCallbackURL string + +	// The three components needed to embed swagger-ui +	SwaggerURL       string +	SwaggerPresetURL string +	SwaggerStylesURL string + +	Favicon32 string +	Favicon16 string + +	// Title for the documentation site, default to: API documentation +	Title string +} + +// EnsureDefaults in case some options are missing +func (r *SwaggerUIOpts) EnsureDefaults() { +	if r.BasePath == "" { +		r.BasePath = "/" +	} +	if r.Path == "" { +		r.Path = "docs" +	} +	if r.SpecURL == "" { +		r.SpecURL = "/swagger.json" +	} +	if r.OAuthCallbackURL == "" { +		r.OAuthCallbackURL = path.Join(r.BasePath, r.Path, "oauth2-callback") +	} +	if r.SwaggerURL == "" { +		r.SwaggerURL = swaggerLatest +	} +	if r.SwaggerPresetURL == "" { +		r.SwaggerPresetURL = swaggerPresetLatest +	} +	if r.SwaggerStylesURL == "" { +		r.SwaggerStylesURL = swaggerStylesLatest +	} +	if r.Favicon16 == "" { +		r.Favicon16 = swaggerFavicon16Latest +	} +	if r.Favicon32 == "" { +		r.Favicon32 = swaggerFavicon32Latest +	} +	if r.Title == "" { +		r.Title = "API documentation" +	} +} + +// SwaggerUI creates a middleware to serve a documentation site for a swagger spec. +// This allows for altering the spec before starting the http listener. +func SwaggerUI(opts SwaggerUIOpts, next http.Handler) http.Handler { +	opts.EnsureDefaults() + +	pth := path.Join(opts.BasePath, opts.Path) +	tmpl := template.Must(template.New("swaggerui").Parse(swaggeruiTemplate)) + +	buf := bytes.NewBuffer(nil) +	_ = tmpl.Execute(buf, &opts) +	b := buf.Bytes() + +	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { +		if path.Join(r.URL.Path) == pth { +			rw.Header().Set("Content-Type", "text/html; charset=utf-8") +			rw.WriteHeader(http.StatusOK) + +			_, _ = rw.Write(b) +			return +		} + +		if next == nil { +			rw.Header().Set("Content-Type", "text/plain") +			rw.WriteHeader(http.StatusNotFound) +			_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth))) +			return +		} +		next.ServeHTTP(rw, r) +	}) +} + +const ( +	swaggerLatest          = "https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js" +	swaggerPresetLatest    = "https://unpkg.com/swagger-ui-dist/swagger-ui-standalone-preset.js" +	swaggerStylesLatest    = "https://unpkg.com/swagger-ui-dist/swagger-ui.css" +	swaggerFavicon32Latest = "https://unpkg.com/swagger-ui-dist/favicon-32x32.png" +	swaggerFavicon16Latest = "https://unpkg.com/swagger-ui-dist/favicon-16x16.png" +	swaggeruiTemplate      = ` +<!DOCTYPE html> +<html lang="en"> +  <head> +    <meta charset="UTF-8"> +		<title>{{ .Title }}</title> + +    <link rel="stylesheet" type="text/css" href="{{ .SwaggerStylesURL }}" > +    <link rel="icon" type="image/png" href="{{ .Favicon32 }}" sizes="32x32" /> +    <link rel="icon" type="image/png" href="{{ .Favicon16 }}" sizes="16x16" /> +    <style> +      html +      { +        box-sizing: border-box; +        overflow: -moz-scrollbars-vertical; +        overflow-y: scroll; +      } + +      *, +      *:before, +      *:after +      { +        box-sizing: inherit; +      } + +      body +      { +        margin:0; +        background: #fafafa; +      } +    </style> +  </head> + +  <body> +    <div id="swagger-ui"></div> + +    <script src="{{ .SwaggerURL }}"> </script> +    <script src="{{ .SwaggerPresetURL }}"> </script> +    <script> +    window.onload = function() { +      // Begin Swagger UI call region +      const ui = SwaggerUIBundle({ +        url: '{{ .SpecURL }}', +        dom_id: '#swagger-ui', +        deepLinking: true, +        presets: [ +          SwaggerUIBundle.presets.apis, +          SwaggerUIStandalonePreset +        ], +        plugins: [ +          SwaggerUIBundle.plugins.DownloadUrl +        ], +        layout: "StandaloneLayout", +		oauth2RedirectUrl: '{{ .OAuthCallbackURL }}' +      }) +      // End Swagger UI call region + +      window.ui = ui +    } +  </script> +  </body> +</html> +` +) diff --git a/vendor/github.com/go-openapi/runtime/middleware/swaggerui_oauth2.go b/vendor/github.com/go-openapi/runtime/middleware/swaggerui_oauth2.go new file mode 100644 index 000000000..576f6003f --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/swaggerui_oauth2.go @@ -0,0 +1,122 @@ +package middleware + +import ( +	"bytes" +	"fmt" +	"net/http" +	"path" +	"text/template" +) + +func SwaggerUIOAuth2Callback(opts SwaggerUIOpts, next http.Handler) http.Handler { +	opts.EnsureDefaults() + +	pth := opts.OAuthCallbackURL +	tmpl := template.Must(template.New("swaggeroauth").Parse(swaggerOAuthTemplate)) + +	buf := bytes.NewBuffer(nil) +	_ = tmpl.Execute(buf, &opts) +	b := buf.Bytes() + +	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { +		if path.Join(r.URL.Path) == pth { +			rw.Header().Set("Content-Type", "text/html; charset=utf-8") +			rw.WriteHeader(http.StatusOK) + +			_, _ = rw.Write(b) +			return +		} + +		if next == nil { +			rw.Header().Set("Content-Type", "text/plain") +			rw.WriteHeader(http.StatusNotFound) +			_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth))) +			return +		} +		next.ServeHTTP(rw, r) +	}) +} + +const ( +	swaggerOAuthTemplate = ` +<!DOCTYPE html> +<html lang="en"> +<head> +    <title>{{ .Title }}</title> +</head> +<body> +<script> +    'use strict'; +    function run () { +        var oauth2 = window.opener.swaggerUIRedirectOauth2; +        var sentState = oauth2.state; +        var redirectUrl = oauth2.redirectUrl; +        var isValid, qp, arr; + +        if (/code|token|error/.test(window.location.hash)) { +            qp = window.location.hash.substring(1).replace('?', '&'); +        } else { +            qp = location.search.substring(1); +        } + +        arr = qp.split("&"); +        arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';}); +        qp = qp ? JSON.parse('{' + arr.join() + '}', +                function (key, value) { +                    return key === "" ? value : decodeURIComponent(value); +                } +        ) : {}; + +        isValid = qp.state === sentState; + +        if (( +          oauth2.auth.schema.get("flow") === "accessCode" || +          oauth2.auth.schema.get("flow") === "authorizationCode" || +          oauth2.auth.schema.get("flow") === "authorization_code" +        ) && !oauth2.auth.code) { +            if (!isValid) { +                oauth2.errCb({ +                    authId: oauth2.auth.name, +                    source: "auth", +                    level: "warning", +                    message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server." +                }); +            } + +            if (qp.code) { +                delete oauth2.state; +                oauth2.auth.code = qp.code; +                oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl}); +            } else { +                let oauthErrorMsg; +                if (qp.error) { +                    oauthErrorMsg = "["+qp.error+"]: " + +                        (qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") + +                        (qp.error_uri ? "More info: "+qp.error_uri : ""); +                } + +                oauth2.errCb({ +                    authId: oauth2.auth.name, +                    source: "auth", +                    level: "error", +                    message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server." +                }); +            } +        } else { +            oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl}); +        } +        window.close(); +    } + +    if (document.readyState !== 'loading') { +        run(); +    } else { +        document.addEventListener('DOMContentLoaded', function () { +            run(); +        }); +    } +</script> +</body> +</html> +` +) diff --git a/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go b/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go new file mode 100644 index 000000000..39a85f7d9 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go @@ -0,0 +1,286 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package untyped + +import ( +	"fmt" +	"net/http" +	"sort" +	"strings" + +	"github.com/go-openapi/analysis" +	"github.com/go-openapi/errors" +	"github.com/go-openapi/loads" +	"github.com/go-openapi/spec" +	"github.com/go-openapi/strfmt" + +	"github.com/go-openapi/runtime" +) + +// NewAPI creates the default untyped API +func NewAPI(spec *loads.Document) *API { +	var an *analysis.Spec +	if spec != nil && spec.Spec() != nil { +		an = analysis.New(spec.Spec()) +	} +	api := &API{ +		spec:           spec, +		analyzer:       an, +		consumers:      make(map[string]runtime.Consumer, 10), +		producers:      make(map[string]runtime.Producer, 10), +		authenticators: make(map[string]runtime.Authenticator), +		operations:     make(map[string]map[string]runtime.OperationHandler), +		ServeError:     errors.ServeError, +		Models:         make(map[string]func() interface{}), +		formats:        strfmt.NewFormats(), +	} +	return api.WithJSONDefaults() +} + +// API represents an untyped mux for a swagger spec +type API struct { +	spec            *loads.Document +	analyzer        *analysis.Spec +	DefaultProduces string +	DefaultConsumes string +	consumers       map[string]runtime.Consumer +	producers       map[string]runtime.Producer +	authenticators  map[string]runtime.Authenticator +	authorizer      runtime.Authorizer +	operations      map[string]map[string]runtime.OperationHandler +	ServeError      func(http.ResponseWriter, *http.Request, error) +	Models          map[string]func() interface{} +	formats         strfmt.Registry +} + +// WithJSONDefaults loads the json defaults for this api +func (d *API) WithJSONDefaults() *API { +	d.DefaultConsumes = runtime.JSONMime +	d.DefaultProduces = runtime.JSONMime +	d.consumers[runtime.JSONMime] = runtime.JSONConsumer() +	d.producers[runtime.JSONMime] = runtime.JSONProducer() +	return d +} + +// WithoutJSONDefaults clears the json defaults for this api +func (d *API) WithoutJSONDefaults() *API { +	d.DefaultConsumes = "" +	d.DefaultProduces = "" +	delete(d.consumers, runtime.JSONMime) +	delete(d.producers, runtime.JSONMime) +	return d +} + +// Formats returns the registered string formats +func (d *API) Formats() strfmt.Registry { +	if d.formats == nil { +		d.formats = strfmt.NewFormats() +	} +	return d.formats +} + +// RegisterFormat registers a custom format validator +func (d *API) RegisterFormat(name string, format strfmt.Format, validator strfmt.Validator) { +	if d.formats == nil { +		d.formats = strfmt.NewFormats() +	} +	d.formats.Add(name, format, validator) +} + +// RegisterAuth registers an auth handler in this api +func (d *API) RegisterAuth(scheme string, handler runtime.Authenticator) { +	if d.authenticators == nil { +		d.authenticators = make(map[string]runtime.Authenticator) +	} +	d.authenticators[scheme] = handler +} + +// RegisterAuthorizer registers an authorizer handler in this api +func (d *API) RegisterAuthorizer(handler runtime.Authorizer) { +	d.authorizer = handler +} + +// RegisterConsumer registers a consumer for a media type. +func (d *API) RegisterConsumer(mediaType string, handler runtime.Consumer) { +	if d.consumers == nil { +		d.consumers = make(map[string]runtime.Consumer, 10) +	} +	d.consumers[strings.ToLower(mediaType)] = handler +} + +// RegisterProducer registers a producer for a media type +func (d *API) RegisterProducer(mediaType string, handler runtime.Producer) { +	if d.producers == nil { +		d.producers = make(map[string]runtime.Producer, 10) +	} +	d.producers[strings.ToLower(mediaType)] = handler +} + +// RegisterOperation registers an operation handler for an operation name +func (d *API) RegisterOperation(method, path string, handler runtime.OperationHandler) { +	if d.operations == nil { +		d.operations = make(map[string]map[string]runtime.OperationHandler, 30) +	} +	um := strings.ToUpper(method) +	if b, ok := d.operations[um]; !ok || b == nil { +		d.operations[um] = make(map[string]runtime.OperationHandler) +	} +	d.operations[um][path] = handler +} + +// OperationHandlerFor returns the operation handler for the specified id if it can be found +func (d *API) OperationHandlerFor(method, path string) (runtime.OperationHandler, bool) { +	if d.operations == nil { +		return nil, false +	} +	if pi, ok := d.operations[strings.ToUpper(method)]; ok { +		h, ok := pi[path] +		return h, ok +	} +	return nil, false +} + +// ConsumersFor gets the consumers for the specified media types +func (d *API) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer { +	result := make(map[string]runtime.Consumer) +	for _, mt := range mediaTypes { +		if consumer, ok := d.consumers[mt]; ok { +			result[mt] = consumer +		} +	} +	return result +} + +// ProducersFor gets the producers for the specified media types +func (d *API) ProducersFor(mediaTypes []string) map[string]runtime.Producer { +	result := make(map[string]runtime.Producer) +	for _, mt := range mediaTypes { +		if producer, ok := d.producers[mt]; ok { +			result[mt] = producer +		} +	} +	return result +} + +// AuthenticatorsFor gets the authenticators for the specified security schemes +func (d *API) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator { +	result := make(map[string]runtime.Authenticator) +	for k := range schemes { +		if a, ok := d.authenticators[k]; ok { +			result[k] = a +		} +	} +	return result +} + +// Authorizer returns the registered authorizer +func (d *API) Authorizer() runtime.Authorizer { +	return d.authorizer +} + +// Validate validates this API for any missing items +func (d *API) Validate() error { +	return d.validate() +} + +// validateWith validates the registrations in this API against the provided spec analyzer +func (d *API) validate() error { +	var consumes []string +	for k := range d.consumers { +		consumes = append(consumes, k) +	} + +	var produces []string +	for k := range d.producers { +		produces = append(produces, k) +	} + +	var authenticators []string +	for k := range d.authenticators { +		authenticators = append(authenticators, k) +	} + +	var operations []string +	for m, v := range d.operations { +		for p := range v { +			operations = append(operations, fmt.Sprintf("%s %s", strings.ToUpper(m), p)) +		} +	} + +	var definedAuths []string +	for k := range d.spec.Spec().SecurityDefinitions { +		definedAuths = append(definedAuths, k) +	} + +	if err := d.verify("consumes", consumes, d.analyzer.RequiredConsumes()); err != nil { +		return err +	} +	if err := d.verify("produces", produces, d.analyzer.RequiredProduces()); err != nil { +		return err +	} +	if err := d.verify("operation", operations, d.analyzer.OperationMethodPaths()); err != nil { +		return err +	} + +	requiredAuths := d.analyzer.RequiredSecuritySchemes() +	if err := d.verify("auth scheme", authenticators, requiredAuths); err != nil { +		return err +	} +	if err := d.verify("security definitions", definedAuths, requiredAuths); err != nil { +		return err +	} +	return nil +} + +func (d *API) verify(name string, registrations []string, expectations []string) error { +	sort.Strings(registrations) +	sort.Strings(expectations) + +	expected := map[string]struct{}{} +	seen := map[string]struct{}{} + +	for _, v := range expectations { +		expected[v] = struct{}{} +	} + +	var unspecified []string +	for _, v := range registrations { +		seen[v] = struct{}{} +		if _, ok := expected[v]; !ok { +			unspecified = append(unspecified, v) +		} +	} + +	for k := range seen { +		delete(expected, k) +	} + +	var unregistered []string +	for k := range expected { +		unregistered = append(unregistered, k) +	} +	sort.Strings(unspecified) +	sort.Strings(unregistered) + +	if len(unregistered) > 0 || len(unspecified) > 0 { +		return &errors.APIVerificationFailed{ +			Section:              name, +			MissingSpecification: unspecified, +			MissingRegistration:  unregistered, +		} +	} + +	return nil +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/validation.go b/vendor/github.com/go-openapi/runtime/middleware/validation.go new file mode 100644 index 000000000..1f0135b57 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/validation.go @@ -0,0 +1,126 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package middleware + +import ( +	"mime" +	"net/http" +	"strings" + +	"github.com/go-openapi/errors" +	"github.com/go-openapi/swag" + +	"github.com/go-openapi/runtime" +) + +type validation struct { +	context *Context +	result  []error +	request *http.Request +	route   *MatchedRoute +	bound   map[string]interface{} +} + +// ContentType validates the content type of a request +func validateContentType(allowed []string, actual string) error { +	debugLog("validating content type for %q against [%s]", actual, strings.Join(allowed, ", ")) +	if len(allowed) == 0 { +		return nil +	} +	mt, _, err := mime.ParseMediaType(actual) +	if err != nil { +		return errors.InvalidContentType(actual, allowed) +	} +	if swag.ContainsStringsCI(allowed, mt) { +		return nil +	} +	if swag.ContainsStringsCI(allowed, "*/*") { +		return nil +	} +	parts := strings.Split(actual, "/") +	if len(parts) == 2 && swag.ContainsStringsCI(allowed, parts[0]+"/*") { +		return nil +	} +	return errors.InvalidContentType(actual, allowed) +} + +func validateRequest(ctx *Context, request *http.Request, route *MatchedRoute) *validation { +	debugLog("validating request %s %s", request.Method, request.URL.EscapedPath()) +	validate := &validation{ +		context: ctx, +		request: request, +		route:   route, +		bound:   make(map[string]interface{}), +	} + +	validate.contentType() +	if len(validate.result) == 0 { +		validate.responseFormat() +	} +	if len(validate.result) == 0 { +		validate.parameters() +	} + +	return validate +} + +func (v *validation) parameters() { +	debugLog("validating request parameters for %s %s", v.request.Method, v.request.URL.EscapedPath()) +	if result := v.route.Binder.Bind(v.request, v.route.Params, v.route.Consumer, v.bound); result != nil { +		if result.Error() == "validation failure list" { +			for _, e := range result.(*errors.Validation).Value.([]interface{}) { +				v.result = append(v.result, e.(error)) +			} +			return +		} +		v.result = append(v.result, result) +	} +} + +func (v *validation) contentType() { +	if len(v.result) == 0 && runtime.HasBody(v.request) { +		debugLog("validating body content type for %s %s", v.request.Method, v.request.URL.EscapedPath()) +		ct, _, req, err := v.context.ContentType(v.request) +		if err != nil { +			v.result = append(v.result, err) +		} else { +			v.request = req +		} + +		if len(v.result) == 0 { +			if err := validateContentType(v.route.Consumes, ct); err != nil { +				v.result = append(v.result, err) +			} +		} +		if ct != "" && v.route.Consumer == nil { +			cons, ok := v.route.Consumers[ct] +			if !ok { +				v.result = append(v.result, errors.New(500, "no consumer registered for %s", ct)) +			} else { +				v.route.Consumer = cons +			} +		} +	} +} + +func (v *validation) responseFormat() { +	// if the route provides values for Produces and no format could be identify then return an error. +	// if the route does not specify values for Produces then treat request as valid since the API designer +	// choose not to specify the format for responses. +	if str, rCtx := v.context.ResponseFormat(v.request, v.route.Produces); str == "" && len(v.route.Produces) > 0 { +		v.request = rCtx +		v.result = append(v.result, errors.InvalidResponseFormat(v.request.Header.Get(runtime.HeaderAccept), v.route.Produces)) +	} +} diff --git a/vendor/github.com/go-openapi/runtime/request.go b/vendor/github.com/go-openapi/runtime/request.go new file mode 100644 index 000000000..078fda173 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/request.go @@ -0,0 +1,139 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( +	"bufio" +	"io" +	"net/http" +	"strings" + +	"github.com/go-openapi/swag" +) + +// CanHaveBody returns true if this method can have a body +func CanHaveBody(method string) bool { +	mn := strings.ToUpper(method) +	return mn == "POST" || mn == "PUT" || mn == "PATCH" || mn == "DELETE" +} + +// IsSafe returns true if this is a request with a safe method +func IsSafe(r *http.Request) bool { +	mn := strings.ToUpper(r.Method) +	return mn == "GET" || mn == "HEAD" +} + +// AllowsBody returns true if the request allows for a body +func AllowsBody(r *http.Request) bool { +	mn := strings.ToUpper(r.Method) +	return mn != "HEAD" +} + +// HasBody returns true if this method needs a content-type +func HasBody(r *http.Request) bool { +	// happy case: we have a content length set +	if r.ContentLength > 0 { +		return true +	} + +	if r.Header.Get("content-length") != "" { +		// in this case, no Transfer-Encoding should be present +		// we have a header set but it was explicitly set to 0, so we assume no body +		return false +	} + +	rdr := newPeekingReader(r.Body) +	r.Body = rdr +	return rdr.HasContent() +} + +func newPeekingReader(r io.ReadCloser) *peekingReader { +	if r == nil { +		return nil +	} +	return &peekingReader{ +		underlying: bufio.NewReader(r), +		orig:       r, +	} +} + +type peekingReader struct { +	underlying interface { +		Buffered() int +		Peek(int) ([]byte, error) +		Read([]byte) (int, error) +	} +	orig io.ReadCloser +} + +func (p *peekingReader) HasContent() bool { +	if p == nil { +		return false +	} +	if p.underlying.Buffered() > 0 { +		return true +	} +	b, err := p.underlying.Peek(1) +	if err != nil { +		return false +	} +	return len(b) > 0 +} + +func (p *peekingReader) Read(d []byte) (int, error) { +	if p == nil { +		return 0, io.EOF +	} +	return p.underlying.Read(d) +} + +func (p *peekingReader) Close() error { +	p.underlying = nil +	if p.orig != nil { +		return p.orig.Close() +	} +	return nil +} + +// JSONRequest creates a new http request with json headers set +func JSONRequest(method, urlStr string, body io.Reader) (*http.Request, error) { +	req, err := http.NewRequest(method, urlStr, body) +	if err != nil { +		return nil, err +	} +	req.Header.Add(HeaderContentType, JSONMime) +	req.Header.Add(HeaderAccept, JSONMime) +	return req, nil +} + +// Gettable for things with a method GetOK(string) (data string, hasKey bool, hasValue bool) +type Gettable interface { +	GetOK(string) ([]string, bool, bool) +} + +// ReadSingleValue reads a single value from the source +func ReadSingleValue(values Gettable, name string) string { +	vv, _, hv := values.GetOK(name) +	if hv { +		return vv[len(vv)-1] +	} +	return "" +} + +// ReadCollectionValue reads a collection value from a string data source +func ReadCollectionValue(values Gettable, name, collectionFormat string) []string { +	v := ReadSingleValue(values, name) +	return swag.SplitByFormat(v, collectionFormat) +} diff --git a/vendor/github.com/go-openapi/runtime/security/authenticator.go b/vendor/github.com/go-openapi/runtime/security/authenticator.go new file mode 100644 index 000000000..c3ffdac7e --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/security/authenticator.go @@ -0,0 +1,276 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package security + +import ( +	"context" +	"net/http" +	"strings" + +	"github.com/go-openapi/errors" + +	"github.com/go-openapi/runtime" +) + +const ( +	query  = "query" +	header = "header" +) + +// HttpAuthenticator is a function that authenticates a HTTP request +func HttpAuthenticator(handler func(*http.Request) (bool, interface{}, error)) runtime.Authenticator { +	return runtime.AuthenticatorFunc(func(params interface{}) (bool, interface{}, error) { +		if request, ok := params.(*http.Request); ok { +			return handler(request) +		} +		if scoped, ok := params.(*ScopedAuthRequest); ok { +			return handler(scoped.Request) +		} +		return false, nil, nil +	}) +} + +// ScopedAuthenticator is a function that authenticates a HTTP request against a list of valid scopes +func ScopedAuthenticator(handler func(*ScopedAuthRequest) (bool, interface{}, error)) runtime.Authenticator { +	return runtime.AuthenticatorFunc(func(params interface{}) (bool, interface{}, error) { +		if request, ok := params.(*ScopedAuthRequest); ok { +			return handler(request) +		} +		return false, nil, nil +	}) +} + +// UserPassAuthentication authentication function +type UserPassAuthentication func(string, string) (interface{}, error) + +// UserPassAuthenticationCtx authentication function with context.Context +type UserPassAuthenticationCtx func(context.Context, string, string) (context.Context, interface{}, error) + +// TokenAuthentication authentication function +type TokenAuthentication func(string) (interface{}, error) + +// TokenAuthenticationCtx authentication function with context.Context +type TokenAuthenticationCtx func(context.Context, string) (context.Context, interface{}, error) + +// ScopedTokenAuthentication authentication function +type ScopedTokenAuthentication func(string, []string) (interface{}, error) + +// ScopedTokenAuthenticationCtx authentication function with context.Context +type ScopedTokenAuthenticationCtx func(context.Context, string, []string) (context.Context, interface{}, error) + +var DefaultRealmName = "API" + +type secCtxKey uint8 + +const ( +	failedBasicAuth secCtxKey = iota +	oauth2SchemeName +) + +func FailedBasicAuth(r *http.Request) string { +	return FailedBasicAuthCtx(r.Context()) +} + +func FailedBasicAuthCtx(ctx context.Context) string { +	v, ok := ctx.Value(failedBasicAuth).(string) +	if !ok { +		return "" +	} +	return v +} + +func OAuth2SchemeName(r *http.Request) string { +	return OAuth2SchemeNameCtx(r.Context()) +} + +func OAuth2SchemeNameCtx(ctx context.Context) string { +	v, ok := ctx.Value(oauth2SchemeName).(string) +	if !ok { +		return "" +	} +	return v +} + +// BasicAuth creates a basic auth authenticator with the provided authentication function +func BasicAuth(authenticate UserPassAuthentication) runtime.Authenticator { +	return BasicAuthRealm(DefaultRealmName, authenticate) +} + +// BasicAuthRealm creates a basic auth authenticator with the provided authentication function and realm name +func BasicAuthRealm(realm string, authenticate UserPassAuthentication) runtime.Authenticator { +	if realm == "" { +		realm = DefaultRealmName +	} + +	return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) { +		if usr, pass, ok := r.BasicAuth(); ok { +			p, err := authenticate(usr, pass) +			if err != nil { +				*r = *r.WithContext(context.WithValue(r.Context(), failedBasicAuth, realm)) +			} +			return true, p, err +		} +		*r = *r.WithContext(context.WithValue(r.Context(), failedBasicAuth, realm)) +		return false, nil, nil +	}) +} + +// BasicAuthCtx creates a basic auth authenticator with the provided authentication function with support for context.Context +func BasicAuthCtx(authenticate UserPassAuthenticationCtx) runtime.Authenticator { +	return BasicAuthRealmCtx(DefaultRealmName, authenticate) +} + +// BasicAuthRealmCtx creates a basic auth authenticator with the provided authentication function and realm name with support for context.Context +func BasicAuthRealmCtx(realm string, authenticate UserPassAuthenticationCtx) runtime.Authenticator { +	if realm == "" { +		realm = DefaultRealmName +	} + +	return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) { +		if usr, pass, ok := r.BasicAuth(); ok { +			ctx, p, err := authenticate(r.Context(), usr, pass) +			if err != nil { +				ctx = context.WithValue(ctx, failedBasicAuth, realm) +			} +			*r = *r.WithContext(ctx) +			return true, p, err +		} +		*r = *r.WithContext(context.WithValue(r.Context(), failedBasicAuth, realm)) +		return false, nil, nil +	}) +} + +// APIKeyAuth creates an authenticator that uses a token for authorization. +// This token can be obtained from either a header or a query string +func APIKeyAuth(name, in string, authenticate TokenAuthentication) runtime.Authenticator { +	inl := strings.ToLower(in) +	if inl != query && inl != header { +		// panic because this is most likely a typo +		panic(errors.New(500, "api key auth: in value needs to be either \"query\" or \"header\".")) +	} + +	var getToken func(*http.Request) string +	switch inl { +	case header: +		getToken = func(r *http.Request) string { return r.Header.Get(name) } +	case query: +		getToken = func(r *http.Request) string { return r.URL.Query().Get(name) } +	} + +	return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) { +		token := getToken(r) +		if token == "" { +			return false, nil, nil +		} + +		p, err := authenticate(token) +		return true, p, err +	}) +} + +// APIKeyAuthCtx creates an authenticator that uses a token for authorization with support for context.Context. +// This token can be obtained from either a header or a query string +func APIKeyAuthCtx(name, in string, authenticate TokenAuthenticationCtx) runtime.Authenticator { +	inl := strings.ToLower(in) +	if inl != query && inl != header { +		// panic because this is most likely a typo +		panic(errors.New(500, "api key auth: in value needs to be either \"query\" or \"header\".")) +	} + +	var getToken func(*http.Request) string +	switch inl { +	case header: +		getToken = func(r *http.Request) string { return r.Header.Get(name) } +	case query: +		getToken = func(r *http.Request) string { return r.URL.Query().Get(name) } +	} + +	return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) { +		token := getToken(r) +		if token == "" { +			return false, nil, nil +		} + +		ctx, p, err := authenticate(r.Context(), token) +		*r = *r.WithContext(ctx) +		return true, p, err +	}) +} + +// ScopedAuthRequest contains both a http request and the required scopes for a particular operation +type ScopedAuthRequest struct { +	Request        *http.Request +	RequiredScopes []string +} + +// BearerAuth for use with oauth2 flows +func BearerAuth(name string, authenticate ScopedTokenAuthentication) runtime.Authenticator { +	const prefix = "Bearer " +	return ScopedAuthenticator(func(r *ScopedAuthRequest) (bool, interface{}, error) { +		var token string +		hdr := r.Request.Header.Get(runtime.HeaderAuthorization) +		if strings.HasPrefix(hdr, prefix) { +			token = strings.TrimPrefix(hdr, prefix) +		} +		if token == "" { +			qs := r.Request.URL.Query() +			token = qs.Get("access_token") +		} +		//#nosec +		ct, _, _ := runtime.ContentType(r.Request.Header) +		if token == "" && (ct == "application/x-www-form-urlencoded" || ct == "multipart/form-data") { +			token = r.Request.FormValue("access_token") +		} + +		if token == "" { +			return false, nil, nil +		} + +		rctx := context.WithValue(r.Request.Context(), oauth2SchemeName, name) +		*r.Request = *r.Request.WithContext(rctx) +		p, err := authenticate(token, r.RequiredScopes) +		return true, p, err +	}) +} + +// BearerAuthCtx for use with oauth2 flows with support for context.Context. +func BearerAuthCtx(name string, authenticate ScopedTokenAuthenticationCtx) runtime.Authenticator { +	const prefix = "Bearer " +	return ScopedAuthenticator(func(r *ScopedAuthRequest) (bool, interface{}, error) { +		var token string +		hdr := r.Request.Header.Get(runtime.HeaderAuthorization) +		if strings.HasPrefix(hdr, prefix) { +			token = strings.TrimPrefix(hdr, prefix) +		} +		if token == "" { +			qs := r.Request.URL.Query() +			token = qs.Get("access_token") +		} +		//#nosec +		ct, _, _ := runtime.ContentType(r.Request.Header) +		if token == "" && (ct == "application/x-www-form-urlencoded" || ct == "multipart/form-data") { +			token = r.Request.FormValue("access_token") +		} + +		if token == "" { +			return false, nil, nil +		} + +		rctx := context.WithValue(r.Request.Context(), oauth2SchemeName, name) +		ctx, p, err := authenticate(rctx, token, r.RequiredScopes) +		*r.Request = *r.Request.WithContext(ctx) +		return true, p, err +	}) +} diff --git a/vendor/github.com/go-openapi/runtime/security/authorizer.go b/vendor/github.com/go-openapi/runtime/security/authorizer.go new file mode 100644 index 000000000..00c1a4d6a --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/security/authorizer.go @@ -0,0 +1,27 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package security + +import ( +	"net/http" + +	"github.com/go-openapi/runtime" +) + +// Authorized provides a default implementation of the Authorizer interface where all +// requests are authorized (successful) +func Authorized() runtime.Authorizer { +	return runtime.AuthorizerFunc(func(_ *http.Request, _ interface{}) error { return nil }) +} diff --git a/vendor/github.com/go-openapi/runtime/statuses.go b/vendor/github.com/go-openapi/runtime/statuses.go new file mode 100644 index 000000000..3b011a0bf --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/statuses.go @@ -0,0 +1,90 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +// Statuses lists the most common HTTP status codes to default message +// taken from https://httpstatuses.com/ +var Statuses = map[int]string{ +	100: "Continue", +	101: "Switching Protocols", +	102: "Processing", +	103: "Checkpoint", +	122: "URI too long", +	200: "OK", +	201: "Created", +	202: "Accepted", +	203: "Request Processed", +	204: "No Content", +	205: "Reset Content", +	206: "Partial Content", +	207: "Multi-Status", +	208: "Already Reported", +	226: "IM Used", +	300: "Multiple Choices", +	301: "Moved Permanently", +	302: "Found", +	303: "See Other", +	304: "Not Modified", +	305: "Use Proxy", +	306: "Switch Proxy", +	307: "Temporary Redirect", +	308: "Permanent Redirect", +	400: "Bad Request", +	401: "Unauthorized", +	402: "Payment Required", +	403: "Forbidden", +	404: "Not Found", +	405: "Method Not Allowed", +	406: "Not Acceptable", +	407: "Proxy Authentication Required", +	408: "Request Timeout", +	409: "Conflict", +	410: "Gone", +	411: "Length Required", +	412: "Precondition Failed", +	413: "Request Entity Too Large", +	414: "Request-URI Too Long", +	415: "Unsupported Media Type", +	416: "Request Range Not Satisfiable", +	417: "Expectation Failed", +	418: "I'm a teapot", +	420: "Enhance Your Calm", +	422: "Unprocessable Entity", +	423: "Locked", +	424: "Failed Dependency", +	426: "Upgrade Required", +	428: "Precondition Required", +	429: "Too Many Requests", +	431: "Request Header Fields Too Large", +	444: "No Response", +	449: "Retry With", +	450: "Blocked by Windows Parental Controls", +	451: "Wrong Exchange Server", +	499: "Client Closed Request", +	500: "Internal Server Error", +	501: "Not Implemented", +	502: "Bad Gateway", +	503: "Service Unavailable", +	504: "Gateway Timeout", +	505: "HTTP Version Not Supported", +	506: "Variant Also Negotiates", +	507: "Insufficient Storage", +	508: "Loop Detected", +	509: "Bandwidth Limit Exceeded", +	510: "Not Extended", +	511: "Network Authentication Required", +	598: "Network read timeout error", +	599: "Network connect timeout error", +} diff --git a/vendor/github.com/go-openapi/runtime/text.go b/vendor/github.com/go-openapi/runtime/text.go new file mode 100644 index 000000000..f33320b7d --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/text.go @@ -0,0 +1,116 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( +	"bytes" +	"encoding" +	"errors" +	"fmt" +	"io" +	"reflect" + +	"github.com/go-openapi/swag" +) + +// TextConsumer creates a new text consumer +func TextConsumer() Consumer { +	return ConsumerFunc(func(reader io.Reader, data interface{}) error { +		if reader == nil { +			return errors.New("TextConsumer requires a reader") // early exit +		} + +		buf := new(bytes.Buffer) +		_, err := buf.ReadFrom(reader) +		if err != nil { +			return err +		} +		b := buf.Bytes() + +		// If the buffer is empty, no need to unmarshal it, which causes a panic. +		if len(b) == 0 { +			return nil +		} + +		if tu, ok := data.(encoding.TextUnmarshaler); ok { +			err := tu.UnmarshalText(b) +			if err != nil { +				return fmt.Errorf("text consumer: %v", err) +			} + +			return nil +		} + +		t := reflect.TypeOf(data) +		if data != nil && t.Kind() == reflect.Ptr { +			v := reflect.Indirect(reflect.ValueOf(data)) +			if t.Elem().Kind() == reflect.String { +				v.SetString(string(b)) +				return nil +			} +		} + +		return fmt.Errorf("%v (%T) is not supported by the TextConsumer, %s", +			data, data, "can be resolved by supporting TextUnmarshaler interface") +	}) +} + +// TextProducer creates a new text producer +func TextProducer() Producer { +	return ProducerFunc(func(writer io.Writer, data interface{}) error { +		if writer == nil { +			return errors.New("TextProducer requires a writer") // early exit +		} + +		if data == nil { +			return errors.New("no data given to produce text from") +		} + +		if tm, ok := data.(encoding.TextMarshaler); ok { +			txt, err := tm.MarshalText() +			if err != nil { +				return fmt.Errorf("text producer: %v", err) +			} +			_, err = writer.Write(txt) +			return err +		} + +		if str, ok := data.(error); ok { +			_, err := writer.Write([]byte(str.Error())) +			return err +		} + +		if str, ok := data.(fmt.Stringer); ok { +			_, err := writer.Write([]byte(str.String())) +			return err +		} + +		v := reflect.Indirect(reflect.ValueOf(data)) +		if t := v.Type(); t.Kind() == reflect.Struct || t.Kind() == reflect.Slice { +			b, err := swag.WriteJSON(data) +			if err != nil { +				return err +			} +			_, err = writer.Write(b) +			return err +		} +		if v.Kind() != reflect.String { +			return fmt.Errorf("%T is not a supported type by the TextProducer", data) +		} + +		_, err := writer.Write([]byte(v.String())) +		return err +	}) +} diff --git a/vendor/github.com/go-openapi/runtime/values.go b/vendor/github.com/go-openapi/runtime/values.go new file mode 100644 index 000000000..11f5732af --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/values.go @@ -0,0 +1,19 @@ +package runtime + +// Values typically represent parameters on a http request. +type Values map[string][]string + +// GetOK returns the values collection for the given key. +// When the key is present in the map it will return true for hasKey. +// When the value is not empty it will return true for hasValue. +func (v Values) GetOK(key string) (value []string, hasKey bool, hasValue bool) { +	value, hasKey = v[key] +	if !hasKey { +		return +	} +	if len(value) == 0 { +		return +	} +	hasValue = true +	return +} diff --git a/vendor/github.com/go-openapi/runtime/xml.go b/vendor/github.com/go-openapi/runtime/xml.go new file mode 100644 index 000000000..821c7393d --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/xml.go @@ -0,0 +1,36 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( +	"encoding/xml" +	"io" +) + +// XMLConsumer creates a new XML consumer +func XMLConsumer() Consumer { +	return ConsumerFunc(func(reader io.Reader, data interface{}) error { +		dec := xml.NewDecoder(reader) +		return dec.Decode(data) +	}) +} + +// XMLProducer creates a new XML producer +func XMLProducer() Producer { +	return ProducerFunc(func(writer io.Writer, data interface{}) error { +		enc := xml.NewEncoder(writer) +		return enc.Encode(data) +	}) +}  | 
