pax_global_header00006660000000000000000000000064141366676270014534gustar00rootroot0000000000000052 comment=12beb96a1ed2d9c4967e0672db7a84d71c4cf13b runtime-0.21.0/000077500000000000000000000000001413666762700132775ustar00rootroot00000000000000runtime-0.21.0/.editorconfig000066400000000000000000000010331413666762700157510ustar00rootroot00000000000000# 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 runtime-0.21.0/.gitattributes000066400000000000000000000000211413666762700161630ustar00rootroot00000000000000*.go text eol=lf runtime-0.21.0/.github/000077500000000000000000000000001413666762700146375ustar00rootroot00000000000000runtime-0.21.0/.github/CONTRIBUTING.md000066400000000000000000000114601413666762700170720ustar00rootroot00000000000000## Contribution Guidelines ### Pull requests are always welcome We are always thrilled to receive pull requests, and do our best to process them as fast as possible. Not sure if that typo is worth a pull request? Do it! We will appreciate it. If your pull request is not accepted on the first try, don't be discouraged! If there's a problem with the implementation, hopefully you received feedback on what to improve. We're trying very hard to keep go-swagger lean and focused. We don't want it to do everything for everybody. This means that we might decide against incorporating a new feature. However, there might be a way to implement that feature *on top of* go-swagger. ### Conventions Fork the repo and make changes on your fork in a feature branch: - If it's a bugfix branch, name it XXX-something where XXX is the number of the issue - If it's a feature branch, create an enhancement issue to announce your intentions, and name it XXX-something where XXX is the number of the issue. Submit unit tests for your changes. Go has a great test framework built in; use it! Take a look at existing tests for inspiration. Run the full test suite on your branch before submitting a pull request. Update the documentation when creating or modifying features. Test your documentation changes for clarity, concision, and correctness, as well as a clean documentation build. See ``docs/README.md`` for more information on building the docs and how docs get released. Write clean code. Universally formatted code promotes ease of writing, reading, and maintenance. Always run `gofmt -s -w file.go` on each changed file before committing your changes. Most editors have plugins that do this automatically. Pull requests descriptions should be as clear as possible and include a reference to all the issues that they address. Pull requests must not contain commits from other users or branches. Commit messages must start with a capitalized and short summary (max. 50 chars) written in the imperative, followed by an optional, more detailed explanatory text which is separated from the summary by an empty line. Code review comments may be added to your pull request. Discuss, then make the suggested modifications and push additional commits to your feature branch. Be sure to post a comment after pushing. The new commits will show up in the pull request automatically, but the reviewers will not be notified unless you comment. Before the pull request is merged, make sure that you squash your commits into logical units of work using `git rebase -i` and `git push -f`. After every commit the test suite should be passing. Include documentation changes in the same commit so that a revert would remove all traces of the feature or fix. Commits that fix or close an issue should include a reference like `Closes #XXX` or `Fixes #XXX`, which will automatically close the issue when merged. ### Sign your work The sign-off is a simple line at the end of the explanation for the patch, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below (from [developercertificate.org](http://developercertificate.org/)): ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` then you just add a line to every git commit message: Signed-off-by: Joe Smith using your real name (sorry, no pseudonyms or anonymous contributions.) You can add the sign off when creating the git commit via `git commit -s`. runtime-0.21.0/.github/workflows/000077500000000000000000000000001413666762700166745ustar00rootroot00000000000000runtime-0.21.0/.github/workflows/ci.yaml000066400000000000000000000012271413666762700201550ustar00rootroot00000000000000name: Go on: [push, pull_request] jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] fail-fast: false steps: - uses: actions/checkout@v2 - name: Set up Go uses: actions/setup-go@v2 with: go-version: 1.17.1 - name: Setup gotestsum uses: autero1/action-gotestsum@v1.0.0 with: gotestsum_version: 1.7.0 - name: Test run: gotestsum --format short-verbose -- -race -timeout=20m -coverprofile=coverage_txt -covermode=atomic ./... - uses: codecov/codecov-action@v2 with: files: coverage_txt runtime-0.21.0/.github/workflows/lint.yaml000066400000000000000000000004171413666762700205300ustar00rootroot00000000000000name: golangci-lint on: [push, pull_request] jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: only-new-issues: true runtime-0.21.0/.gitignore000066400000000000000000000000601413666762700152630ustar00rootroot00000000000000secrets.yml coverage.out *.cov *.out playground runtime-0.21.0/.golangci.yml000066400000000000000000000012271413666762700156650ustar00rootroot00000000000000linters-settings: govet: check-shadowing: true 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 runtime-0.21.0/CODE_OF_CONDUCT.md000066400000000000000000000062411413666762700161010ustar00rootroot00000000000000# 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/ runtime-0.21.0/LICENSE000066400000000000000000000261361413666762700143140ustar00rootroot00000000000000 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. runtime-0.21.0/README.md000066400000000000000000000013221413666762700145540ustar00rootroot00000000000000# runtime [![Build Status](https://travis-ci.org/go-openapi/runtime.svg?branch=client-context)](https://travis-ci.org/go-openapi/runtime) [![codecov](https://codecov.io/gh/go-openapi/runtime/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/runtime) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) [![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/runtime/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/runtime?status.svg)](http://godoc.org/github.com/go-openapi/runtime) # golang Open-API toolkit - runtime The runtime component for use in codegeneration or as untyped usage. runtime-0.21.0/authinfo_test.go000066400000000000000000000021011413666762700164740ustar00rootroot00000000000000// 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 ( "testing" "github.com/go-openapi/strfmt" "github.com/stretchr/testify/assert" ) func TestAuthInfoWriter(t *testing.T) { hand := ClientAuthInfoWriterFunc(func(r ClientRequest, _ strfmt.Registry) error { return r.SetHeaderParam("authorization", "Bearer the-token-goes-here") }) tr := new(TestClientRequest) err := hand.AuthenticateRequest(tr, nil) assert.NoError(t, err) assert.Equal(t, "Bearer the-token-goes-here", tr.Headers.Get("Authorization")) } runtime-0.21.0/bytestream.go000066400000000000000000000076631413666762700160210ustar00rootroot00000000000000// 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 consmer 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 } } 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 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 } } 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") }) } runtime-0.21.0/bytestream_test.go000066400000000000000000000141441413666762700170500ustar00rootroot00000000000000package runtime import ( "bytes" "errors" "fmt" "sync/atomic" "testing" "github.com/stretchr/testify/assert" ) func TestByteStreamConsumer(t *testing.T) { cons := ByteStreamConsumer() expected := "the data for the stream to be sent over the wire" // can consume as a Writer var b bytes.Buffer if assert.NoError(t, cons.Consume(bytes.NewBufferString(expected), &b)) { assert.Equal(t, expected, b.String()) } // can consume as an UnmarshalBinary var bu binaryUnmarshalDummy if assert.NoError(t, cons.Consume(bytes.NewBufferString(expected), &bu)) { assert.Equal(t, expected, bu.str) } // can consume as a binary slice var bs []byte if assert.NoError(t, cons.Consume(bytes.NewBufferString(expected), &bs)) { assert.Equal(t, expected, string(bs)) } type binarySlice []byte var bs2 binarySlice if assert.NoError(t, cons.Consume(bytes.NewBufferString(expected), &bs2)) { assert.Equal(t, expected, string(bs2)) } // passing in a nilslice wil result in an error var ns *[]byte assert.Error(t, cons.Consume(bytes.NewBufferString(expected), &ns)) // passing in nil wil result in an error as well assert.Error(t, cons.Consume(bytes.NewBufferString(expected), nil)) // a reader who results in an error, will make it fail assert.Error(t, cons.Consume(new(nopReader), &bu)) assert.Error(t, cons.Consume(new(nopReader), &bs)) // the readers can also not be nil assert.Error(t, cons.Consume(nil, &bs)) } type binaryUnmarshalDummy struct { str string } func (b *binaryUnmarshalDummy) UnmarshalBinary(bytes []byte) error { if len(bytes) == 0 { return errors.New("no text given") } b.str = string(bytes) return nil } func TestByteStreamProducer(t *testing.T) { cons := ByteStreamProducer() expected := "the data for the stream to be sent over the wire" var rdr bytes.Buffer // can produce using a reader if assert.NoError(t, cons.Produce(&rdr, bytes.NewBufferString(expected))) { assert.Equal(t, expected, rdr.String()) rdr.Reset() } // can produce using a binary marshaller if assert.NoError(t, cons.Produce(&rdr, &binaryMarshalDummy{expected})) { assert.Equal(t, expected, rdr.String()) rdr.Reset() } // string can also be used to produce if assert.NoError(t, cons.Produce(&rdr, expected)) { assert.Equal(t, expected, rdr.String()) rdr.Reset() } // binary slices can also be used to produce if assert.NoError(t, cons.Produce(&rdr, []byte(expected))) { assert.Equal(t, expected, rdr.String()) rdr.Reset() } // errors can also be used to produce if assert.NoError(t, cons.Produce(&rdr, errors.New(expected))) { assert.Equal(t, expected, rdr.String()) rdr.Reset() } // structs can also be used to produce if assert.NoError(t, cons.Produce(&rdr, Error{Message: expected})) { assert.Equal(t, fmt.Sprintf(`{"message":%q}`, expected), rdr.String()) rdr.Reset() } // struct pointers can also be used to produce if assert.NoError(t, cons.Produce(&rdr, &Error{Message: expected})) { assert.Equal(t, fmt.Sprintf(`{"message":%q}`, expected), rdr.String()) rdr.Reset() } // slices can also be used to produce if assert.NoError(t, cons.Produce(&rdr, []string{expected})) { assert.Equal(t, fmt.Sprintf(`[%q]`, expected), rdr.String()) rdr.Reset() } type binarySlice []byte if assert.NoError(t, cons.Produce(&rdr, binarySlice(expected))) { assert.Equal(t, expected, rdr.String()) rdr.Reset() } // when binaryMarshal data is used, its potential error gets propagated assert.Error(t, cons.Produce(&rdr, new(binaryMarshalDummy))) // nil data should never be accepted either assert.Error(t, cons.Produce(&rdr, nil)) // nil readers should also never be acccepted assert.Error(t, cons.Produce(nil, bytes.NewBufferString(expected))) } type binaryMarshalDummy struct { str string } func (b *binaryMarshalDummy) MarshalBinary() ([]byte, error) { if len(b.str) == 0 { return nil, errors.New("no text set") } return []byte(b.str), nil } type closingWriter struct { calledClose int64 calledWrite int64 b bytes.Buffer } func (c *closingWriter) Close() error { atomic.AddInt64(&c.calledClose, 1) return nil } func (c *closingWriter) Write(p []byte) (n int, err error) { atomic.AddInt64(&c.calledWrite, 1) return c.b.Write(p) } func (c *closingWriter) String() string { return c.b.String() } type closingReader struct { calledClose int64 calledRead int64 b *bytes.Buffer } func (c *closingReader) Close() error { atomic.AddInt64(&c.calledClose, 1) return nil } func (c *closingReader) Read(p []byte) (n int, err error) { atomic.AddInt64(&c.calledRead, 1) return c.b.Read(p) } func TestBytestreamConsumer_Close(t *testing.T) { cons := ByteStreamConsumer(ClosesStream) expected := "the data for the stream to be sent over the wire" // can consume as a Writer var b bytes.Buffer r := &closingReader{b: bytes.NewBufferString(expected)} if assert.NoError(t, cons.Consume(r, &b)) { assert.Equal(t, expected, b.String()) assert.EqualValues(t, 1, r.calledClose) } // can consume as a Writer cons = ByteStreamConsumer() b.Reset() r = &closingReader{b: bytes.NewBufferString(expected)} if assert.NoError(t, cons.Consume(r, &b)) { assert.Equal(t, expected, b.String()) assert.EqualValues(t, 0, r.calledClose) } } func TestBytestreamProducer_Close(t *testing.T) { cons := ByteStreamProducer(ClosesStream) expected := "the data for the stream to be sent over the wire" // can consume as a Writer r := &closingWriter{} // can produce using a reader if assert.NoError(t, cons.Produce(r, bytes.NewBufferString(expected))) { assert.Equal(t, expected, r.String()) assert.EqualValues(t, 1, r.calledClose) } cons = ByteStreamProducer() r = &closingWriter{} // can produce using a reader if assert.NoError(t, cons.Produce(r, bytes.NewBufferString(expected))) { assert.Equal(t, expected, r.String()) assert.EqualValues(t, 0, r.calledClose) } cons = ByteStreamProducer() r = &closingWriter{} data := &closingReader{b: bytes.NewBufferString(expected)} // can produce using a readcloser if assert.NoError(t, cons.Produce(r, data)) { assert.Equal(t, expected, r.String()) assert.EqualValues(t, 0, r.calledClose) assert.EqualValues(t, 1, data.calledClose) } } runtime-0.21.0/client/000077500000000000000000000000001413666762700145555ustar00rootroot00000000000000runtime-0.21.0/client/auth_info.go000066400000000000000000000050161413666762700170620ustar00rootroot00000000000000// 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 client import ( "encoding/base64" "github.com/go-openapi/strfmt" "github.com/go-openapi/runtime" ) // PassThroughAuth never manipulates the request var PassThroughAuth runtime.ClientAuthInfoWriter func init() { PassThroughAuth = runtime.ClientAuthInfoWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error { return nil }) } // BasicAuth provides a basic auth info writer func BasicAuth(username, password string) runtime.ClientAuthInfoWriter { return runtime.ClientAuthInfoWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error { encoded := base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) return r.SetHeaderParam("Authorization", "Basic "+encoded) }) } // APIKeyAuth provides an API key auth info writer func APIKeyAuth(name, in, value string) runtime.ClientAuthInfoWriter { if in == "query" { return runtime.ClientAuthInfoWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error { return r.SetQueryParam(name, value) }) } if in == "header" { return runtime.ClientAuthInfoWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error { return r.SetHeaderParam(name, value) }) } return nil } // BearerToken provides a header based oauth2 bearer access token auth info writer func BearerToken(token string) runtime.ClientAuthInfoWriter { return runtime.ClientAuthInfoWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error { return r.SetHeaderParam("Authorization", "Bearer "+token) }) } // Compose combines multiple ClientAuthInfoWriters into a single one. // Useful when multiple auth headers are needed. func Compose(auths ...runtime.ClientAuthInfoWriter) runtime.ClientAuthInfoWriter { return runtime.ClientAuthInfoWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error { for _, auth := range auths { if auth == nil { continue } if err := auth.AuthenticateRequest(r, nil); err != nil { return err } } return nil }) } runtime-0.21.0/client/auth_info_test.go000066400000000000000000000044561413666762700201300ustar00rootroot00000000000000// 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 client import ( "net/http" "testing" "github.com/stretchr/testify/assert" ) func TestBasicAuth(t *testing.T) { r, _ := newRequest("GET", "/", nil) writer := BasicAuth("someone", "with a password") err := writer.AuthenticateRequest(r, nil) assert.NoError(t, err) req := new(http.Request) req.Header = make(http.Header) req.Header.Set("Authorization", r.header.Get("Authorization")) usr, pw, ok := req.BasicAuth() if assert.True(t, ok) { assert.Equal(t, "someone", usr) assert.Equal(t, "with a password", pw) } } func TestAPIKeyAuth_Query(t *testing.T) { r, _ := newRequest("GET", "/", nil) writer := APIKeyAuth("api_key", "query", "the-shared-key") err := writer.AuthenticateRequest(r, nil) assert.NoError(t, err) assert.Equal(t, "the-shared-key", r.query.Get("api_key")) } func TestAPIKeyAuth_Header(t *testing.T) { r, _ := newRequest("GET", "/", nil) writer := APIKeyAuth("x-api-token", "header", "the-shared-key") err := writer.AuthenticateRequest(r, nil) assert.NoError(t, err) assert.Equal(t, "the-shared-key", r.header.Get("x-api-token")) } func TestBearerTokenAuth(t *testing.T) { r, _ := newRequest("GET", "/", nil) writer := BearerToken("the-shared-token") err := writer.AuthenticateRequest(r, nil) assert.NoError(t, err) assert.Equal(t, "Bearer the-shared-token", r.header.Get("Authorization")) } func TestCompose(t *testing.T) { r, _ := newRequest("GET", "/", nil) writer := Compose(APIKeyAuth("x-api-key", "header", "the-api-key"), APIKeyAuth("x-secret-key", "header", "the-secret-key")) err := writer.AuthenticateRequest(r, nil) assert.NoError(t, err) assert.Equal(t, "the-api-key", r.header.Get("x-api-key")) assert.Equal(t, "the-secret-key", r.header.Get("x-secret-key")) } runtime-0.21.0/client/keepalive.go000066400000000000000000000023371413666762700170560ustar00rootroot00000000000000package client import ( "io" "io/ioutil" "net/http" "sync/atomic" ) // KeepAliveTransport drains the remaining body from a response // so that go will reuse the TCP connections. // This is not enabled by default because there are servers where // the response never gets closed and that would make the code hang forever. // So instead it's provided as a http client middleware that can be used to override // any request. func KeepAliveTransport(rt http.RoundTripper) http.RoundTripper { return &keepAliveTransport{wrapped: rt} } type keepAliveTransport struct { wrapped http.RoundTripper } func (k *keepAliveTransport) RoundTrip(r *http.Request) (*http.Response, error) { resp, err := k.wrapped.RoundTrip(r) if err != nil { return resp, err } resp.Body = &drainingReadCloser{rdr: resp.Body} return resp, nil } type drainingReadCloser struct { rdr io.ReadCloser seenEOF uint32 } func (d *drainingReadCloser) Read(p []byte) (n int, err error) { n, err = d.rdr.Read(p) if err == io.EOF || n == 0 { atomic.StoreUint32(&d.seenEOF, 1) } return } func (d *drainingReadCloser) Close() error { // drain buffer if atomic.LoadUint32(&d.seenEOF) != 1 { //#nosec io.Copy(ioutil.Discard, d.rdr) } return d.rdr.Close() } runtime-0.21.0/client/keepalive_test.go000066400000000000000000000033711413666762700201140ustar00rootroot00000000000000package client import ( "bytes" "io" "io/ioutil" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func newCountingReader(rdr io.Reader, readOnce bool) *countingReadCloser { return &countingReadCloser{ rdr: rdr, readOnce: readOnce, } } type countingReadCloser struct { rdr io.Reader readOnce bool readCalled int closeCalled int } func (c *countingReadCloser) Read(b []byte) (int, error) { c.readCalled++ if c.readCalled > 1 && c.readOnce { return 0, io.EOF } return c.rdr.Read(b) } func (c *countingReadCloser) Close() error { c.closeCalled++ return nil } func TestDrainingReadCloser(t *testing.T) { rdr := newCountingReader(bytes.NewBufferString("There are many things to do"), false) prevDisc := ioutil.Discard disc := bytes.NewBuffer(nil) ioutil.Discard = disc defer func() { ioutil.Discard = prevDisc }() buf := make([]byte, 5) ts := &drainingReadCloser{rdr: rdr} _, err := ts.Read(buf) require.NoError(t, err) ts.Close() assert.Equal(t, "There", string(buf)) assert.Equal(t, " are many things to do", disc.String()) assert.Equal(t, 3, rdr.readCalled) assert.Equal(t, 1, rdr.closeCalled) } func TestDrainingReadCloser_SeenEOF(t *testing.T) { rdr := newCountingReader(bytes.NewBufferString("There are many things to do"), true) prevDisc := ioutil.Discard disc := bytes.NewBuffer(nil) ioutil.Discard = disc defer func() { ioutil.Discard = prevDisc }() buf := make([]byte, 5) ts := &drainingReadCloser{rdr: rdr} _, err := ts.Read(buf) assert.NoError(t, err) _, err = ts.Read(nil) assert.Equal(t, io.EOF, err) ts.Close() assert.Equal(t, string(buf), "There") assert.Equal(t, disc.String(), "") assert.Equal(t, 2, rdr.readCalled) assert.Equal(t, 1, rdr.closeCalled) } runtime-0.21.0/client/opentracing.go000066400000000000000000000046051413666762700174220ustar00rootroot00000000000000package client import ( "fmt" "net/http" "github.com/go-openapi/strfmt" "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/opentracing/opentracing-go/log" "github.com/go-openapi/runtime" ) type tracingTransport struct { transport runtime.ClientTransport host string opts []opentracing.StartSpanOption } func newOpenTracingTransport(transport runtime.ClientTransport, host string, opts []opentracing.StartSpanOption, ) runtime.ClientTransport { return &tracingTransport{ transport: transport, host: host, opts: opts, } } func (t *tracingTransport) Submit(op *runtime.ClientOperation) (interface{}, error) { if op.Context == nil { return t.transport.Submit(op) } params := op.Params reader := op.Reader var span opentracing.Span defer func() { if span != nil { span.Finish() } }() op.Params = runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { span = createClientSpan(op, req.GetHeaderParams(), t.host, t.opts) return params.WriteToRequest(req, reg) }) op.Reader = runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if span != nil { code := response.Code() ext.HTTPStatusCode.Set(span, uint16(code)) if code >= 400 { ext.Error.Set(span, true) } } return reader.ReadResponse(response, consumer) }) submit, err := t.transport.Submit(op) if err != nil && span != nil { ext.Error.Set(span, true) span.LogFields(log.Error(err)) } return submit, err } func createClientSpan(op *runtime.ClientOperation, header http.Header, host string, opts []opentracing.StartSpanOption) opentracing.Span { ctx := op.Context span := opentracing.SpanFromContext(ctx) if span != nil { opts = append(opts, ext.SpanKindRPCClient) span, _ = opentracing.StartSpanFromContextWithTracer( ctx, span.Tracer(), operationName(op), opts...) ext.Component.Set(span, "go-openapi") ext.PeerHostname.Set(span, host) span.SetTag("http.path", op.PathPattern) ext.HTTPMethod.Set(span, op.Method) _ = span.Tracer().Inject( span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(header)) return span } return nil } func operationName(op *runtime.ClientOperation) string { if op.ID != "" { return op.ID } return fmt.Sprintf("%s_%s", op.Method, op.PathPattern) } runtime-0.21.0/client/opentracing_test.go000066400000000000000000000074211413666762700204600ustar00rootroot00000000000000package client import ( "bytes" "context" "io" "io/ioutil" "testing" "github.com/go-openapi/strfmt" "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/opentracing/opentracing-go/mocktracer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/go-openapi/runtime" ) type tres struct { } func (r tres) Code() int { return 490 } func (r tres) Message() string { return "the message" } func (r tres) GetHeader(_ string) string { return "the header" } func (r tres) GetHeaders(_ string) []string { return []string{"the headers", "the headers2"} } func (r tres) Body() io.ReadCloser { return ioutil.NopCloser(bytes.NewBufferString("the content")) } type mockRuntime struct { req runtime.TestClientRequest } func (m *mockRuntime) Submit(operation *runtime.ClientOperation) (interface{}, error) { _ = operation.Params.WriteToRequest(&m.req, nil) _, _ = operation.Reader.ReadResponse(&tres{}, nil) return nil, nil } func testOperation(ctx context.Context) *runtime.ClientOperation { return &runtime.ClientOperation{ ID: "getCluster", Method: "GET", PathPattern: "/kubernetes-clusters/{cluster_id}", ProducesMediaTypes: []string{"application/json"}, ConsumesMediaTypes: []string{"application/json"}, Schemes: []string{"https"}, Reader: runtime.ClientResponseReaderFunc(func(runtime.ClientResponse, runtime.Consumer) (interface{}, error) { return nil, nil }), Params: runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { return nil }), AuthInfo: PassThroughAuth, Context: ctx, } } func Test_TracingRuntime_submit(t *testing.T) { t.Parallel() tracer := mocktracer.New() _, ctx := opentracing.StartSpanFromContextWithTracer(context.Background(), tracer, "op") testSubmit(t, testOperation(ctx), tracer, 1) } func Test_TracingRuntime_submit_nilAuthInfo(t *testing.T) { t.Parallel() tracer := mocktracer.New() _, ctx := opentracing.StartSpanFromContextWithTracer(context.Background(), tracer, "op") operation := testOperation(ctx) operation.AuthInfo = nil testSubmit(t, operation, tracer, 1) } func Test_TracingRuntime_submit_nilContext(t *testing.T) { t.Parallel() tracer := mocktracer.New() _, ctx := opentracing.StartSpanFromContextWithTracer(context.Background(), tracer, "op") operation := testOperation(ctx) operation.Context = nil testSubmit(t, operation, tracer, 0) // just don't panic } func testSubmit(t *testing.T, operation *runtime.ClientOperation, tracer *mocktracer.MockTracer, expectedSpans int) { header := map[string][]string{} r := newOpenTracingTransport(&mockRuntime{runtime.TestClientRequest{Headers: header}}, "remote_host", []opentracing.StartSpanOption{opentracing.Tag{ Key: string(ext.PeerService), Value: "service", }}) _, err := r.Submit(operation) require.NoError(t, err) assert.Len(t, tracer.FinishedSpans(), expectedSpans) if expectedSpans == 1 { span := tracer.FinishedSpans()[0] assert.Equal(t, "getCluster", span.OperationName) assert.Equal(t, map[string]interface{}{ "component": "go-openapi", "http.method": "GET", "http.path": "/kubernetes-clusters/{cluster_id}", "http.status_code": uint16(490), "peer.hostname": "remote_host", "peer.service": "service", "span.kind": ext.SpanKindRPCClientEnum, "error": true, }, span.Tags()) } } func Test_injectSpanContext(t *testing.T) { t.Parallel() tracer := mocktracer.New() _, ctx := opentracing.StartSpanFromContextWithTracer(context.Background(), tracer, "op") header := map[string][]string{} createClientSpan(testOperation(ctx), header, "", nil) // values are random - just check that something was injected assert.Len(t, header, 3) } runtime-0.21.0/client/request.go000066400000000000000000000313601413666762700165770ustar00rootroot00000000000000// 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 client import ( "bytes" "fmt" "io" "log" "mime/multipart" "net/http" "net/textproto" "net/url" "os" "path" "path/filepath" "strings" "time" "github.com/go-openapi/strfmt" "github.com/go-openapi/runtime" ) // NewRequest creates a new swagger http client request func newRequest(method, pathPattern string, writer runtime.ClientRequestWriter) (*request, error) { return &request{ pathPattern: pathPattern, method: method, writer: writer, header: make(http.Header), query: make(url.Values), timeout: DefaultTimeout, getBody: getRequestBuffer, }, nil } // Request represents a swagger client request. // // This Request struct converts to a HTTP request. // There might be others that convert to other transports. // There is no error checking here, it is assumed to be used after a spec has been validated. // so impossible combinations should not arise (hopefully). // // The main purpose of this struct is to hide the machinery of adding params to a transport request. // The generated code only implements what is necessary to turn a param into a valid value for these methods. type request struct { pathPattern string method string writer runtime.ClientRequestWriter pathParams map[string]string header http.Header query url.Values formFields url.Values fileFields map[string][]runtime.NamedReadCloser payload interface{} timeout time.Duration buf *bytes.Buffer getBody func(r *request) []byte } var ( // ensure interface compliance _ runtime.ClientRequest = new(request) ) func (r *request) isMultipart(mediaType string) bool { if len(r.fileFields) > 0 { return true } return runtime.MultipartFormMime == mediaType } // BuildHTTP creates a new http request based on the data from the params func (r *request) BuildHTTP(mediaType, basePath string, producers map[string]runtime.Producer, registry strfmt.Registry) (*http.Request, error) { return r.buildHTTP(mediaType, basePath, producers, registry, nil) } func escapeQuotes(s string) string { return strings.NewReplacer("\\", "\\\\", `"`, "\\\"").Replace(s) } func (r *request) buildHTTP(mediaType, basePath string, producers map[string]runtime.Producer, registry strfmt.Registry, auth runtime.ClientAuthInfoWriter) (*http.Request, error) { // build the data if err := r.writer.WriteToRequest(r, registry); err != nil { return nil, err } // Our body must be an io.Reader. // When we create the http.Request, if we pass it a // bytes.Buffer then it will wrap it in an io.ReadCloser // and set the content length automatically. var body io.Reader var pr *io.PipeReader var pw *io.PipeWriter r.buf = bytes.NewBuffer(nil) if r.payload != nil || len(r.formFields) > 0 || len(r.fileFields) > 0 { body = r.buf if r.isMultipart(mediaType) { pr, pw = io.Pipe() body = pr } } // check if this is a form type request if len(r.formFields) > 0 || len(r.fileFields) > 0 { if !r.isMultipart(mediaType) { r.header.Set(runtime.HeaderContentType, mediaType) formString := r.formFields.Encode() r.buf.WriteString(formString) goto DoneChoosingBodySource } mp := multipart.NewWriter(pw) r.header.Set(runtime.HeaderContentType, mangleContentType(mediaType, mp.Boundary())) go func() { defer func() { mp.Close() pw.Close() }() for fn, v := range r.formFields { for _, vi := range v { if err := mp.WriteField(fn, vi); err != nil { pw.CloseWithError(err) log.Println(err) return } } } defer func() { for _, ff := range r.fileFields { for _, ffi := range ff { ffi.Close() } } }() for fn, f := range r.fileFields { for _, fi := range f { // Need to read the data so that we can detect the content type buf := make([]byte, 512) size, err := fi.Read(buf) if err != nil { _ = pw.CloseWithError(err) log.Println(err) return } fileContentType := http.DetectContentType(buf) newFi := runtime.NamedReader(fi.Name(), io.MultiReader(bytes.NewReader(buf[:size]), fi)) // Create the MIME headers for the new part h := make(textproto.MIMEHeader) h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, escapeQuotes(fn), escapeQuotes(filepath.Base(fi.Name())))) h.Set("Content-Type", fileContentType) wrtr, err := mp.CreatePart(h) if err != nil { pw.CloseWithError(err) log.Println(err) return } if _, err := io.Copy(wrtr, newFi); err != nil { pw.CloseWithError(err) log.Println(err) } } } }() goto DoneChoosingBodySource } // if there is payload, use the producer to write the payload, and then // set the header to the content-type appropriate for the payload produced if r.payload != nil { // TODO: infer most appropriate content type based on the producer used, // and the `consumers` section of the spec/operation r.header.Set(runtime.HeaderContentType, mediaType) if rdr, ok := r.payload.(io.ReadCloser); ok { body = rdr goto DoneChoosingBodySource } if rdr, ok := r.payload.(io.Reader); ok { body = rdr goto DoneChoosingBodySource } producer := producers[mediaType] if err := producer.Produce(r.buf, r.payload); err != nil { return nil, err } } DoneChoosingBodySource: if runtime.CanHaveBody(r.method) && body == nil && r.header.Get(runtime.HeaderContentType) == "" { r.header.Set(runtime.HeaderContentType, mediaType) } if auth != nil { // If we're not using r.buf as our http.Request's body, // either the payload is an io.Reader or io.ReadCloser, // or we're doing a multipart form/file. // // In those cases, if the AuthenticateRequest call asks for the body, // we must read it into a buffer and provide that, then use that buffer // as the body of our http.Request. // // This is done in-line with the GetBody() request rather than ahead // of time, because there's no way to know if the AuthenticateRequest // will even ask for the body of the request. // // If for some reason the copy fails, there's no way to return that // error to the GetBody() call, so return it afterwards. // // An error from the copy action is prioritized over any error // from the AuthenticateRequest call, because the mis-read // body may have interfered with the auth. // var copyErr error if buf, ok := body.(*bytes.Buffer); body != nil && (!ok || buf != r.buf) { var copied bool r.getBody = func(r *request) []byte { if copied { return getRequestBuffer(r) } defer func() { copied = true }() if _, copyErr = io.Copy(r.buf, body); copyErr != nil { return nil } if closer, ok := body.(io.ReadCloser); ok { if copyErr = closer.Close(); copyErr != nil { return nil } } body = r.buf return getRequestBuffer(r) } } authErr := auth.AuthenticateRequest(r, registry) if copyErr != nil { return nil, fmt.Errorf("error retrieving the response body: %v", copyErr) } if authErr != nil { return nil, authErr } } // In case the basePath or the request pathPattern include static query parameters, // parse those out before constructing the final path. The parameters themselves // will be merged with the ones set by the client, with the priority given first to // the ones set by the client, then the path pattern, and lastly the base path. basePathURL, err := url.Parse(basePath) if err != nil { return nil, err } staticQueryParams := basePathURL.Query() pathPatternURL, err := url.Parse(r.pathPattern) if err != nil { return nil, err } for name, values := range pathPatternURL.Query() { if _, present := staticQueryParams[name]; present { staticQueryParams.Del(name) } for _, value := range values { staticQueryParams.Add(name, value) } } // create http request var reinstateSlash bool if pathPatternURL.Path != "" && pathPatternURL.Path != "/" && pathPatternURL.Path[len(pathPatternURL.Path)-1] == '/' { reinstateSlash = true } urlPath := path.Join(basePathURL.Path, pathPatternURL.Path) for k, v := range r.pathParams { urlPath = strings.Replace(urlPath, "{"+k+"}", url.PathEscape(v), -1) } if reinstateSlash { urlPath = urlPath + "/" } req, err := http.NewRequest(r.method, urlPath, body) if err != nil { return nil, err } originalParams := r.GetQueryParams() // Merge the query parameters extracted from the basePath with the ones set by // the client in this struct. In case of conflict, the client wins. for k, v := range staticQueryParams { _, present := originalParams[k] if !present { if err = r.SetQueryParam(k, v...); err != nil { return nil, err } } } req.URL.RawQuery = r.query.Encode() req.Header = r.header return req, nil } func mangleContentType(mediaType, boundary string) string { if strings.ToLower(mediaType) == runtime.URLencodedFormMime { return fmt.Sprintf("%s; boundary=%s", mediaType, boundary) } return "multipart/form-data; boundary=" + boundary } func (r *request) GetMethod() string { return r.method } func (r *request) GetPath() string { path := r.pathPattern for k, v := range r.pathParams { path = strings.Replace(path, "{"+k+"}", v, -1) } return path } func (r *request) GetBody() []byte { return r.getBody(r) } func getRequestBuffer(r *request) []byte { if r.buf == nil { return nil } return r.buf.Bytes() } // SetHeaderParam adds a header param to the request // when there is only 1 value provided for the varargs, it will set it. // when there are several values provided for the varargs it will add it (no overriding) func (r *request) SetHeaderParam(name string, values ...string) error { if r.header == nil { r.header = make(http.Header) } r.header[http.CanonicalHeaderKey(name)] = values return nil } // GetHeaderParams returns the all headers currently set for the request func (r *request) GetHeaderParams() http.Header { return r.header } // SetQueryParam adds a query param to the request // when there is only 1 value provided for the varargs, it will set it. // when there are several values provided for the varargs it will add it (no overriding) func (r *request) SetQueryParam(name string, values ...string) error { if r.query == nil { r.query = make(url.Values) } r.query[name] = values return nil } // GetQueryParams returns a copy of all query params currently set for the request func (r *request) GetQueryParams() url.Values { var result = make(url.Values) for key, value := range r.query { result[key] = append([]string{}, value...) } return result } // SetFormParam adds a forn param to the request // when there is only 1 value provided for the varargs, it will set it. // when there are several values provided for the varargs it will add it (no overriding) func (r *request) SetFormParam(name string, values ...string) error { if r.formFields == nil { r.formFields = make(url.Values) } r.formFields[name] = values return nil } // SetPathParam adds a path param to the request func (r *request) SetPathParam(name string, value string) error { if r.pathParams == nil { r.pathParams = make(map[string]string) } r.pathParams[name] = value return nil } // SetFileParam adds a file param to the request func (r *request) SetFileParam(name string, files ...runtime.NamedReadCloser) error { for _, file := range files { if actualFile, ok := file.(*os.File); ok { fi, err := os.Stat(actualFile.Name()) if err != nil { return err } if fi.IsDir() { return fmt.Errorf("%q is a directory, only files are supported", file.Name()) } } } if r.fileFields == nil { r.fileFields = make(map[string][]runtime.NamedReadCloser) } if r.formFields == nil { r.formFields = make(url.Values) } r.fileFields[name] = files return nil } func (r *request) GetFileParam() map[string][]runtime.NamedReadCloser { return r.fileFields } // SetBodyParam sets a body parameter on the request. // This does not yet serialze the object, this happens as late as possible. func (r *request) SetBodyParam(payload interface{}) error { r.payload = payload return nil } func (r *request) GetBodyParam() interface{} { return r.payload } // SetTimeout sets the timeout for a request func (r *request) SetTimeout(timeout time.Duration) error { r.timeout = timeout return nil } runtime-0.21.0/client/request_test.go000066400000000000000000000610501413666762700176350ustar00rootroot00000000000000// 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 client import ( "bytes" "encoding/json" "encoding/xml" "errors" "io" "io/ioutil" "mime" "mime/multipart" "net/http" "net/http/httptest" "net/url" "os" "path/filepath" "strings" "testing" "github.com/go-openapi/strfmt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/go-openapi/runtime" ) var testProducers = map[string]runtime.Producer{ runtime.JSONMime: runtime.JSONProducer(), runtime.XMLMime: runtime.XMLProducer(), runtime.TextMime: runtime.TextProducer(), } func TestBuildRequest_SetHeaders(t *testing.T) { r, _ := newRequest("GET", "/flats/{id}/", nil) // single value _ = r.SetHeaderParam("X-Rate-Limit", "500") assert.Equal(t, "500", r.header.Get("X-Rate-Limit")) _ = r.SetHeaderParam("X-Rate-Limit", "400") assert.Equal(t, "400", r.header.Get("X-Rate-Limit")) // multi value _ = r.SetHeaderParam("X-Accepts", "json", "xml", "yaml") assert.EqualValues(t, []string{"json", "xml", "yaml"}, r.header["X-Accepts"]) } func TestBuildRequest_SetPath(t *testing.T) { r, _ := newRequest("GET", "/flats/{id}/?hello=world", nil) _ = r.SetPathParam("id", "1345") assert.Equal(t, "1345", r.pathParams["id"]) } func TestBuildRequest_SetQuery(t *testing.T) { r, _ := newRequest("GET", "/flats/{id}/", nil) // single value _ = r.SetQueryParam("hello", "there") assert.Equal(t, "there", r.query.Get("hello")) // multi value _ = r.SetQueryParam("goodbye", "cruel", "world") assert.Equal(t, []string{"cruel", "world"}, r.query["goodbye"]) } func TestBuildRequest_SetForm(t *testing.T) { // non-multipart r, _ := newRequest("POST", "/flats", nil) _ = r.SetFormParam("hello", "world") assert.Equal(t, "world", r.formFields.Get("hello")) _ = r.SetFormParam("goodbye", "cruel", "world") assert.Equal(t, []string{"cruel", "world"}, r.formFields["goodbye"]) } func TestBuildRequest_SetFile(t *testing.T) { // needs to convert form to multipart r, _ := newRequest("POST", "/flats/{id}/image", nil) // error if it isn't there err := r.SetFileParam("not there", os.NewFile(0, "./i-dont-exist")) assert.Error(t, err) // error if it isn't a file err = r.SetFileParam("directory", os.NewFile(0, "../client")) assert.Error(t, err) // success adds it to the map err = r.SetFileParam("file", mustGetFile("./runtime.go")) if assert.NoError(t, err) { fl, ok := r.fileFields["file"] if assert.True(t, ok) { assert.Equal(t, "runtime.go", filepath.Base(fl[0].Name())) } } // success adds a file param with multiple files err = r.SetFileParam("otherfiles", mustGetFile("./runtime.go"), mustGetFile("./request.go")) if assert.NoError(t, err) { fl, ok := r.fileFields["otherfiles"] if assert.True(t, ok) { assert.Equal(t, "runtime.go", filepath.Base(fl[0].Name())) assert.Equal(t, "request.go", filepath.Base(fl[1].Name())) } } } func mustGetFile(path string) *os.File { f, err := os.Open(path) if err != nil { panic(err) } return f } func TestBuildRequest_SetBody(t *testing.T) { r, _ := newRequest("GET", "/flats/{id}/?hello=world", nil) bd := []struct{ Name, Hobby string }{{"Tom", "Organ trail"}, {"John", "Bird watching"}} _ = r.SetBodyParam(bd) assert.Equal(t, bd, r.payload) } func TestBuildRequest_BuildHTTP_NoPayload(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(nil) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("POST", "/flats/{id}/", reqWrtr) req, err := r.BuildHTTP(runtime.JSONMime, "", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) assert.Equal(t, runtime.JSONMime, req.Header.Get(runtime.HeaderContentType)) } } func TestBuildRequest_BuildHTTP_Payload(t *testing.T) { bd := []struct{ Name, Hobby string }{{"Tom", "Organ trail"}, {"John", "Bird watching"}} reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(bd) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.JSONMime) req, err := r.BuildHTTP(runtime.JSONMime, "", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expectedBody, _ := json.Marshal(bd) actualBody, _ := ioutil.ReadAll(req.Body) assert.Equal(t, append(expectedBody, '\n'), actualBody) } } func TestBuildRequest_BuildHTTP_SetsInAuth(t *testing.T) { bd := []struct{ Name, Hobby string }{{"Tom", "Organ trail"}, {"John", "Bird watching"}} reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(bd) _ = req.SetQueryParam("hello", "wrong") _ = req.SetPathParam("id", "wrong") _ = req.SetHeaderParam("X-Rate-Limit", "wrong") return nil }) auth := runtime.ClientAuthInfoWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(bd) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.JSONMime) req, err := r.buildHTTP(runtime.JSONMime, "", testProducers, nil, auth) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expectedBody, _ := json.Marshal(bd) actualBody, _ := ioutil.ReadAll(req.Body) assert.Equal(t, append(expectedBody, '\n'), actualBody) } } func TestBuildRequest_BuildHTTP_XMLPayload(t *testing.T) { bd := []struct { XMLName xml.Name `xml:"person"` Name string `xml:"name"` Hobby string `xml:"hobby"` }{{xml.Name{}, "Tom", "Organ trail"}, {xml.Name{}, "John", "Bird watching"}} reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(bd) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.XMLMime) req, err := r.BuildHTTP(runtime.XMLMime, "", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expectedBody, _ := xml.Marshal(bd) actualBody, _ := ioutil.ReadAll(req.Body) assert.Equal(t, expectedBody, actualBody) } } func TestBuildRequest_BuildHTTP_TextPayload(t *testing.T) { bd := "Tom: Organ trail; John: Bird watching" reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(bd) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.TextMime) req, err := r.BuildHTTP(runtime.TextMime, "", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expectedBody := []byte(bd) actualBody, _ := ioutil.ReadAll(req.Body) assert.Equal(t, expectedBody, actualBody) } } func TestBuildRequest_BuildHTTP_Form(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetFormParam("something", "some value") _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.JSONMime) req, err := r.BuildHTTP(runtime.JSONMime, "", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expected := []byte("something=some+value") actual, _ := ioutil.ReadAll(req.Body) assert.Equal(t, expected, actual) } } func TestBuildRequest_BuildHTTP_Form_URLEncoded(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetFormParam("something", "some value") _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.URLencodedFormMime) req, err := r.BuildHTTP(runtime.URLencodedFormMime, "", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, runtime.URLencodedFormMime, req.Header.Get(runtime.HeaderContentType)) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expected := []byte("something=some+value") actual, _ := ioutil.ReadAll(req.Body) assert.Equal(t, expected, actual) } } func TestBuildRequest_BuildHTTP_Form_Content_Length(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetFormParam("something", "some value") _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.MultipartFormMime) req, err := r.BuildHTTP(runtime.JSONMime, "", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) assert.Condition(t, func() bool { return req.ContentLength > 0 }, "ContentLength must great than 0. got %d", req.ContentLength) expected := []byte("something=some+value") actual, _ := ioutil.ReadAll(req.Body) assert.Equal(t, expected, actual) } } func TestBuildRequest_BuildHTTP_FormMultipart(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetFormParam("something", "some value") _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.MultipartFormMime) req, err := r.BuildHTTP(runtime.MultipartFormMime, "", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expected1 := []byte("Content-Disposition: form-data; name=\"something\"") expected2 := []byte("some value") actual, _ := ioutil.ReadAll(req.Body) actuallines := bytes.Split(actual, []byte("\r\n")) assert.Equal(t, 6, len(actuallines)) boundary := string(actuallines[0]) lastboundary := string(actuallines[4]) assert.True(t, strings.HasPrefix(boundary, "--")) assert.True(t, strings.HasPrefix(lastboundary, "--") && strings.HasSuffix(lastboundary, "--")) assert.Equal(t, lastboundary, boundary+"--") assert.Equal(t, expected1, actuallines[1]) assert.Equal(t, expected2, actuallines[3]) } } func TestBuildRequest_BuildHTTP_FormMultiples(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetFormParam("something", "some value", "another value") _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.MultipartFormMime) req, err := r.BuildHTTP(runtime.MultipartFormMime, "", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expected1 := []byte("Content-Disposition: form-data; name=\"something\"") expected2 := []byte("some value") expected3 := []byte("another value") actual, _ := ioutil.ReadAll(req.Body) actuallines := bytes.Split(actual, []byte("\r\n")) assert.Equal(t, 10, len(actuallines)) boundary := string(actuallines[0]) lastboundary := string(actuallines[8]) assert.True(t, strings.HasPrefix(boundary, "--")) assert.True(t, strings.HasPrefix(lastboundary, "--") && strings.HasSuffix(lastboundary, "--")) assert.Equal(t, lastboundary, boundary+"--") assert.Equal(t, expected1, actuallines[1]) assert.Equal(t, expected2, actuallines[3]) assert.Equal(t, actuallines[0], actuallines[4]) assert.Equal(t, expected1, actuallines[5]) assert.Equal(t, expected3, actuallines[7]) } } func TestBuildRequest_BuildHTTP_Files(t *testing.T) { cont, _ := ioutil.ReadFile("./runtime.go") cont2, _ := ioutil.ReadFile("./request.go") reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetFormParam("something", "some value") _ = req.SetFileParam("file", mustGetFile("./runtime.go")) _ = req.SetFileParam("otherfiles", mustGetFile("./runtime.go"), mustGetFile("./request.go")) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.JSONMime) req, err := r.BuildHTTP(runtime.JSONMime, "", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) mediaType, params, err := mime.ParseMediaType(req.Header.Get(runtime.HeaderContentType)) if assert.NoError(t, err) { assert.Equal(t, runtime.MultipartFormMime, mediaType) boundary := params["boundary"] mr := multipart.NewReader(req.Body, boundary) defer req.Body.Close() frm, err := mr.ReadForm(1 << 20) if assert.NoError(t, err) { assert.Equal(t, "some value", frm.Value["something"][0]) fileverifier := func(name string, index int, filename string, content []byte) { mpff := frm.File[name][index] mpf, _ := mpff.Open() defer mpf.Close() assert.Equal(t, filename, mpff.Filename) actual, _ := ioutil.ReadAll(mpf) assert.Equal(t, content, actual) } fileverifier("file", 0, "runtime.go", cont) fileverifier("otherfiles", 0, "runtime.go", cont) fileverifier("otherfiles", 1, "request.go", cont2) } } } } func TestBuildRequest_BuildHTTP_Files_URLEncoded(t *testing.T) { cont, _ := ioutil.ReadFile("./runtime.go") cont2, _ := ioutil.ReadFile("./request.go") reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetFormParam("something", "some value") _ = req.SetFileParam("file", mustGetFile("./runtime.go")) _ = req.SetFileParam("otherfiles", mustGetFile("./runtime.go"), mustGetFile("./request.go")) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.URLencodedFormMime) req, err := r.BuildHTTP(runtime.URLencodedFormMime, "", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) mediaType, params, err := mime.ParseMediaType(req.Header.Get(runtime.HeaderContentType)) if assert.NoError(t, err) { assert.Equal(t, runtime.URLencodedFormMime, mediaType) boundary := params["boundary"] mr := multipart.NewReader(req.Body, boundary) defer req.Body.Close() frm, err := mr.ReadForm(1 << 20) if assert.NoError(t, err) { assert.Equal(t, "some value", frm.Value["something"][0]) fileverifier := func(name string, index int, filename string, content []byte) { mpff := frm.File[name][index] mpf, _ := mpff.Open() defer mpf.Close() assert.Equal(t, filename, mpff.Filename) actual, _ := ioutil.ReadAll(mpf) assert.Equal(t, content, actual) } fileverifier("file", 0, "runtime.go", cont) fileverifier("otherfiles", 0, "runtime.go", cont) fileverifier("otherfiles", 1, "request.go", cont2) } } } } func TestBuildRequest_BuildHTTP_BasePath(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(nil) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("POST", "/flats/{id}/", reqWrtr) req, err := r.BuildHTTP(runtime.JSONMime, "/basepath", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/basepath/flats/1234/", req.URL.Path) assert.Equal(t, runtime.JSONMime, req.Header.Get(runtime.HeaderContentType)) } } func TestBuildRequest_BuildHTTP_EscapedPath(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(nil) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234/?*&^%") _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("POST", "/flats/{id}/", reqWrtr) req, err := r.BuildHTTP(runtime.JSONMime, "/basepath", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/basepath/flats/1234/?*&^%/", req.URL.Path) assert.Equal(t, "/basepath/flats/1234%2F%3F%2A&%5E%25/", req.URL.RawPath) assert.Equal(t, req.URL.RawPath, req.URL.EscapedPath()) assert.Equal(t, runtime.JSONMime, req.Header.Get(runtime.HeaderContentType)) } } func TestBuildRequest_BuildHTTP_BasePathWithQueryParameters(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(nil) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") return nil }) r, _ := newRequest("POST", "/flats/{id}/", reqWrtr) req, err := r.BuildHTTP(runtime.JSONMime, "/basepath?foo=bar", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "bar", req.URL.Query().Get("foo")) assert.Equal(t, "/basepath/flats/1234/", req.URL.Path) } } func TestBuildRequest_BuildHTTP_PathPatternWithQueryParameters(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(nil) _ = req.SetQueryParam("hello", "world") _ = req.SetPathParam("id", "1234") return nil }) r, _ := newRequest("POST", "/flats/{id}/?foo=bar", reqWrtr) req, err := r.BuildHTTP(runtime.JSONMime, "/basepath", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "bar", req.URL.Query().Get("foo")) assert.Equal(t, "/basepath/flats/1234/", req.URL.Path) } } func TestBuildRequest_BuildHTTP_StaticParametersPathPatternPrevails(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(nil) _ = req.SetPathParam("id", "1234") return nil }) r, _ := newRequest("POST", "/flats/{id}/?hello=world", reqWrtr) req, err := r.BuildHTTP(runtime.JSONMime, "/basepath?hello=kitty", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/basepath/flats/1234/", req.URL.Path) } } func TestBuildRequest_BuildHTTP_StaticParametersConflictClientPrevails(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { _ = req.SetBodyParam(nil) _ = req.SetQueryParam("hello", "there") _ = req.SetPathParam("id", "1234") return nil }) r, _ := newRequest("POST", "/flats/{id}/?hello=world", reqWrtr) req, err := r.BuildHTTP(runtime.JSONMime, "/basepath?hello=kitty", testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "there", req.URL.Query().Get("hello")) assert.Equal(t, "/basepath/flats/1234/", req.URL.Path) } } type testReqFn func(*testing.T, *http.Request) type testRoundTripper struct { tr http.RoundTripper testFn testReqFn testHarness *testing.T } func (t *testRoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) { t.testFn(t.testHarness, req) return t.tr.RoundTrip(req) } func TestGetBodyCallsBeforeRoundTrip(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusCreated) _, err := rw.Write([]byte("test result")) require.NoError(t, err) })) defer server.Close() hu, _ := url.Parse(server.URL) client := http.DefaultClient transport := http.DefaultTransport client.Transport = &testRoundTripper{ tr: transport, testHarness: t, testFn: func(t *testing.T, req *http.Request) { // Read the body once before sending the request body, err := req.GetBody() require.NoError(t, err) bodyContent, err := ioutil.ReadAll(io.Reader(body)) require.EqualValues(t, req.ContentLength, len(bodyContent)) require.NoError(t, err) require.EqualValues(t, "\"test body\"\n", string(bodyContent)) // Read the body a second time before sending the request body, err = req.GetBody() require.NoError(t, err) bodyContent, err = ioutil.ReadAll(io.Reader(body)) require.NoError(t, err) require.EqualValues(t, req.ContentLength, len(bodyContent)) require.EqualValues(t, "\"test body\"\n", string(bodyContent)) }, } rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return req.SetBodyParam("test body") }) operation := &runtime.ClientOperation{ ID: "getSites", Method: "POST", PathPattern: "/", Params: rwrtr, Client: client, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == http.StatusCreated { var result string if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Unexpected error code") }), } openAPIClient := New(hu.Host, "/", []string{"http"}) res, err := openAPIClient.Submit(operation) require.NoError(t, err) actual := res.(string) require.EqualValues(t, "test result", actual) } runtime-0.21.0/client/response.go000066400000000000000000000021521413666762700167420ustar00rootroot00000000000000// 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 client import ( "io" "net/http" "github.com/go-openapi/runtime" ) var _ runtime.ClientResponse = response{} type response struct { resp *http.Response } func (r response) Code() int { return r.resp.StatusCode } func (r response) Message() string { return r.resp.Status } func (r response) GetHeader(name string) string { return r.resp.Header.Get(name) } func (r response) GetHeaders(name string) []string { return r.resp.Header.Values(name) } func (r response) Body() io.ReadCloser { return r.resp.Body } runtime-0.21.0/client/response_test.go000066400000000000000000000024601413666762700200030ustar00rootroot00000000000000// 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 client import ( "bytes" "io/ioutil" "net/http" "testing" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" ) func TestResponse(t *testing.T) { under := new(http.Response) under.Status = "the status message" under.StatusCode = 392 under.Header = make(http.Header) under.Header.Set("Blah", "blah blah") under.Body = ioutil.NopCloser(bytes.NewBufferString("some content")) var resp runtime.ClientResponse = response{under} assert.EqualValues(t, under.StatusCode, resp.Code()) assert.Equal(t, under.Status, resp.Message()) assert.Equal(t, "blah blah", resp.GetHeader("blah")) assert.Equal(t, []string{"blah blah"}, resp.GetHeaders("blah")) assert.Equal(t, under.Body, resp.Body()) } runtime-0.21.0/client/runtime.go000066400000000000000000000347341413666762700166020ustar00rootroot00000000000000// 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 client import ( "context" "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/pem" "fmt" "io/ioutil" "mime" "net/http" "net/http/httputil" "strings" "sync" "time" "github.com/go-openapi/strfmt" "github.com/opentracing/opentracing-go" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/logger" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/runtime/yamlpc" ) // TLSClientOptions to configure client authentication with mutual TLS type TLSClientOptions struct { // Certificate is the path to a PEM-encoded certificate to be used for // client authentication. If set then Key must also be set. Certificate string // LoadedCertificate is the certificate to be used for client authentication. // This field is ignored if Certificate is set. If this field is set, LoadedKey // is also required. LoadedCertificate *x509.Certificate // Key is the path to an unencrypted PEM-encoded private key for client // authentication. This field is required if Certificate is set. Key string // LoadedKey is the key for client authentication. This field is required if // LoadedCertificate is set. LoadedKey crypto.PrivateKey // CA is a path to a PEM-encoded certificate that specifies the root certificate // to use when validating the TLS certificate presented by the server. If this field // (and LoadedCA) is not set, the system certificate pool is used. This field is ignored if LoadedCA // is set. CA string // LoadedCA specifies the root certificate to use when validating the server's TLS certificate. // If this field (and CA) is not set, the system certificate pool is used. LoadedCA *x509.Certificate // LoadedCAPool specifies a pool of RootCAs to use when validating the server's TLS certificate. // If set, it will be combined with the the other loaded certificates (see LoadedCA and CA). // If neither LoadedCA or CA is set, the provided pool with override the system // certificate pool. // The caller must not use the supplied pool after calling TLSClientAuth. LoadedCAPool *x509.CertPool // ServerName specifies the hostname to use when verifying the server certificate. // If this field is set then InsecureSkipVerify will be ignored and treated as // false. ServerName string // InsecureSkipVerify controls whether the certificate chain and hostname presented // by the server are validated. If true, any certificate is accepted. InsecureSkipVerify bool // VerifyPeerCertificate, if not nil, is called after normal // certificate verification. It receives the raw ASN.1 certificates // provided by the peer and also any verified chains that normal processing found. // If it returns a non-nil error, the handshake is aborted and that error results. // // If normal verification fails then the handshake will abort before // considering this callback. If normal verification is disabled by // setting InsecureSkipVerify then this callback will be considered but // the verifiedChains argument will always be nil. VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error // SessionTicketsDisabled may be set to true to disable session ticket and // PSK (resumption) support. Note that on clients, session ticket support is // also disabled if ClientSessionCache is nil. SessionTicketsDisabled bool // ClientSessionCache is a cache of ClientSessionState entries for TLS // session resumption. It is only used by clients. ClientSessionCache tls.ClientSessionCache // Prevents callers using unkeyed fields. _ struct{} } // TLSClientAuth creates a tls.Config for mutual auth func TLSClientAuth(opts TLSClientOptions) (*tls.Config, error) { // create client tls config cfg := &tls.Config{} // load client cert if specified if opts.Certificate != "" { cert, err := tls.LoadX509KeyPair(opts.Certificate, opts.Key) if err != nil { return nil, fmt.Errorf("tls client cert: %v", err) } cfg.Certificates = []tls.Certificate{cert} } else if opts.LoadedCertificate != nil { block := pem.Block{Type: "CERTIFICATE", Bytes: opts.LoadedCertificate.Raw} certPem := pem.EncodeToMemory(&block) var keyBytes []byte switch k := opts.LoadedKey.(type) { case *rsa.PrivateKey: keyBytes = x509.MarshalPKCS1PrivateKey(k) case *ecdsa.PrivateKey: var err error keyBytes, err = x509.MarshalECPrivateKey(k) if err != nil { return nil, fmt.Errorf("tls client priv key: %v", err) } default: return nil, fmt.Errorf("tls client priv key: unsupported key type") } block = pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes} keyPem := pem.EncodeToMemory(&block) cert, err := tls.X509KeyPair(certPem, keyPem) if err != nil { return nil, fmt.Errorf("tls client cert: %v", err) } cfg.Certificates = []tls.Certificate{cert} } cfg.InsecureSkipVerify = opts.InsecureSkipVerify cfg.VerifyPeerCertificate = opts.VerifyPeerCertificate cfg.SessionTicketsDisabled = opts.SessionTicketsDisabled cfg.ClientSessionCache = opts.ClientSessionCache // When no CA certificate is provided, default to the system cert pool // that way when a request is made to a server known by the system trust store, // the name is still verified if opts.LoadedCA != nil { caCertPool := basePool(opts.LoadedCAPool) caCertPool.AddCert(opts.LoadedCA) cfg.RootCAs = caCertPool } else if opts.CA != "" { // load ca cert caCert, err := ioutil.ReadFile(opts.CA) if err != nil { return nil, fmt.Errorf("tls client ca: %v", err) } caCertPool := basePool(opts.LoadedCAPool) caCertPool.AppendCertsFromPEM(caCert) cfg.RootCAs = caCertPool } else if opts.LoadedCAPool != nil { cfg.RootCAs = opts.LoadedCAPool } // apply servername overrride if opts.ServerName != "" { cfg.InsecureSkipVerify = false cfg.ServerName = opts.ServerName } cfg.BuildNameToCertificate() return cfg, nil } func basePool(pool *x509.CertPool) *x509.CertPool { if pool == nil { return x509.NewCertPool() } return pool } // TLSTransport creates a http client transport suitable for mutual tls auth func TLSTransport(opts TLSClientOptions) (http.RoundTripper, error) { cfg, err := TLSClientAuth(opts) if err != nil { return nil, err } return &http.Transport{TLSClientConfig: cfg}, nil } // TLSClient creates a http.Client for mutual auth func TLSClient(opts TLSClientOptions) (*http.Client, error) { transport, err := TLSTransport(opts) if err != nil { return nil, err } return &http.Client{Transport: transport}, nil } // DefaultTimeout the default request timeout var DefaultTimeout = 30 * time.Second // Runtime represents an API client that uses the transport // to make http requests based on a swagger specification. type Runtime struct { DefaultMediaType string DefaultAuthentication runtime.ClientAuthInfoWriter Consumers map[string]runtime.Consumer Producers map[string]runtime.Producer Transport http.RoundTripper Jar http.CookieJar //Spec *spec.Document Host string BasePath string Formats strfmt.Registry Context context.Context Debug bool logger logger.Logger clientOnce *sync.Once client *http.Client schemes []string } // New creates a new default runtime for a swagger api runtime.Client func New(host, basePath string, schemes []string) *Runtime { var rt Runtime rt.DefaultMediaType = runtime.JSONMime // TODO: actually infer this stuff from the spec rt.Consumers = map[string]runtime.Consumer{ runtime.YAMLMime: yamlpc.YAMLConsumer(), runtime.JSONMime: runtime.JSONConsumer(), runtime.XMLMime: runtime.XMLConsumer(), runtime.TextMime: runtime.TextConsumer(), runtime.HTMLMime: runtime.TextConsumer(), runtime.CSVMime: runtime.CSVConsumer(), runtime.DefaultMime: runtime.ByteStreamConsumer(), } rt.Producers = map[string]runtime.Producer{ runtime.YAMLMime: yamlpc.YAMLProducer(), runtime.JSONMime: runtime.JSONProducer(), runtime.XMLMime: runtime.XMLProducer(), runtime.TextMime: runtime.TextProducer(), runtime.HTMLMime: runtime.TextProducer(), runtime.CSVMime: runtime.CSVProducer(), runtime.DefaultMime: runtime.ByteStreamProducer(), } rt.Transport = http.DefaultTransport rt.Jar = nil rt.Host = host rt.BasePath = basePath rt.Context = context.Background() rt.clientOnce = new(sync.Once) if !strings.HasPrefix(rt.BasePath, "/") { rt.BasePath = "/" + rt.BasePath } rt.Debug = logger.DebugEnabled() rt.logger = logger.StandardLogger{} if len(schemes) > 0 { rt.schemes = schemes } return &rt } // NewWithClient allows you to create a new transport with a configured http.Client func NewWithClient(host, basePath string, schemes []string, client *http.Client) *Runtime { rt := New(host, basePath, schemes) if client != nil { rt.clientOnce.Do(func() { rt.client = client }) } return rt } // WithOpenTracing adds opentracing support to the provided runtime. // A new client span is created for each request. // If the context of the client operation does not contain an active span, no span is created. // The provided opts are applied to each spans - for example to add global tags. func (r *Runtime) WithOpenTracing(opts ...opentracing.StartSpanOption) runtime.ClientTransport { return newOpenTracingTransport(r, r.Host, opts) } func (r *Runtime) pickScheme(schemes []string) string { if v := r.selectScheme(r.schemes); v != "" { return v } if v := r.selectScheme(schemes); v != "" { return v } return "http" } func (r *Runtime) selectScheme(schemes []string) string { schLen := len(schemes) if schLen == 0 { return "" } scheme := schemes[0] // prefer https, but skip when not possible if scheme != "https" && schLen > 1 { for _, sch := range schemes { if sch == "https" { scheme = sch break } } } return scheme } func transportOrDefault(left, right http.RoundTripper) http.RoundTripper { if left == nil { return right } return left } // EnableConnectionReuse drains the remaining body from a response // so that go will reuse the TCP connections. // // This is not enabled by default because there are servers where // the response never gets closed and that would make the code hang forever. // So instead it's provided as a http client middleware that can be used to override // any request. func (r *Runtime) EnableConnectionReuse() { if r.client == nil { r.Transport = KeepAliveTransport( transportOrDefault(r.Transport, http.DefaultTransport), ) return } r.client.Transport = KeepAliveTransport( transportOrDefault(r.client.Transport, transportOrDefault(r.Transport, http.DefaultTransport), ), ) } // Submit a request and when there is a body on success it will turn that into the result // all other things are turned into an api error for swagger which retains the status code func (r *Runtime) Submit(operation *runtime.ClientOperation) (interface{}, error) { params, readResponse, auth := operation.Params, operation.Reader, operation.AuthInfo request, err := newRequest(operation.Method, operation.PathPattern, params) if err != nil { return nil, err } var accept []string accept = append(accept, operation.ProducesMediaTypes...) if err = request.SetHeaderParam(runtime.HeaderAccept, accept...); err != nil { return nil, err } if auth == nil && r.DefaultAuthentication != nil { auth = r.DefaultAuthentication } //if auth != nil { // if err := auth.AuthenticateRequest(request, r.Formats); err != nil { // return nil, err // } //} // TODO: pick appropriate media type cmt := r.DefaultMediaType for _, mediaType := range operation.ConsumesMediaTypes { // Pick first non-empty media type if mediaType != "" { cmt = mediaType break } } if _, ok := r.Producers[cmt]; !ok && cmt != runtime.MultipartFormMime && cmt != runtime.URLencodedFormMime { return nil, fmt.Errorf("none of producers: %v registered. try %s", r.Producers, cmt) } req, err := request.buildHTTP(cmt, r.BasePath, r.Producers, r.Formats, auth) if err != nil { return nil, err } req.URL.Scheme = r.pickScheme(operation.Schemes) req.URL.Host = r.Host req.Host = r.Host r.clientOnce.Do(func() { r.client = &http.Client{ Transport: r.Transport, Jar: r.Jar, } }) if r.Debug { b, err2 := httputil.DumpRequestOut(req, true) if err2 != nil { return nil, err2 } r.logger.Debugf("%s\n", string(b)) } var hasTimeout bool pctx := operation.Context if pctx == nil { pctx = r.Context } else { hasTimeout = true } if pctx == nil { pctx = context.Background() } var ctx context.Context var cancel context.CancelFunc if hasTimeout { ctx, cancel = context.WithCancel(pctx) } else { ctx, cancel = context.WithTimeout(pctx, request.timeout) } defer cancel() client := operation.Client if client == nil { client = r.client } req = req.WithContext(ctx) res, err := client.Do(req) // make requests, by default follows 10 redirects before failing if err != nil { return nil, err } defer res.Body.Close() ct := res.Header.Get(runtime.HeaderContentType) if ct == "" { // this should really really never occur ct = r.DefaultMediaType } if r.Debug { printBody := true if ct == runtime.DefaultMime { printBody = false // Spare the terminal from a binary blob. } b, err2 := httputil.DumpResponse(res, printBody) if err2 != nil { return nil, err2 } r.logger.Debugf("%s\n", string(b)) } mt, _, err := mime.ParseMediaType(ct) if err != nil { return nil, fmt.Errorf("parse content type: %s", err) } cons, ok := r.Consumers[mt] if !ok { if cons, ok = r.Consumers["*/*"]; !ok { // scream about not knowing what to do return nil, fmt.Errorf("no consumer: %q", ct) } } return readResponse.ReadResponse(response{res}, cons) } // SetDebug changes the debug flag. // It ensures that client and middlewares have the set debug level. func (r *Runtime) SetDebug(debug bool) { r.Debug = debug middleware.Debug = debug } // SetLogger changes the logger stream. // It ensures that client and middlewares use the same logger. func (r *Runtime) SetLogger(logger logger.Logger) { r.logger = logger middleware.Logger = logger } runtime-0.21.0/client/runtime_test.go000066400000000000000000000755021413666762700176370ustar00rootroot00000000000000// 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 client import ( "bytes" "encoding/json" "encoding/xml" "errors" "io/ioutil" "net/http" "net/http/cookiejar" "net/http/httptest" "net/url" "os" "testing" "time" "crypto/tls" "crypto/x509" "encoding/pem" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" goruntime "runtime" ) // task This describes a task. Tasks require a content property to be set. type task struct { // Completed Completed bool `json:"completed" xml:"completed"` // Content Task content can contain [GFM](https://help.github.com/articles/github-flavored-markdown/). Content string `json:"content" xml:"content"` // ID This id property is autogenerated when a task is created. ID int64 `json:"id" xml:"id"` } func TestRuntime_TLSAuthConfig(t *testing.T) { var opts TLSClientOptions opts.CA = "../fixtures/certs/myCA.crt" opts.Key = "../fixtures/certs/myclient.key" opts.Certificate = "../fixtures/certs/myclient.crt" opts.ServerName = "somewhere" cfg, err := TLSClientAuth(opts) if assert.NoError(t, err) { if assert.NotNil(t, cfg) { assert.Len(t, cfg.Certificates, 1) assert.NotNil(t, cfg.RootCAs) assert.Equal(t, "somewhere", cfg.ServerName) } } } func TestRuntime_TLSAuthConfigWithRSAKey(t *testing.T) { keyPem, err := ioutil.ReadFile("../fixtures/certs/myclient.key") require.NoError(t, err) keyDer, _ := pem.Decode(keyPem) require.NotNil(t, keyDer) key, err := x509.ParsePKCS1PrivateKey(keyDer.Bytes) require.NoError(t, err) certPem, err := ioutil.ReadFile("../fixtures/certs/myclient.crt") require.NoError(t, err) certDer, _ := pem.Decode(certPem) require.NotNil(t, certDer) cert, err := x509.ParseCertificate(certDer.Bytes) require.NoError(t, err) var opts TLSClientOptions opts.LoadedKey = key opts.LoadedCertificate = cert cfg, err := TLSClientAuth(opts) if assert.NoError(t, err) { if assert.NotNil(t, cfg) { assert.Len(t, cfg.Certificates, 1) } } } func TestRuntime_TLSAuthConfigWithECKey(t *testing.T) { keyPem, err := ioutil.ReadFile("../fixtures/certs/myclient-ecc.key") require.NoError(t, err) _, remainder := pem.Decode(keyPem) keyDer, _ := pem.Decode(remainder) require.NotNil(t, keyDer) key, err := x509.ParseECPrivateKey(keyDer.Bytes) require.NoError(t, err) certPem, err := ioutil.ReadFile("../fixtures/certs/myclient-ecc.crt") require.NoError(t, err) certDer, _ := pem.Decode(certPem) require.NotNil(t, certDer) cert, err := x509.ParseCertificate(certDer.Bytes) require.NoError(t, err) var opts TLSClientOptions opts.LoadedKey = key opts.LoadedCertificate = cert cfg, err := TLSClientAuth(opts) if assert.NoError(t, err) { if assert.NotNil(t, cfg) { assert.Len(t, cfg.Certificates, 1) } } } func TestRuntime_TLSAuthConfigWithLoadedCA(t *testing.T) { certPem, err := ioutil.ReadFile("../fixtures/certs/myCA.crt") require.NoError(t, err) block, _ := pem.Decode(certPem) require.NotNil(t, block) cert, err := x509.ParseCertificate(block.Bytes) require.NoError(t, err) var opts TLSClientOptions opts.LoadedCA = cert cfg, err := TLSClientAuth(opts) if assert.NoError(t, err) { if assert.NotNil(t, cfg) { assert.NotNil(t, cfg.RootCAs) } } } func TestRuntime_TLSAuthConfigWithLoadedCAPool(t *testing.T) { certPem, err := ioutil.ReadFile("../fixtures/certs/myCA.crt") require.NoError(t, err) block, _ := pem.Decode(certPem) require.NotNil(t, block) cert, err := x509.ParseCertificate(block.Bytes) require.NoError(t, err) pool := x509.NewCertPool() pool.AddCert(cert) var opts TLSClientOptions opts.LoadedCAPool = pool cfg, err := TLSClientAuth(opts) if assert.NoError(t, err) { if assert.NotNil(t, cfg) { require.NotNil(t, cfg.RootCAs) // Using require.Len prints the (very large and incomprehensible) // Subjects list on failure, so instead use require.Equal. require.Equal(t, 1, len(cfg.RootCAs.Subjects())) } } } func TestRuntime_TLSAuthConfigWithLoadedCAPoolAndLoadedCA(t *testing.T) { certPem, err := ioutil.ReadFile("../fixtures/certs/myCA.crt") require.NoError(t, err) block, _ := pem.Decode(certPem) require.NotNil(t, block) cert, err := x509.ParseCertificate(block.Bytes) require.NoError(t, err) var pool *x509.CertPool if goruntime.GOOS == "windows" { // Windows doesn't have the system cert pool. pool = x509.NewCertPool() } else { pool, err = x509.SystemCertPool() require.NoError(t, err) } startingCertCount := len(pool.Subjects()) var opts TLSClientOptions opts.LoadedCAPool = pool opts.LoadedCA = cert cfg, err := TLSClientAuth(opts) if assert.NoError(t, err) { if assert.NotNil(t, cfg) { require.NotNil(t, cfg.RootCAs) // Using require.Len prints the (very large and incomprehensible) // Subjects list on failure, so instead use require.Equal. require.Equal(t, startingCertCount+1, len(cfg.RootCAs.Subjects())) } } } func TestRuntime_TLSAuthConfigWithVerifyPeerCertificate(t *testing.T) { var opts TLSClientOptions opts.InsecureSkipVerify = true var verify = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { return nil } opts.VerifyPeerCertificate = verify cfg, err := TLSClientAuth(opts) if assert.NoError(t, err) { if assert.NotNil(t, cfg) { assert.True(t, cfg.InsecureSkipVerify) assert.NotNil(t, cfg.VerifyPeerCertificate) } } } func TestRuntime_ManualCertificateValidation(t *testing.T) { // test manual verification of server certificates // against root certificate on client side. result := []task{ {false, "task 1 content", 1}, {false, "task 2 content", 2}, } var verifyCalled bool server := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) _ = jsongen.Encode(result) })) // root cert rootCertFile := "../fixtures/certs/myCA.crt" rootCertPem, err := ioutil.ReadFile(rootCertFile) require.NoError(t, err) rootCertRaw, _ := pem.Decode(rootCertPem) require.NotNil(t, rootCertRaw) rootCert, err := x509.ParseCertificate(rootCertRaw.Bytes) require.NoError(t, err) // create server tls config serverCACertPool := x509.NewCertPool() serverCACertPool.AddCert(rootCert) server.TLS = &tls.Config{ RootCAs: serverCACertPool, } // load server certs serverCertFile := "../fixtures/certs/mycert1.crt" serverKeyFile := "../fixtures/certs/mycert1.key" server.TLS.Certificates = make([]tls.Certificate, 1) server.TLS.Certificates[0], err = tls.LoadX509KeyPair( serverCertFile, serverKeyFile, ) require.NoError(t, err) server.StartTLS() defer server.Close() // test if server is a valid endpoint // by comparing received certs against root cert, // explicitly omitting DNSName check. client, err := TLSClient(TLSClientOptions{ InsecureSkipVerify: true, VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { verifyCalled = true caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(rootCertPem) opts := x509.VerifyOptions{ Roots: caCertPool, CurrentTime: time.Date(2017, time.July, 1, 1, 1, 1, 1, time.UTC), } cert, err := x509.ParseCertificate(rawCerts[0]) if err != nil { return err } _, err = cert.Verify(opts) return err }, }) require.NoError(t, err) hu, _ := url.Parse(server.URL) rt := NewWithClient(hu.Host, "/", []string{"https"}, client) rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) var received []task _, err = rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "GET", PathPattern: "/", Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { if err := consumer.Consume(response.Body(), &received); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), }) if assert.NoError(t, err) { assert.True(t, verifyCalled) assert.IsType(t, []task{}, received) assert.EqualValues(t, result, received) } } func TestRuntime_Concurrent(t *testing.T) { // test that it can make a simple request // and get the response for it. // defaults all the way down result := []task{ {false, "task 1 content", 1}, {false, "task 2 content", 2}, } server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) _ = jsongen.Encode(result) })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) resCC := make(chan interface{}) errCC := make(chan error) var res interface{} var err error for j := 0; j < 6; j++ { go func() { resC := make(chan interface{}) errC := make(chan error) go func() { var resp interface{} var errp error for i := 0; i < 3; i++ { resp, errp = rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "GET", PathPattern: "/", Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result []task if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), }) <-time.After(100 * time.Millisecond) } resC <- resp errC <- errp }() resCC <- <-resC errCC <- <-errC }() } c := 6 for c > 0 { res = <-resCC err = <-errCC c-- } if assert.NoError(t, err) { assert.IsType(t, []task{}, res) actual := res.([]task) assert.EqualValues(t, result, actual) } } func TestRuntime_Canary(t *testing.T) { // test that it can make a simple request // and get the response for it. // defaults all the way down result := []task{ {false, "task 1 content", 1}, {false, "task 2 content", 2}, } server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) _ = jsongen.Encode(result) })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) res, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "GET", PathPattern: "/", Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result []task if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), }) if assert.NoError(t, err) { assert.IsType(t, []task{}, res) actual := res.([]task) assert.EqualValues(t, result, actual) } } type tasks struct { Tasks []task `xml:"task"` } func TestRuntime_XMLCanary(t *testing.T) { // test that it can make a simple XML request // and get the response for it. result := tasks{ Tasks: []task{ {false, "task 1 content", 1}, {false, "task 2 content", 2}, }, } server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.Header().Add(runtime.HeaderContentType, runtime.XMLMime) rw.WriteHeader(http.StatusOK) xmlgen := xml.NewEncoder(rw) _ = xmlgen.Encode(result) })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) res, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "GET", PathPattern: "/", Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result tasks if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), }) if assert.NoError(t, err) { assert.IsType(t, tasks{}, res) actual := res.(tasks) assert.EqualValues(t, result, actual) } } func TestRuntime_TextCanary(t *testing.T) { // test that it can make a simple text request // and get the response for it. result := "1: task 1 content; 2: task 2 content" server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.Header().Add(runtime.HeaderContentType, runtime.TextMime) rw.WriteHeader(http.StatusOK) _, _ = rw.Write([]byte(result)) })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) res, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "GET", PathPattern: "/", Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result string if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), }) if assert.NoError(t, err) { assert.IsType(t, "", res) actual := res.(string) assert.EqualValues(t, result, actual) } } func TestRuntime_CSVCanary(t *testing.T) { // test that it can make a simple csv request // and get the response for it. result := `task,content,result 1,task1,ok 2,task2,fail ` server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.Header().Add(runtime.HeaderContentType, runtime.CSVMime) rw.WriteHeader(http.StatusOK) _, _ = rw.Write([]byte(result)) })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) res, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "GET", PathPattern: "/", Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result bytes.Buffer if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), }) if assert.NoError(t, err) { assert.IsType(t, bytes.Buffer{}, res) actual := res.(bytes.Buffer) assert.EqualValues(t, result, actual.String()) } } type roundTripperFunc func(*http.Request) (*http.Response, error) func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { return fn(req) } func TestRuntime_CustomTransport(t *testing.T) { rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) result := []task{ {false, "task 1 content", 1}, {false, "task 2 content", 2}, } rt := New("localhost:3245", "/", []string{"ws", "wss", "https"}) rt.Transport = roundTripperFunc(func(req *http.Request) (*http.Response, error) { if req.URL.Scheme != "https" { return nil, errors.New("this was not a https request") } assert.Equal(t, "localhost:3245", req.Host) assert.Equal(t, "localhost:3245", req.URL.Host) var resp http.Response resp.StatusCode = 200 resp.Header = make(http.Header) resp.Header.Set("content-type", "application/json") buf := bytes.NewBuffer(nil) enc := json.NewEncoder(buf) _ = enc.Encode(result) resp.Body = ioutil.NopCloser(buf) return &resp, nil }) res, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "GET", PathPattern: "/", Schemes: []string{"ws", "wss", "https"}, Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result []task if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), }) if assert.NoError(t, err) { assert.IsType(t, []task{}, res) actual := res.([]task) assert.EqualValues(t, result, actual) } } func TestRuntime_CustomCookieJar(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { authenticated := false for _, cookie := range req.Cookies() { if cookie.Name == "sessionid" && cookie.Value == "abc" { authenticated = true } } if !authenticated { username, password, ok := req.BasicAuth() if ok && username == "username" && password == "password" { authenticated = true http.SetCookie(rw, &http.Cookie{Name: "sessionid", Value: "abc"}) } } if authenticated { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) _ = jsongen.Encode([]task{}) } else { rw.WriteHeader(http.StatusUnauthorized) } })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) rt.Jar, _ = cookiejar.New(nil) submit := func(authInfo runtime.ClientAuthInfoWriter) { _, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "GET", PathPattern: "/", Params: rwrtr, AuthInfo: authInfo, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { return nil, nil } return nil, errors.New("Generic error") }), }) assert.NoError(t, err) } submit(BasicAuth("username", "password")) submit(nil) } func TestRuntime_AuthCanary(t *testing.T) { // test that it can make a simple request // and get the response for it. // defaults all the way down result := []task{ {false, "task 1 content", 1}, {false, "task 2 content", 2}, } server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { if req.Header.Get("Authorization") != "Bearer the-super-secret-token" { rw.WriteHeader(400) return } rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) _ = jsongen.Encode(result) })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) res, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result []task if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), AuthInfo: BearerToken("the-super-secret-token"), }) if assert.NoError(t, err) { assert.IsType(t, []task{}, res) actual := res.([]task) assert.EqualValues(t, result, actual) } } func TestRuntime_PickConsumer(t *testing.T) { result := []task{ {false, "task 1 content", 1}, {false, "task 2 content", 2}, } server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { if req.Header.Get("Content-Type") != "application/octet-stream" { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+";charset=utf-8") rw.WriteHeader(400) return } rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+";charset=utf-8") rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) _ = jsongen.Encode(result) })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return req.SetBodyParam(bytes.NewBufferString("hello")) }) hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) res, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "POST", PathPattern: "/", Schemes: []string{"http"}, ConsumesMediaTypes: []string{"application/octet-stream"}, Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result []task if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), AuthInfo: BearerToken("the-super-secret-token"), }) if assert.NoError(t, err) { assert.IsType(t, []task{}, res) actual := res.([]task) assert.EqualValues(t, result, actual) } } func TestRuntime_ContentTypeCanary(t *testing.T) { // test that it can make a simple request // and get the response for it. // defaults all the way down result := []task{ {false, "task 1 content", 1}, {false, "task 2 content", 2}, } server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { if req.Header.Get("Authorization") != "Bearer the-super-secret-token" { rw.WriteHeader(400) return } rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+";charset=utf-8") rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) _ = jsongen.Encode(result) })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) res, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "GET", PathPattern: "/", Schemes: []string{"http"}, Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result []task if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), AuthInfo: BearerToken("the-super-secret-token"), }) if assert.NoError(t, err) { assert.IsType(t, []task{}, res) actual := res.([]task) assert.EqualValues(t, result, actual) } } func TestRuntime_ChunkedResponse(t *testing.T) { // test that it can make a simple request // and get the response for it. // defaults all the way down result := []task{ {false, "task 1 content", 1}, {false, "task 2 content", 2}, } server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { if req.Header.Get("Authorization") != "Bearer the-super-secret-token" { rw.WriteHeader(400) return } rw.Header().Add(runtime.HeaderTransferEncoding, "chunked") rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+";charset=utf-8") rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) _ = jsongen.Encode(result) })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) //specDoc, err := spec.Load("../../fixtures/codegen/todolist.simple.yml") hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) res, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "GET", PathPattern: "/", Schemes: []string{"http"}, Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result []task if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), AuthInfo: BearerToken("the-super-secret-token"), }) if assert.NoError(t, err) { assert.IsType(t, []task{}, res) actual := res.([]task) assert.EqualValues(t, result, actual) } } func TestRuntime_DebugValue(t *testing.T) { original := os.Getenv("DEBUG") // Emtpy DEBUG means Debug is False _ = os.Setenv("DEBUG", "") runtime := New("", "/", []string{"https"}) assert.False(t, runtime.Debug) // Non-Empty Debug means Debug is True _ = os.Setenv("DEBUG", "1") runtime = New("", "/", []string{"https"}) assert.True(t, runtime.Debug) _ = os.Setenv("DEBUG", "true") runtime = New("", "/", []string{"https"}) assert.True(t, runtime.Debug) _ = os.Setenv("DEBUG", "false") runtime = New("", "/", []string{"https"}) assert.False(t, runtime.Debug) _ = os.Setenv("DEBUG", "foo") runtime = New("", "/", []string{"https"}) assert.True(t, runtime.Debug) // Make sure DEBUG is initial value once again _ = os.Setenv("DEBUG", original) } func TestRuntime_OverrideScheme(t *testing.T) { runtime := New("", "/", []string{"https"}) sch := runtime.pickScheme([]string{"http"}) assert.Equal(t, "https", sch) } func TestRuntime_OverrideClient(t *testing.T) { client := &http.Client{} runtime := NewWithClient("", "/", []string{"https"}, client) var i int runtime.clientOnce.Do(func() { i++ }) assert.Equal(t, client, runtime.client) assert.Equal(t, 0, i) } type overrideRoundTripper struct { overriden bool } func (o *overrideRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { o.overriden = true res := new(http.Response) res.StatusCode = 200 res.Body = ioutil.NopCloser(bytes.NewBufferString("OK")) return res, nil } func TestRuntime_OverrideClientOperation(t *testing.T) { client := &http.Client{} rt := NewWithClient("", "/", []string{"https"}, client) var i int rt.clientOnce.Do(func() { i++ }) assert.Equal(t, client, rt.client) assert.Equal(t, 0, i) client2 := new(http.Client) var transport = &overrideRoundTripper{} client2.Transport = transport if assert.NotEqual(t, client, client2) { _, err := rt.Submit(&runtime.ClientOperation{ Client: client2, Params: runtime.ClientRequestWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error { return nil }), Reader: runtime.ClientResponseReaderFunc(func(_ runtime.ClientResponse, _ runtime.Consumer) (interface{}, error) { return nil, nil }), }) if assert.NoError(t, err) { assert.True(t, transport.overriden) } } } func TestRuntime_PreserveTrailingSlash(t *testing.T) { var redirected bool server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+";charset=utf-8") if req.URL.Path == "/api/tasks" { redirected = true return } if req.URL.Path == "/api/tasks/" { rw.WriteHeader(http.StatusOK) } })) defer server.Close() hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return nil }) _, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "GET", PathPattern: "/api/tasks/", Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if redirected { return nil, errors.New("expected Submit to preserve trailing slashes - this caused a redirect") } if response.Code() == http.StatusOK { return nil, nil } return nil, errors.New("Generic error") }), }) assert.NoError(t, err) } func TestRuntime_FallbackConsumer(t *testing.T) { result := `W3siY29tcGxldGVkIjpmYWxzZSwiY29udGVudCI6ImRHRnpheUF4SUdOdmJuUmxiblE9IiwiaWQiOjF9XQ==` server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.Header().Add(runtime.HeaderContentType, "application/x-task") rw.WriteHeader(http.StatusOK) _, _ = rw.Write([]byte(result)) })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { return req.SetBodyParam(bytes.NewBufferString("hello")) }) hu, _ := url.Parse(server.URL) rt := New(hu.Host, "/", []string{"http"}) // without the fallback consumer _, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "POST", PathPattern: "/", Schemes: []string{"http"}, ConsumesMediaTypes: []string{"application/octet-stream"}, Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result []byte if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), }) if assert.Error(t, err) { assert.Equal(t, `no consumer: "application/x-task"`, err.Error()) } // add the fallback consumer rt.Consumers["*/*"] = rt.Consumers[runtime.DefaultMime] res, err := rt.Submit(&runtime.ClientOperation{ ID: "getTasks", Method: "POST", PathPattern: "/", Schemes: []string{"http"}, ConsumesMediaTypes: []string{"application/octet-stream"}, Params: rwrtr, Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if response.Code() == 200 { var result []byte if err := consumer.Consume(response.Body(), &result); err != nil { return nil, err } return result, nil } return nil, errors.New("Generic error") }), }) if assert.NoError(t, err) { assert.IsType(t, []byte{}, res) actual := res.([]byte) assert.EqualValues(t, result, actual) } } runtime-0.21.0/client_auth_info.go000066400000000000000000000022441413666762700171420ustar00rootroot00000000000000// 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 } runtime-0.21.0/client_operation.go000066400000000000000000000025671413666762700171760ustar00rootroot00000000000000// 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) } runtime-0.21.0/client_request.go000066400000000000000000000072601413666762700166610ustar00rootroot00000000000000// 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" "io/ioutil" "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 = ioutil.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 } runtime-0.21.0/client_request_test.go000066400000000000000000000021641413666762700177160ustar00rootroot00000000000000// 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 ( "testing" "github.com/go-openapi/strfmt" "github.com/stretchr/testify/assert" ) func TestRequestWriterFunc(t *testing.T) { hand := ClientRequestWriterFunc(func(r ClientRequest, reg strfmt.Registry) error { _ = r.SetHeaderParam("blah", "blah blah") _ = r.SetBodyParam(struct{ Name string }{"Adriana"}) return nil }) tr := new(TestClientRequest) _ = hand.WriteToRequest(tr, nil) assert.Equal(t, "blah blah", tr.Headers.Get("blah")) assert.Equal(t, "Adriana", tr.Body.(struct{ Name string }).Name) } runtime-0.21.0/client_response.go000066400000000000000000000040711413666762700170240ustar00rootroot00000000000000// 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 ( "fmt" "io" "encoding/json" ) // 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 (a *APIError) Error() string { resp, _ := json.Marshal(a.Response) return fmt.Sprintf("%s (status %d): %s", a.OperationName, a.Code, resp) } func (a *APIError) String() string { return a.Error() } runtime-0.21.0/client_response_test.go000066400000000000000000000033111413666762700200570ustar00rootroot00000000000000// 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" "io" "io/ioutil" "testing" "github.com/stretchr/testify/assert" ) type response struct { } func (r response) Code() int { return 490 } func (r response) Message() string { return "the message" } func (r response) GetHeader(_ string) string { return "the header" } func (r response) GetHeaders(_ string) []string { return []string{"the headers", "the headers2"} } func (r response) Body() io.ReadCloser { return ioutil.NopCloser(bytes.NewBufferString("the content")) } func TestResponseReaderFunc(t *testing.T) { var actual struct { Header, Message, Body string Code int } reader := ClientResponseReaderFunc(func(r ClientResponse, _ Consumer) (interface{}, error) { b, _ := ioutil.ReadAll(r.Body()) actual.Body = string(b) actual.Code = r.Code() actual.Message = r.Message() actual.Header = r.GetHeader("blah") return actual, nil }) _, _ = reader.ReadResponse(response{}, nil) assert.Equal(t, "the content", actual.Body) assert.Equal(t, "the message", actual.Message) assert.Equal(t, "the header", actual.Header) assert.Equal(t, 490, actual.Code) } runtime-0.21.0/constants.go000066400000000000000000000031201413666762700156360ustar00rootroot00000000000000// 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" 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" ) runtime-0.21.0/csv.go000066400000000000000000000035661413666762700144330ustar00rootroot00000000000000// 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 }) } runtime-0.21.0/csv_test.go000066400000000000000000000034161413666762700154640ustar00rootroot00000000000000// 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" "io" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) const consProdCSV = `name,country,age John,US,19 Mike,US,20 ` type csvEmptyReader struct{} func (r *csvEmptyReader) Read(d []byte) (int, error) { return 0, io.EOF } func TestCSVConsumer(t *testing.T) { cons := CSVConsumer() reader := bytes.NewBuffer([]byte(consProdCSV)) outBuf := new(bytes.Buffer) err := cons.Consume(reader, outBuf) assert.NoError(t, err) assert.Equal(t, consProdCSV, outBuf.String()) outBuf2 := new(bytes.Buffer) err = cons.Consume(nil, outBuf2) assert.Error(t, err) err = cons.Consume(reader, struct{}{}) assert.Error(t, err) emptyOutBuf := new(bytes.Buffer) err = cons.Consume(&csvEmptyReader{}, emptyOutBuf) assert.NoError(t, err) assert.Equal(t, "", emptyOutBuf.String()) } func TestCSVProducer(t *testing.T) { prod := CSVProducer() data := []byte(consProdCSV) rw := httptest.NewRecorder() err := prod.Produce(rw, data) assert.NoError(t, err) assert.Equal(t, consProdCSV, rw.Body.String()) rw2 := httptest.NewRecorder() err = prod.Produce(rw2, struct{}{}) assert.Error(t, err) err = prod.Produce(nil, data) assert.Error(t, err) } runtime-0.21.0/discard.go000066400000000000000000000005211413666762700152350ustar00rootroot00000000000000package 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 }) runtime-0.21.0/file.go000066400000000000000000000016441413666762700145520ustar00rootroot00000000000000// 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/multipart" // File represents an uploaded file. type File struct { Data multipart.File Header *multipart.FileHeader } // Read bytes from the file func (f *File) Read(p []byte) (n int, err error) { return f.Data.Read(p) } // Close the file func (f *File) Close() error { return f.Data.Close() } runtime-0.21.0/file_test.go000066400000000000000000000007161413666762700156100ustar00rootroot00000000000000package runtime import ( "io" "testing" "github.com/stretchr/testify/assert" ) func TestFileImplementsIOReader(t *testing.T) { var file interface{} = &File{} expected := "that File implements io.Reader" assert.Implements(t, new(io.Reader), file, expected) } func TestFileImplementsIOReadCloser(t *testing.T) { var file interface{} = &File{} expected := "that File implements io.ReadCloser" assert.Implements(t, new(io.ReadCloser), file, expected) } runtime-0.21.0/fixtures/000077500000000000000000000000001413666762700151505ustar00rootroot00000000000000runtime-0.21.0/fixtures/bugs/000077500000000000000000000000001413666762700161105ustar00rootroot00000000000000runtime-0.21.0/fixtures/bugs/172/000077500000000000000000000000001413666762700164215ustar00rootroot00000000000000runtime-0.21.0/fixtures/bugs/172/swagger.yml000066400000000000000000000040761413666762700206120ustar00rootroot00000000000000swagger: '2.0' info: version: 1.0.0 title: 'Test' schemes: - http produces: - application/vnd.cia.v1+json paths: /pets: get: description: Returns all pets from the system that the user has access to operationId: findPets parameters: - name: tags in: query description: tags to filter by required: false type: array items: type: string collectionFormat: csv - name: limit in: query description: maximum number of results to return required: false type: integer format: int32 responses: '200': description: pet response schema: type: array items: $ref: '#/definitions/pet' default: description: unexpected error schema: $ref: '#/definitions/errorModel' post: description: Creates a new pet in the store. Duplicates are allowed operationId: addPet consumes: - application/vnd.cia.v1+json parameters: - name: pet in: body description: Pet to add to the store required: true schema: $ref: '#/definitions/newPet' responses: '200': description: pet response schema: $ref: '#/definitions/pet' default: description: unexpected error schema: $ref: '#/definitions/errorModel' definitions: pet: required: - id - name properties: id: type: integer format: int64 name: type: string tag: type: string newPet: allOf: - $ref: '#/definitions/pet' - required: - name properties: id: type: integer format: int64 name: type: string errorModel: required: - code - message properties: code: type: integer format: int32 message: type: string runtime-0.21.0/fixtures/bugs/174/000077500000000000000000000000001413666762700164235ustar00rootroot00000000000000runtime-0.21.0/fixtures/bugs/174/swagger.yml000066400000000000000000000040641413666762700206110ustar00rootroot00000000000000swagger: '2.0' info: version: 1.0.0 title: 'Test' schemes: - http paths: /pets: get: description: Returns all pets from the system that the user has access to operationId: findPets parameters: - name: tags in: query description: tags to filter by required: false type: array items: type: string collectionFormat: csv - name: limit in: query description: maximum number of results to return required: false type: integer format: int32 responses: '200': description: pet response schema: type: array items: $ref: '#/definitions/pet' default: description: unexpected error schema: $ref: '#/definitions/errorModel' post: description: Creates a new pet in the store. Duplicates are allowed operationId: addPet consumes: - application/json produces: - application/json parameters: - name: pet in: body description: Pet to add to the store required: true schema: $ref: '#/definitions/newPet' responses: '200': description: pet response schema: $ref: '#/definitions/pet' default: description: unexpected error schema: $ref: '#/definitions/errorModel' definitions: pet: required: - id - name properties: id: type: integer format: int64 name: type: string tag: type: string newPet: allOf: - $ref: '#/definitions/pet' - required: - name properties: id: type: integer format: int64 name: type: string errorModel: required: - code - message properties: code: type: integer format: int32 message: type: string runtime-0.21.0/fixtures/bugs/264/000077500000000000000000000000001413666762700164235ustar00rootroot00000000000000runtime-0.21.0/fixtures/bugs/264/swagger.yml000066400000000000000000000005111413666762700206020ustar00rootroot00000000000000swagger: '2.0' info: version: 1.0.0 title: 'Test' schemes: - http produces: - application/json consumes: - application/json paths: /key/{id}: delete: parameters: - name: id in: path type: integer required: true responses: '200': description: OK runtime-0.21.0/fixtures/certs/000077500000000000000000000000001413666762700162705ustar00rootroot00000000000000runtime-0.21.0/fixtures/certs/myCA.crt000066400000000000000000000033751413666762700176430ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIE/TCCAuWgAwIBAgIJAJ0kpLFo4pEzMA0GCSqGSIb3DQEBCwUAMBUxEzARBgNV BAMMCkdvIFN3YWdnZXIwHhcNMTYxMjIxMDgzMzM4WhcNMTgxMjIxMDgzMzM4WjAV MRMwEQYDVQQDDApHbyBTd2FnZ2VyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC CgKCAgEAzQ5NC1JBNNP79HPiUBAO59LoUMGbmSU9K9v+cQMuyyOuv0nwuiXc5anU J1BINqgLR1VJjwTnQsXSlsr2SPs/144KgTsgk/QpMXdlFQwfqLJBIFlsQQBbMx6L /2Ho6KE7z/qz6cqgKvYrGDu6ELUu016MbUsPWfhPBJE7Ftoajk5AIomDPmiTi0cZ wdhC8SB0aVVQ2IWrsusfgPeOQ+ZLa/WHmpJ2Syfq41i/VKllEeCrMwtMP2By2kA/ ufBLCnhr7yZ0u22O1Bl1+0XedWli2GiXyt1h9nQ5blTTKZi5grOzAgCcshb/bw1H 1hdJKMqkzbqt2Mxc/78PJbDgicJU1ap+fhfBmUviWIMML6eum2ObuKd4ihhXKfqp T/nSUA0P9565W71SLAHFLdZX/VSMZnoehkwIicVGgEzjlYj2j9qBc0CjYzbEtQXH TRGhbjMX5LSByeE6hwLM6hIbQL4nriRobar63rbOc74Tm1ed02R6BvQjgXgOGqAN BgCKKjfUIm0Qm2qV4WkwGIAOi+hdUpbNJ0X2dU/B00qLhar+h4NT9TW4PmKf4agk NZ6O3C1saGxjtuPnIdDxWTdRhPSUyjsllmWhrmkY2bsRB8Z47zqrdfyajXlPOmBM 1f0am4Zeo3ditBTfFqtA2LLQbn1yZwYJQ8+sESu6bsm3S89DFT0CAwEAAaNQME4w HQYDVR0OBBYEFN4BShcjqDbbgaGvPiGMNrUEi/RZMB8GA1UdIwQYMBaAFN4BShcj qDbbgaGvPiGMNrUEi/RZMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB AIqZYn+PFMcOwraYtXtAzJwU4gElWDjIA+xVMMzulHde8t7EWo+I5iDdtn+sHzhG B1oYFhKv/HIX9GR3XDYWluoUVA4Ed9eBamL0Qqzq4Era6ZN1VQd0egvyOT40UQ7m 2aNw1nLgWadCmsxeVMKQRdzct9G3dOfJp7K5WybINWTibNMTuoSuU5RwtzriK000 C9pnxCD8boSNY/flOX0M5Mt3kv2JaIH2UsMKNGBf5+rXcKfhTE6JgiXorUEEztHP PFpZ6VFKDlr8QC/4aLYhOJ9LIloaxZyk/rccCuHbdPPX5XGA3Z9i/lxSoqtShYlS mt5vmdRwQob/ul6hPch3YRqD4VgeM1O80FEsWBK2WmGGH3wKNKea7u6dZyfQv3t3 fUVmByAVMllVRA1YiKmBZ/kOeAMku5hpR9kzErCXZd/xrKWVym000RsvRb6apltM sYnlCyKfIdKxUXavO0Bf4+YoaN4/p3mZchxpLBwrzhPyUpGQ9b3TuGjoEmtG57yn 6I3U40/TouJR0aF7i1bAF5QJWYOS7OycJbHAIZiQx9ENDP3ZMfYNWQO6STFJAjvC C0u23DyiJWZqE4Uw51O7jWKh7bSEKWutwa0XKWrpxhUjHFX4qGigIvXpO9LMjR60 YDhdCEmUiu/Hc0tt0QzyTA6w47TP0gXREeBLabzuEDPi -----END CERTIFICATE----- runtime-0.21.0/fixtures/certs/myCA.key000066400000000000000000000062571413666762700176450ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIJKgIBAAKCAgEAzQ5NC1JBNNP79HPiUBAO59LoUMGbmSU9K9v+cQMuyyOuv0nw uiXc5anUJ1BINqgLR1VJjwTnQsXSlsr2SPs/144KgTsgk/QpMXdlFQwfqLJBIFls QQBbMx6L/2Ho6KE7z/qz6cqgKvYrGDu6ELUu016MbUsPWfhPBJE7Ftoajk5AIomD PmiTi0cZwdhC8SB0aVVQ2IWrsusfgPeOQ+ZLa/WHmpJ2Syfq41i/VKllEeCrMwtM P2By2kA/ufBLCnhr7yZ0u22O1Bl1+0XedWli2GiXyt1h9nQ5blTTKZi5grOzAgCc shb/bw1H1hdJKMqkzbqt2Mxc/78PJbDgicJU1ap+fhfBmUviWIMML6eum2ObuKd4 ihhXKfqpT/nSUA0P9565W71SLAHFLdZX/VSMZnoehkwIicVGgEzjlYj2j9qBc0Cj YzbEtQXHTRGhbjMX5LSByeE6hwLM6hIbQL4nriRobar63rbOc74Tm1ed02R6BvQj gXgOGqANBgCKKjfUIm0Qm2qV4WkwGIAOi+hdUpbNJ0X2dU/B00qLhar+h4NT9TW4 PmKf4agkNZ6O3C1saGxjtuPnIdDxWTdRhPSUyjsllmWhrmkY2bsRB8Z47zqrdfya jXlPOmBM1f0am4Zeo3ditBTfFqtA2LLQbn1yZwYJQ8+sESu6bsm3S89DFT0CAwEA AQKCAgAjBkBOoLwWg+bTOD/9oOCK5FFeCdPD8sJiDW+Gah7B/9RHRB/kC7eRWtKr 7GCJRWa3xm+MCDBgDV4M95ndmVysOsy8ihbkbp3inxwa3jlCHUBWgC+nYqIxNxR+ iIC5y2BmA9JbKor1C5sMxpbfZ7MZ01p1CI8UtP76LrxDCPnkOKVnwMk0DbS1420Y 2RGGEh8QJsxqT1qmctastpwMKPfU9tk0o7Ok3qqWLoBvu4dR6GgVjeZ2JMk5UiQQ ZGTM4wi8jnr90JbGz5qBUsvOjjOd9y+GLQ4ghHWSzNZMkpONKZh3zRb2rErw8vnE LbIHT6Wapjovf6ia3k1+CJoxrYnDrsOHcWopm2kle7FXjgfHRXubcNU2aLdIAcRg ZGGyalex3/NXKjhGf8jhaXKkOYDL37ZFtEmaUJVjjhiIE5jGByBHU0pqKk9Tdtv0 s5r5m0T8Gk8h70+fZ/C+wkYE4h8uzqAlq/yrxBSlGMHEVG9PI9tr9bM1FLM/H92q CqoVR6YWTC7o5Kasr33RKYJg5vPHfFoIGHX9etbfHPGQsbCLaWhTLIYus+0b4ZS1 D1jHCoxHCjKzf2PFwogtRsmhyQSS3A3GyEWy7BZgFvgKFpq9hRC66k8Z7pnnkKrW i4YihK17ivI5uG67Aqlc+kdahRNVWOOaPbwjGosmlULyfCOdGQKCAQEA79dD3caa zXqFQkYDZwZcthXw9Hn+ChTCcfucJz9I0DUeL8FpUQYCkFZRUoEgF/eNrKSG+ctn VDgrj0IZAcumpfjc4ZMug8svFYkE+vsUVgSI03KPRJ5NZ+Egn+HExURzCSQY6fK8 mCp05+gXndiUhoco2H851esmMtCSd/5IyR3d3C64ZfFGSk/Nx66A4Z643ffB6tOH KYWFgVoQtSb92pgyxuBzZ1JhxuBVihRzAQtuE+uZ14xPoVv52fUlYXUhGmdqtZ3l Cio3YGZTaUqtF0BP8HshzAWQ2k2vCJUxY99dbFfsE+v8vCojgMz8KmzO7C+j3Pa1 hq77rT29WFvaHwKCAQEA2t8R3QCkcaCRDMAPomqPJjUHrX2gdPM2zFFCvSqiVkh6 8ft9NF8sO1aXq600IxTiTf/L8ZvM0HlPlYQSjFzGWsOgNww9MKF7L4NnJ7e/8mwP jqfajNcqecHIXvNi0AqXOpN/hEhm5MWKce/BPV6GpnRnb5doy8wOG0ESsmUA/5TJ y/65LVxDKT9SdymDVayRwq2vNn9qW2BBcM9yan5GstkE3zzkrzKcCgz5X09/vO3R K3fYk0FReE9CY9XAQGtz36Ra19efETzvWPi18zsP96QMUYIS2+Y45sVPhGZbY2aG HQXTg8xIJN51E+jmWpJ1vv27izFh5TXeloRD4qldIwKCAQEAqkG6+KVy4OjXzlsb MTiP+eaLfVFYaFmiSv3dNPM0wjDi8+2t0Imeqk3MPvBRExJ17ReChbLB8ERLj8/R Jrgl3e5TBoLP41kKXJQ/B9fS8NkZNFk/oOtrcZGb8kN3xr23l8abNQBOpwqEoNfe Y/wKO5GZCk8OhHAAVtQ/FZVaoAJmq1YzKpLjXf9WyihzbzaYb2Hgs81jRrN1OYTx FVfPnyyp5woQgkk2BdLchj/L//LYOqXmOOBu6tH7BKGE3rEiRbciRjkHDXc4hmM9 VSJgy3+o/8K5FDbjREUfOs2GGSrIDBBCE0ZTzFNxjo51d7x0C7Ap98Ley/RNzwZj 8mSJ6wKCAQEA0NXvcXPfdBvEyumnAU2zcL1AqiUoKO635pPSnjRD2RgnVySi/omg 5q1k4oXNLXwLwmjD67DA6FoXuY3fNNaA3LGz+VJQQEqUA23Zy2fkWicJYRB/08qp 2KsxyIdqTR8N1PJPxaRfqQFja/tb4naC++gtmahacbot64tXj6gYH8WUFnThs4pI +t5UjSarDeAu5BZdDB7fGHjrd/w4K6x5QMUZhPfRK+maQWzHtE1ikJ5J6rPbjgXQ +n6F1kRpwA3G7ikgFLrEJ+qAZeBJm99LCPsaVdtKq08sE+VITghsQpfcd2zLuQH+ BE/OXkTnJpyAhNANVm6z/cQ8sllZfLglCQKCAQEAkZTQ0xnUeJTV0CqljMNCIN4M i6Xyqf5uUDPfooY+fILbemT/acYUAkbjamgJlQMQ7yui9WV7Q/9HiCHaVh3o5zrV zaq3vocA9x9nK998/He7772/uJarINvLFj/p/vTQM4Lni+bJ93bk6XE+FQKPgY9B GfeFFaVtH7gimB4CjrxYprhAfqyxyE/m6JVMRg1olIFuav37GYP+TJ2K85klQRNa TEXbm6ZJpSHfNjKZzUczziaIbwnMN9OxJY6M3a1JuEy2h+og5oRdMOoB6RETzhle mxT5uEtA6mR6KyBZBjWhcl/V/Rw1DVMmtVbHCdc0+Xn/CMemRLCw1bxRUu/iww== -----END RSA PRIVATE KEY----- runtime-0.21.0/fixtures/certs/mycert1.crt000066400000000000000000000032171413666762700203710ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIEqzCCApMCCQChJZEdSdrQkjANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDDApH byBTd2FnZ2VyMB4XDTE2MTIyMTA4MzMzOFoXDTE3MTIyMTA4MzMzOFowGjEYMBYG A1UEAwwPZ29zd2FnZ2VyLmxvY2FsMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC CgKCAgEAs1MHhleossLBkYKYwOT+82RT651CfCOilpEUhm92KGRSMQXZEk+2TUgc dGPeQNDNmbpXGzdk1HZkqWR5XKfSjWWxfmBlbBoYnkL3neoiXBdBVsgHkEPdP5ly uJRkohy6az1vnq2vLaI+YujStutf8hSdcPu9VeALbrR027dMbY2XMC97FteeVaw1 mXmW9UHDVSV9UPBPswUOQWhjIADBk5IYaYASCY3M4X5BPCWFu1oQhgVMEhodBoBj pHhHrfoDm1TwtT+dp53TmR10zpUiN+FcaVMsjqN4DWX4ma0uhu+zJew2XjCJkNfX wVqGFpe2Hx+lupOGs/kwBvQ4PYn5ydgcm5DTggBC45JxCAVi3tQCYGsg2xkX9yPj aXYc8E4/aeQI9UZxUeR2siBn8ECX4gJTmPJbQ4Xykqn6YOHyxIVoqd+9wo9Z1weH xCWtPGESg7l7Jn/6WQ5V8z6RzrquGi67asrpYpv2lxNXMQA0f3S8sWYe4f8QVazy ALtu8+0XE17UPjlbNBqEfCIrMsYmL5VyMVbL0dlXXBxHjzfpXraNGoSD4v6LxRxP dWQgrhEZ6DmfiWfX8uhLdMwlvUxNXj33UDtM8dtN6mHERA9wF2RQQzPddZ0MYmUF DI92i9mRC7Yzx6mcv/yUnFw213Jnzg297lW0Xp0ifawyPi2V8f8CAwEAATANBgkq hkiG9w0BAQsFAAOCAgEAme1gyNQry3E5bj4XfdL4aNvZamzLaQVRlNZSHUzDhhpH 6N/DK/CAw4g4Msty7g3KBZPmldJhxH0bnSoRGMjFdKn9tVQeJOjaHQ2Z3cQWwdte iXtu2F38SVfP5HCh9ASQ9vQXahGOruUPUUNUnDLfOBea7vrT3DmVugXlMSmaYuSJ JdrbPzD48yy60AEDlCVpY2m1cEc5SmTkXbrAg2jhQd6ytaPQ28vGQnpZHSS/xWjC Hh68o5SUoGoFErZxPd0o2brHavi4YybYt7CXlWG2TJ89s3BCSPIHclNF2HjxRq/r 2Q/Ttzo3cRBxi3RBnrLdn4qNgJjZnWaLobjaWcs1fbI32allogLsiurCwZb0ToC0 fNMzyHVNWY8BqsuyWyF2H0F9rklmqGFJSmrqt8kDLx0xpkZchGPIDSRh+f+PPDmE jGPPH2qxz4un0foJx99dtw18TPaplFo2LxRK89koTiQNyzAHwSn6PHGlyXhNPsUt K5GzjAu6B4uyldcg2m+4O/dbNdeqSczYAFenfEO7PRAy3AP7Lxs2xqQaNiA10965 vYmCNIOuV24CuFEIrjOQkZeFCw+odsgFs5Nv8JfDdA+BRr+Haq8FVX8afEc0BEnr xY6f2fvgYTMvx0Z3UVT/XJ3POWHRL0HFLj5avHE0eOOkrcPbX6UsANd1v0F2BH8= -----END CERTIFICATE----- runtime-0.21.0/fixtures/certs/mycert1.key000066400000000000000000000062531413666762700203740ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIJKQIBAAKCAgEAs1MHhleossLBkYKYwOT+82RT651CfCOilpEUhm92KGRSMQXZ Ek+2TUgcdGPeQNDNmbpXGzdk1HZkqWR5XKfSjWWxfmBlbBoYnkL3neoiXBdBVsgH kEPdP5lyuJRkohy6az1vnq2vLaI+YujStutf8hSdcPu9VeALbrR027dMbY2XMC97 FteeVaw1mXmW9UHDVSV9UPBPswUOQWhjIADBk5IYaYASCY3M4X5BPCWFu1oQhgVM EhodBoBjpHhHrfoDm1TwtT+dp53TmR10zpUiN+FcaVMsjqN4DWX4ma0uhu+zJew2 XjCJkNfXwVqGFpe2Hx+lupOGs/kwBvQ4PYn5ydgcm5DTggBC45JxCAVi3tQCYGsg 2xkX9yPjaXYc8E4/aeQI9UZxUeR2siBn8ECX4gJTmPJbQ4Xykqn6YOHyxIVoqd+9 wo9Z1weHxCWtPGESg7l7Jn/6WQ5V8z6RzrquGi67asrpYpv2lxNXMQA0f3S8sWYe 4f8QVazyALtu8+0XE17UPjlbNBqEfCIrMsYmL5VyMVbL0dlXXBxHjzfpXraNGoSD 4v6LxRxPdWQgrhEZ6DmfiWfX8uhLdMwlvUxNXj33UDtM8dtN6mHERA9wF2RQQzPd dZ0MYmUFDI92i9mRC7Yzx6mcv/yUnFw213Jnzg297lW0Xp0ifawyPi2V8f8CAwEA AQKCAgBZtF8/RPqO8f4C3GGtnOAswTN52eE4WFstFsY9ueRRsF2tSE+eaWG4pyoU zyCPK+St0hlg9ATsg403b5uGTi11rjlsDqyttyA5iyZzSHyHtNpqnwWplUlIV2qc Cx+MOPLIUqNTrW7EVTUAJZfDCVulrcpUipncK4eMiZkrkDYbV4kaAaaBdrsuAEeP ztNFPPCJ14coxg4Yb58B+UYc7EPpnlu36uka/mRPKOlZPSv43MUHRf8XzxhV+EPg Moso7LiBK6x9/qTPBJSlM6cK8G99pK6lwYW4lO2pRilmNsvflGj5v4Ay/fTTECZO AwqwopPoXdx5yPLJdQ4hbGn13t+k0pB4LYXl1xqLg2Z9QN+pgC2h41OrSx8Ozw9U KTocbsMV6pafnMRoQ5Fjb+eTy4VE8rZl/OlMDX2cR2XL+a3ypIAA5E4KrYDiIBiU MSA3EA3GsOOnyrV+fII+f2tVo/qDnvxQO/ZPUr/XG2xtJ+gqThWlrBft/O4/lCju +kfNg8cMHtahGOmLz1ALsl32ANj5jTZmVOEs9xTG7+TeQ2RzWeBYTB7oNTMNIbaL pTZTzxoeRyxx8sUvtaTb23IWSpRUiS4+F7Tn97g6ks8fYQPsVkl3WzXeECaL9uNN hFkAwd0omD4TwQlmOUVm3IH7A0InTAaooC9jJfNqmhhHcLUAgQKCAQEA3N+pR1ed aCXQit6bgw0rIF6RzjeGp6lLGaPdvCUM7sdAUwSGbFOgkcaw9TELFpCpfZGKFXI9 IxPOwjFrURY4S2iuyAVv+Cw7trXW4BF1z+08M9RWYGLvyUsO7FIsGUmdYRtasb5L IfHfGoXttadKWcdFMSF+25CUcbleyCNrJzXOzeMn1/UoN6+qfsyfaAD03nw/ZmhA mK3UKjR7UOUPXt9gIXVivRaEQBakrLkJtK33fl1hztqxComE3/h6Qmj6iRmyxX3y v3mzXbyC6jobq1tLUWpxvlQgoAyk+zZ0LNEHlkVfertaz0XdN/L2ZgjoGjJxfn/y OK0a4jJyCpXXEwKCAQEAz9fJcpqaB25joZsqG+islNljyFjJRT9+E8IU97OglhJM 8T9TFs4RNsAxIqHwYm4KuoSlI4GhYeuOxGk6yUGm8cO314J7Wv4CAgZeJBcmxJUs C8FbcXKbesK3VIuvATgzI9Vq/UF+GxJCkPctZJ9Oa0t578rVS4JH5ZJQdw2A77Lq kGunMDboVY7EYDOn/fNMKGfcnH8KIQn/2b6CKLarj39b1fG7MeCuxPRejijaKtZI ra5n0ofLluGo9HBXLgqgsjhjkSWU79lRypuKS8qOcjI08Xaw3Q8+qn0uLCARd8rN 2+mQy5OAZJCWpYIKPL6E41GaxcaTdOYzlOCs9Oz65QKCAQEAgSqzXitYvC1RFcU1 AKDU1as4bXZ/YtFYP/halcq9E26mqWX+Dp+hSV7+4YT6zQlwdSSFsiEKq9bLlTk9 X0A1T7Q6cnLrliCYEzOoI4VSdnRwPocwtFFnlTo10fIEJA2u4bkTgtqcKY+/P02P RCo/Ct3EEwVZoKGejhsv2K8N3PJUrIbpKBwQlvA+LsUPe80DZpEWqpbRH/iYGM50 R0yNfpf3KdnyEk52rNwRFYloqacLE3Uc29F8s4LUl/5B0VB/I2pJ58DOEzfiszCp Br1QrRdIpqYvOnUMV0zNtrOToRnk6/ZJ7gZfBtP+mNeXTPhsc9WIFchRKN/i1uFV W+dgzQKCAQEArcXTDdio85GeB1395PuyX3kqbjWdgiJFvStF8JvkpdSDNCknxSdh SQ+DhVsz6nfqzGtezsLxNTeHVDxPBDm55OUobi0QCdHZx+ufBjm9FhtKikGNvNp/ mDH4qd1n4nMkfs9O9pOtZeDsetvOvhRbsmWWe6BwmQNCLXUZhZBqvv4uE7WOQUeH FRGaqnxF9pNWl2nPD6E/zMPZgCpCFNw1sHJhTA0h39/k/5L5A46waaRje6MX9vPG ik39vvG2Ui5ckOWIibCMR8TBF87X3+ppEp1bmo8L7Kd0U4L5+baOJEQRvc4YW7zl Wi9xZMvG12bLIGv4JWeTnediNRVsRhNk6QKCAQBXYkpxk6LTgP+b6FJ7fiImzDbH QJ+RbBYJdgYLEarKWdWhNqj3YiDOUJt+ve13reybL4cLmOYoNzeUO9IHyGeTp+WV gtPf1g2hm2TZannWsoTvnoXJ8yR52ZQQ5JusNosbmlrqWRAN8GhISdYTJDNcS2hD PnVX/kaJfRDennokD+FWuyygua66LBdZi3UNgGMay15/2CCoC3PoejfQORxDyPP9 am+e3/U6QG1/VWMHen3Mb0AZKwEBAwX1jL4EpoDZ+Y6jP0tbQ5xL7RivsUNtAVlQ m7lumflcBy1WqkmviVJ9M2iFuo0HznuH1qlgOJpUiqZZjL/gEvkdDNMcQSmH -----END RSA PRIVATE KEY----- runtime-0.21.0/fixtures/certs/mycert1.req000066400000000000000000000030721413666762700203670ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIEXzCCAkcCAQAwGjEYMBYGA1UEAwwPZ29zd2FnZ2VyLmxvY2FsMIICIjANBgkq hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAs1MHhleossLBkYKYwOT+82RT651CfCOi lpEUhm92KGRSMQXZEk+2TUgcdGPeQNDNmbpXGzdk1HZkqWR5XKfSjWWxfmBlbBoY nkL3neoiXBdBVsgHkEPdP5lyuJRkohy6az1vnq2vLaI+YujStutf8hSdcPu9VeAL brR027dMbY2XMC97FteeVaw1mXmW9UHDVSV9UPBPswUOQWhjIADBk5IYaYASCY3M 4X5BPCWFu1oQhgVMEhodBoBjpHhHrfoDm1TwtT+dp53TmR10zpUiN+FcaVMsjqN4 DWX4ma0uhu+zJew2XjCJkNfXwVqGFpe2Hx+lupOGs/kwBvQ4PYn5ydgcm5DTggBC 45JxCAVi3tQCYGsg2xkX9yPjaXYc8E4/aeQI9UZxUeR2siBn8ECX4gJTmPJbQ4Xy kqn6YOHyxIVoqd+9wo9Z1weHxCWtPGESg7l7Jn/6WQ5V8z6RzrquGi67asrpYpv2 lxNXMQA0f3S8sWYe4f8QVazyALtu8+0XE17UPjlbNBqEfCIrMsYmL5VyMVbL0dlX XBxHjzfpXraNGoSD4v6LxRxPdWQgrhEZ6DmfiWfX8uhLdMwlvUxNXj33UDtM8dtN 6mHERA9wF2RQQzPddZ0MYmUFDI92i9mRC7Yzx6mcv/yUnFw213Jnzg297lW0Xp0i fawyPi2V8f8CAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQB7U21HoBp5Qfrwd8aA KzBY2BfEp5ouyn32kpfkB3Ha6+XJ69Lt1WHMSKnmYamlwZCSOS2uQ6DzdTLDfZpC 8PH5Gs32O9zJwUeSuYcUQGfcAenauu9gwC5ZnIbhOs5YTnEFquVsBqrNUKS+hLKJ sAPtucoqlLX5qSkv/BOK2X4os90LAmx+yB/yarAzZOO0ku8qXt+MHI+rOMPLTmm9 kYhtyXejQaXLOVbvQ9b2gxHvMcyLhklc4KpJPRfPzOdNebHsf5o4Em6lxeglGw/A z05sBSAla69sEygcItZryQ4WjMRUpsLePXJrlSL5DYWGK6BX1gCkWtpXLqE1HgR3 4L/xvaJQ5ZWpLoyJoJauU37Zhd5dLNGpNiSSEA0BKOjj9Kjm8nvsJE9DgziTaG57 qFLRkMkDdBdb5wOfVYI/MY9zc+igrFPQJkQ0Xkdza8yXegBldv1JRe+49zifysea Y/B+qWx8IpeHke0iEMqR6iWrw6oGBG/obHJ/V09DwC6iU8vot+pLr/bSyoUCUP30 OEATJf50ic9oZYXgdT9oNBcAlAriuzoQuGi9nAKZJss6YkhooWoqXlXNQgAEc2gl WF4fNumXwVaPVeW2q36Xk1btHz7k+IeVUg1jaPMPUJ+1dgIOZA7FcoYotvF6StyX xoHybhvC7lbeif8EK7tJ2p4hug== -----END CERTIFICATE REQUEST----- runtime-0.21.0/fixtures/certs/myclient-ecc.crt000066400000000000000000000020361413666762700213570ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIC3TCBxgIJAKElkR1J2tCUMA0GCSqGSIb3DQEBCwUAMBUxEzARBgNVBAMMCkdv IFN3YWdnZXIwHhcNMTgwMTIyMTUyODA3WhcNMTkwMTIyMTUyODA3WjAYMRYwFAYD VQQDDA1UZXN0IEVDQyBDZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENlON 9ojzKxcMlsAIFcsc+JoMSOd1bREjEHAPaj77L0NjO6p605jT2TTLbj1lpDGD9XRc vw5iiHvhF41Sl454wjANBgkqhkiG9w0BAQsFAAOCAgEAyYwVGVG/Jzei9zxmWT+k LeK7OpGzOIyfBdEvQDFSfmyHoVJsR2LdicMJAI0gnVl5V0LdfBpu1MRz6Jdsto+u RKL1WdygESzWQ/LtqvT4rb5vLhkpCBY6Ua+SoTpY/93iOZpf1ArKOtHzL4Xop2Lb 6/0hHYU73vaBjd9YnA3T0jVFsI/otpfJhSY8FGdKSYKMf6rob9+iv2Tyjm1svkV0 IBL0D0v/LlGeM8UqXC3ZLaHsTxWi2v6PNfRyFnSNoRX4+I9ejjYvjIKQ9giVcPFQ SfhR5xm0C0xxYVqoIb6gX6owlmX2duIaV6qjU5YSzwEZqkv0Ze9i+zztBVqBRA7q fC/AMSxtqo4+Faj+hxX9T4D15hysx76uS7LxCi8GkypSZTGkjhHdMRKa2jIEvW3A 9nKW4nnC5sEBDrOTwaH4Mn6zFik3r9LTfh1gljLu9Ieqizb1gXloFhWJYvC2UwXO ins3tX2VYBF7p6yIXRmc5nZlpFErGqu2MR/lwJKD6zGIJOzCza/4DP+Mppw+DSPN XkNJG05uymsaEZceupeBH0uCgVSuVaZ3nfA73RM+0evxsscii/Kw/VFNvNDy5fLg OQWRm6RlBTK2dRqpsfo9irjdd6NVC0EfqZceYIte/eWn9aPU5uTy/TzRG24ouKtY Ixs1usnXCabNN/n0AMI+xVc= -----END CERTIFICATE----- runtime-0.21.0/fixtures/certs/myclient-ecc.csr000066400000000000000000000005471413666762700213630ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIHSMHoCAQAwGDEWMBQGA1UEAwwNVGVzdCBFQ0MgQ2VydDBZMBMGByqGSM49AgEG CCqGSM49AwEHA0IABDZTjfaI8ysXDJbACBXLHPiaDEjndW0RIxBwD2o++y9DYzuq etOY09k0y249ZaQxg/V0XL8OYoh74ReNUpeOeMKgADAKBggqhkjOPQQDAgNIADBF AiEAsqdXJEIuedKkuiavgfc0YXssFWBORAC37F5w+Z0kGEMCIDRGiCaZG4Z/Gutm id7N5T0Uxah0p5i6OzvCpYPN8f3Y -----END CERTIFICATE REQUEST----- runtime-0.21.0/fixtures/certs/myclient-ecc.key000066400000000000000000000004561413666762700213630ustar00rootroot00000000000000-----BEGIN EC PARAMETERS----- BggqhkjOPQMBBw== -----END EC PARAMETERS----- -----BEGIN EC PRIVATE KEY----- MHcCAQEEIJyZvFqw3os7TVGOSvK8XM1qysN32jG6G0AQ2mDxcRaaoAoGCCqGSM49 AwEHoUQDQgAENlON9ojzKxcMlsAIFcsc+JoMSOd1bREjEHAPaj77L0NjO6p605jT 2TTLbj1lpDGD9XRcvw5iiHvhF41Sl454wg== -----END EC PRIVATE KEY----- runtime-0.21.0/fixtures/certs/myclient.crt000066400000000000000000000033511413666762700206300ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIE7jCCAtYCCQChJZEdSdrQkzANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDDApH byBTd2FnZ2VyMB4XDTE2MTIyMTA4MzM0N1oXDTE3MTIyMTA4MzM0N1owXTELMAkG A1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExETAPBgNVBAcMCE1pbGxicmFl MRIwEAYDVQQKDAlMb2NhbCBEZXYxEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJ KoZIhvcNAQEBBQADggIPADCCAgoCggIBALGjhpJfej7btWCO4OCRJBliUAUyPMO8 B649Qjn1Yiw9E1L5viByYJSihsfUQ7u2gHip7QdigKCA/s4+w8V2L0Dv8lCowCLk exf10b7XGQaOqhk2mlr/jOapAg0pKDoUlVErBBZK2s6UbD/gLXAbxudwxCFKJ1Y7 d/Dw5aTl1vlWZpHzf2o9/ZCeHXf8Xu3aMIEPJ79wG0vzNZK7bL1r1lQVzACdHAr3 4HAQAvgWB4ZjKqN8z0vGC0N0MpaAuHD8fH8wQ5YiWBbDhDPFVzRYU8PcQjeZSMFq Oulew9KVm+vXtcMvteEoXMXwWlqAGlvnv7sskc/VbrLJJQaoswyKgy1QCKxVO47E f2iU4kP75iDYx6NpApdnpN3zxHMHyZDxuwmtoKealenxl5cZeHc6uUU1wXk+nmy7 TrgW509mcopHzHj+Q0zyGUg/dRws3qXPAGZehJPoaYF1F54eiindF1yLMMH5osvy 1bNp2EQezOlY3P4gqW9VHq3CQvytmDbXqS0vPzVAsFu8YazM3Bs0mW2bBXrEsajW DSjrvbhdZjlL9j2jqwZ2nzyan88M5t5T0vZhcu+wKisATI1yLdV3oWvLmdFz/XA9 L6UyosTiwC1MWPmkOY4mcHn/Px70f40+wO815pZ6FbjecxRSyMfAm6vDPWtLAMUr 1UoD4vasyvQNAgMBAAEwDQYJKoZIhvcNAQELBQADggIBACI85R1LfDwKpmWFdCeN M8O6TwSftxh113QzMvobYDMmBI+yVn0ZvpcfP7E+LWRjzNcDxMFbntbbFWt99Rcd rJek6CVxLJug53mUEFmvktreHsJ1T7cMbk1ZVroAurE7hZOWYM2HwXlKzVyXX0qh wR26HSuzQcGBfo5/8e6dzMuy0fUVkgDMu4oKt0+mGgS4kXsOyexfRRBkY9GPusVk gSzu/WbSGNxNvp/ewWNi8waqrN3noM83iae+BXxI0Sq4eLTQ/vnV1ReM4gRR12Vw anwZqHZ/WzBV27z9gW36t7wRxJS/uTXQ8J08KtBRBPv+19NXSqqjys5Jg0P1f+l9 k+sWwpVqIF2rAQ3FyMfboaFKPC0jRn7iJMjp9KyvMbSI+25/rP5xvMicoJwRlk9I GNGasxSfmRpVpV+WG04xMGp3cPrCXHBdAAjI3O68YIPOX3VqZ6MasN1iGuYWOmam yeKzLUApYdtkR7yJ+X1FOKVfbzX27CLYmzwrHnDLJzu8NVgqLGU+qTSK0zm3sYE3 w3ex6WX86Oz2QBJ5h/s2TLbsWis7ZkKjMyXqVWlbg4P3reyNrfpAoc0y1R9EjZlf 1c9HZBRBuRMgaPWmdSR4lxw1FhQBTstIfzC8lBYNbt8QRRtJIxVF9mxiL7H+6XH5 FZXcQCHun6klGtCkypeAaviE -----END CERTIFICATE----- runtime-0.21.0/fixtures/certs/myclient.csr000066400000000000000000000032231413666762700206250ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIEojCCAooCAQAwXTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx ETAPBgNVBAcMCE1pbGxicmFlMRIwEAYDVQQKDAlMb2NhbCBEZXYxEjAQBgNVBAMM CWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALGjhpJf ej7btWCO4OCRJBliUAUyPMO8B649Qjn1Yiw9E1L5viByYJSihsfUQ7u2gHip7Qdi gKCA/s4+w8V2L0Dv8lCowCLkexf10b7XGQaOqhk2mlr/jOapAg0pKDoUlVErBBZK 2s6UbD/gLXAbxudwxCFKJ1Y7d/Dw5aTl1vlWZpHzf2o9/ZCeHXf8Xu3aMIEPJ79w G0vzNZK7bL1r1lQVzACdHAr34HAQAvgWB4ZjKqN8z0vGC0N0MpaAuHD8fH8wQ5Yi WBbDhDPFVzRYU8PcQjeZSMFqOulew9KVm+vXtcMvteEoXMXwWlqAGlvnv7sskc/V brLJJQaoswyKgy1QCKxVO47Ef2iU4kP75iDYx6NpApdnpN3zxHMHyZDxuwmtoKea lenxl5cZeHc6uUU1wXk+nmy7TrgW509mcopHzHj+Q0zyGUg/dRws3qXPAGZehJPo aYF1F54eiindF1yLMMH5osvy1bNp2EQezOlY3P4gqW9VHq3CQvytmDbXqS0vPzVA sFu8YazM3Bs0mW2bBXrEsajWDSjrvbhdZjlL9j2jqwZ2nzyan88M5t5T0vZhcu+w KisATI1yLdV3oWvLmdFz/XA9L6UyosTiwC1MWPmkOY4mcHn/Px70f40+wO815pZ6 FbjecxRSyMfAm6vDPWtLAMUr1UoD4vasyvQNAgMBAAGgADANBgkqhkiG9w0BAQsF AAOCAgEAM9VLDurmvoYQyNEpRvFpOLPkgr8rgboo/HN+O/hN9jtmXranLxzTzd+u OJCujyzS3sbqiZwPeT3APHH4c/mLdrEKZHjfy2sEeXMsVW6dCOcIEYsADSCM6chi zU86aw4rAkd6YYB+lXFsEmBq78AIpw0vcdpoPoqGRG9ETQsjr4kD3ATGHTnaP551 61JJed7Kn5FTbieTmzmMa46dn7GjTTmPEcoAnHNCx4CbJAHwWEzvQWF4lVlyb2di jFD0NQ0WeaFHK/f6UQMqMq+7TpurN8sLWDlyPHA2X/FT+OsUMAX2mLcwZEsYhTjP dC4ZCuZ/itDgEp3hyPeKiLo+mL/bhhy50nzah/qclI9PS8ufUXEjWoObqiJ5eyIZ jTZ73qpLupS+Yrami98IYfuOotwGzKkVLwUPtCWQrKsun6YNtotuKKmqEEQX3Fm3 ZXIYv0BckkXIGd0aKPeMGgMUO26pyxPBSRWB29F07LXzS6eEmfOHvZcT+QLZmys9 FkH3yePeTilojCnxNINPyKT4Dk0NiZviCdKavUIJ5QtOyDJ1Nc9j5ss+QaAaNtZZ VTTjupNp+cfCh/kdyGpGP+GgXQQcGgw4OaIbfXqmec7RsqTOppK5gDR4Ne3e5FVm SpPDyHbv2GJolPG8/HCOsLCJED+wAEfhK/wUg8ZpC+7Ymct2TU8= -----END CERTIFICATE REQUEST----- runtime-0.21.0/fixtures/certs/myclient.key000066400000000000000000000062531413666762700206340ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIJJwIBAAKCAgEAsaOGkl96Ptu1YI7g4JEkGWJQBTI8w7wHrj1COfViLD0TUvm+ IHJglKKGx9RDu7aAeKntB2KAoID+zj7DxXYvQO/yUKjAIuR7F/XRvtcZBo6qGTaa Wv+M5qkCDSkoOhSVUSsEFkrazpRsP+AtcBvG53DEIUonVjt38PDlpOXW+VZmkfN/ aj39kJ4dd/xe7dowgQ8nv3AbS/M1krtsvWvWVBXMAJ0cCvfgcBAC+BYHhmMqo3zP S8YLQ3QyloC4cPx8fzBDliJYFsOEM8VXNFhTw9xCN5lIwWo66V7D0pWb69e1wy+1 4ShcxfBaWoAaW+e/uyyRz9VussklBqizDIqDLVAIrFU7jsR/aJTiQ/vmINjHo2kC l2ek3fPEcwfJkPG7Ca2gp5qV6fGXlxl4dzq5RTXBeT6ebLtOuBbnT2ZyikfMeP5D TPIZSD91HCzepc8AZl6Ek+hpgXUXnh6KKd0XXIswwfmiy/LVs2nYRB7M6Vjc/iCp b1UercJC/K2YNtepLS8/NUCwW7xhrMzcGzSZbZsFesSxqNYNKOu9uF1mOUv2PaOr BnafPJqfzwzm3lPS9mFy77AqKwBMjXIt1Xeha8uZ0XP9cD0vpTKixOLALUxY+aQ5 jiZwef8/HvR/jT7A7zXmlnoVuN5zFFLIx8Cbq8M9a0sAxSvVSgPi9qzK9A0CAwEA AQKCAgAb4VyHsLCRGQ64nvQwitctnL6OcjoTRnm2ISs5yYelBdj4lvX+RbVe3rtk ta4D0jsLtS/cjts9VcGoQTWc0lXMTVysyC+Pymh/dDd9SmlFHDMaTfWf/qfws+n8 gs8rfnuJB8VWcl0xOx5aUCcRh2qKfKprxyWxZRgIGucQIHrDG4pxsdP3qs8XWZmq cVO85RfjyaslYsUGAKAR7ZS9jiVPgTRJjF8QYaM6M2kj4uE/eGUCz94BOI4gAibG dGF+akJn+/0/nRhSSlF/hqOPNaXAAdvqugYvRSsF4be+X3jfZTXD8sMLGbil4Hlt 5tk8P31aNT6Vbhw3t1Y2W1fuyfaYbPZfprpR/6ZPV3Uf1oWoh1ystIxWnfU7Qdxu vrkHkrtho6Qt/7d8nNg0mQ8y5glNcVh/iNu9gkyHIpQ2dZpM9tpHArBGweHVDMvZ vrb/oJ5fRxnKkyouMtWvqO1TY4STPBwCDNSwJa0yxTn3fLvkOdHk1nGEKra7E7Nc hgsIe4q1ZoEikg7cZe8pvcsHIFfju3Kv/zgDTvHjzHPTdNear7mpk/LihlWdbTiI UKkgv17JHRsIhfE5G4pZXLRv2qjCGh+uS8yn2k5qPJqGoyIQ2A7BYbaQ/y2gVh6u hnVdKeETT2uUqIS3xHrV0U9grAeldPJu7bHRwSoJ+HUbp+D8QQKCAQEA4/5K0+Qq p4rpB+4FpPkMcM4yE1j0ASwHsKGMDPU70/t8yfzZBlpf5WNHTOWa8frnxOyazo8E sjm2Xw1RlMb21bFF0wjb3uhN2ak++0zIKMf5vWnM0bb2z7yzbcOJVxLzO9DmRUh0 OXvHvbeKbW9KXHT3YKA2zjaw0mO1zl7sd7r028wYpD6owGtfzooyXwWCnByIQ3nM JFB7wFJGIg6Kbu2eJULrN1EaT1Ye0FUVmc4x55FLmZvkYziQ88e4UsjYdZ4R5EFi 2XULVI1RA+NPqDXkXmpIx3JnRRvaPc74QatGvDFwY8YeCAjfGFN5LiwFJ6Cz3/jf WjDLOhqoSiYQ2QKCAQEAx3W7uPE7WNQRsyu2QnsEDdlikgP0FJf3fFZOYurfsxp7 iqTpNkR9pSWXftP4FBM/KRruoY5TucmPTfGPRS6sQWTfUkVOhrUpOLnuWjf2DJxH Qqb0wnT76WcAB4L5Gr//91a+w3dwAX5YhdTZLxEKgpm8ky290otCg3+AYOb/P3Ja V8RR8RQCNV1+y7peBgjj/mbYeVpxjTiZ5cq4cx2OU4rnup/k3HIg1Gw+qr0N9AUN 2WYOL+X0qaKffDa2ekv88H6mVnfRSReFIpteuV0XITwvMc0DbHdj6zEj1TSZMExu rDVe7eh2BeL1QxbuazRUgwZ+kfy2NUzPkB1SSwi8VQKCAQBs8K4qj0S+Z8avjlFO Id6K7EvLKN72zGYkRRzZeDiNMwbOsS22NmrJ/eUs3i1qYJxsYS4bcwUocCEvS/rm XyfEtf8KNppw6YmBbrh0dZzSt7MiibJfptBKNP17fkpau+hTdZ8CDfvTF806XsAb SGk8wnsNxaBKaqGU9iYCJSNSlpe3is9fc71IrEXMOAaXltdw5sVJkKI12+s121o9 nbsSBCJj5ZTlCrDKpfj1TSKUKo13+9om3PGFY5sHkTAHBoc/tDcSXRfxllbCoP/M HsqKMq4bWyfJfWXRBN0EWagQINocxHbShfEFn8+SHRizMj+ITuaEJ7P5sYT6D5DI VWYJAoIBAEqaxdFiIYGTKN+sbOqm2phXhB/7bJM7WC1glsc29N8n+6ebEUPkEF7y FZ0xqavQmyJD2ZgCBV0LgBd2T9FfqLx4/3LlS37lSfrWyMlj/xsuZRUQH6KQYR0n EoK8wXH4+MPJ5WZ1SSa13GSKfYW2SQkaecdPJ54VypYm3ZzhKf3QRuxnGQMkKcNO KjwHhF2be7PPQg75/lkFH8MstRsRpgengA90+QRfh9oMdtAkEJECRvDW1F2kFIRS uHacfFp4C67koFDdViGRs5GDLcYFhL5ApaJp/WrXqT7yTWXU26uOGyM8fzpbZbHD 91rVu+3LUAUGK9ds/7Yl+cj8vqgkJ1UCggEAc0a5kmBREz/8rAWKnlCZrhBsxUUM tiIj32h6dVdFo5SsoyVTxdB394npw1DAsC8xdowrcm/zsYstB3IDMYlrBnCdRxTU Xu6RD3Jci0Qg1cfLQg5snlRnrNz12wygXcvTvW8cHsda8vO+FL1RgFdehDtYoyZr swcLLRAOOLTRXy1Xdbv+8vE6s5ryl3uAO+2Zwbmur3tRL+rhXE+Tb308jlt8g2NK WrRbc3c092aImdGcKmkMGqo6H+xnL9Jj7sR161uO5JJQjxcYbZ5PBmm3J5Z71cSY LR5snbYdxUy7WKE3yxAoWi+FMsoGf+O77+oHAcpXRaTDv0Enr/7rEku5Yw== -----END RSA PRIVATE KEY----- runtime-0.21.0/fixtures/certs/myclient.p12000066400000000000000000000076651413666762700204560ustar00rootroot000000000000000‚±0‚w *†H†÷  ‚h‚d0‚`0‚— *†H†÷  ‚ˆ0‚„0‚} *†H†÷ 0 *†H†÷  0úJ52\g€‚PYR.ïeê¢h«– “Áuj‡{6ŸCYLåÙgÚþû€ š³uôýzÝbKèeöZŸº¨¦íuÿŒ»­¡6·X­8vÝ«cºiƒÖÖP°ƒÏÚgsÈYòÊ,¡ûËphhÑ`¹`FãgP‹ÑýÊÒìƒjT)ƒœ |ÒØÂûs>69$&x´ð{å±äA=åø5 …RÈ›¼A#ìÉ#`ÙGxL·7BŒ¬éìãS¶ öbsê%£R>J\oõÑEÏé‹‹^\«“…÷cÚ…zt¿µ¬Svƒäc¦ BÉ”øul^ò^ª äkJÉÜ¡¡ºGj¯RÈb0&šyê>ƒ5™$Ú`Çç`…?—`Â2+M*-)¥i6Yx V"ãü…Bó ©ßu¤#"\¦²?µݶç|cÛÍ5Xçä«Jjj}œ½7ˆuJ”ÝJDÞ©À.;Ñ®X¤)Cè#L‰·c-]8¢ƒ2vù ò»3j"£+›íš1¾¬¹±òd²ïÈéâ_…‡†®¢ð:–™ºz·q)c&™¦ä&Ìb˜B?˜=.bÆ ·T‡ðœy2F—ôúb8¯*$0ƒ7÷ŽZ‹Ö†‘Šôè63k™uztÖa“«L[ªé¨‰a-T7±ì+ ”ë7\á Ú%Òƒ{Ÿê´=RŸþ5îÛÉÓ˜ þåÎÖvîq‡Ù…Ë›';pŸÎ"€*lù½ø$7.ÅQÚtu— ¿Ÿ,Œ¿Šªž˜ÒƒœŠÐ3ª¯2>nïCér Cn²+œ~<*…™§£×«â¦Ýr£@Õ½¨ËdJmOºìgÅ£õ»Ê¯c ’ë —[ ,MD & ¥ˆËŠöˆŽÚĬ¥½c™&a¿/œ¦½%§ëR+Ò=,ãd¶/ðÙ²í‘ÃøÞ}{]òCïÓÿ’ž§‡ê _×ÕÂÄ m½NÀrÍN‰Ù×¶¥Y‡«N Aæž G{ÆÖÖG£n73ÏoAD)y­‰®b.K—7 ",Âå3|:ÿ‰8Ëè*HÊÁ »CjÄ0Ù5ÿ ÎP¸&#×^žŽÇ)ü¹ –]õnóãß0«„Cg¬8p…“SDrrÇ!þÊÒn×Q_:œ޽Xž¢.²_2Z°èaõgKlýè”—IÝ…ì{O·;“ö›_îòÛs6Úc¼0xÒúŸ´¨od¥ûÕDÈÛ 9acѰYóìÔMš"]å62Ot¸@©ï—œÑ@Âc ì›iZŒµ ÷¦« $'‚™2¦Ã æÄ£ÜŸ„ èÚá^ãä@IEIºÐ—ƒèÁF8!¡J$ô)Öúý‹`.›¾"¿gY ›Ï•~lÏ<Ò>ùß´ÿOKÆúZö]v¬LNºí[ Ùu»3Üî(T(cóºäS{_í^H î4Q°·¸žÞyÒ:i¥£DT0”4atRíRçš:¹ãg,·3²îV„|w2ÑžU.¹ÄUí£iKfl6)ÒÀÍüUg¡È+fm Jp°fñ^J·1;¨¡¦0‘ÏVò…¾ŽkE½ûˆýO¾s®º¾qtн>!Þ Æ<ˆìÌî¥ÛYwجá œñÄ_´ù‡éõ¡kÄÍ-³í]S?jÎT×–O)ÌŽLK>þˆ3ðßqï€âéÐꛣ x¶X+ãPkè.ž5_B¢ftŰÂ2ÍI›R6³Ý##fI·ö^¨2dÊ0u» èük ͹Οép0‚ Á *†H†÷  ‚ ²‚ ®0‚ ª0‚ ¦ *†H†÷   ‚ n0‚ j0 *†H†÷  0Vkä”Jà‘ ‚ HþÔ¢Õ`Æ++ 1M hží¬'—Ý`ºÖIRl\‹jál.(Ø™…/„«\(Ä—¹#£}= °êk"­«ÝMÙ¨8ãVø*ÄÒ2çB‚¾ßù_ª“µ{€Þ(NÃ’´™3Ð8Å‹7@vPþ4ñ÷lŸûgb`³!©ä!#.8Ò³Jêû3ãkbØ¢xÃW `3eyòfÒZnËï8‰´V|‘±Ú¾! xcâùñ(œ_uSèÄòŸF¯Ÿ×_Ü&CmLïG²Úßpøâ)õüjó1Xx6‡_ZÐÂ0€ÿ $AU1kÛâü ¢8sÊó¾»ÇÙÐ+´è8ªK³É5³—±Ð^ËI5hJý¤%n´^Sw¤|œÏ&–'‰!ü5³}_×Ý%DÚ$iìrh%&eºWðá@"$˜¬˜/¨‘él¾Ø`à?¯S=ÂîµFÃùIئÄÚí ð›Ëoû3Õ\lÅ3I»Ý¦Ÿafd•xò¨ Gܺ4 #ß<òŒð…˃*Q<üIov«j¦ïûº2Ä/ÙX@”ÉqzE ¶ R{ÎU^ð/Ùdã†ä)•\í÷ÕòHÃKk-ÄØ).g§ëZ1C‚°ârá­óh®â^Àjw½ã •bÊLAÈjJ ”¿ñ=$e²6E ¡ÊdЂ ÈßÍ{ÁêG䬺é%g=½g›«!RöêR@R[Ùi2W|°>¾ÅŠ4Xi‰ƒyüe™’ûÜÕ‹±•u½õ£Ç‹8Ú_‹îïi‚a¶xü6ãÌ|à÷|ÿ}3Q¼ ‘ו²Á/FÒܺ/ÒkMUòÀ… &Yhìtwc-En¹Ë$*äÞôb5æ¾ÉWììL·YS‘ŸÍ”ß3\½æÅH­é‰¢@+‘E¿ÃZ,¥Ö1g)ófcÆY>"´ÓÒ,À%/ŸŽõ­qoG…ñbã{ö)w`onžÂº„Ì%ãP-ZMoì%×N™øã¿­Uî—Q'MDÛp]"BÒ@ÿ+0¬Øv‚GÑ ^ä¨~å =yÕP€qÚ<Ðý8`gÆÇÁÜ”¢Ž^Çàâ=žM¯þ`'úGƒ¢;`?<ìÉt9tDN:ð_Ñœö&º[ˆùüQ:Ï\&£—ìf{»4Ö“·RaÕåˆÒU”iJ8¿à2t‰^òqÞ—=B’½Í€º6µ~Œ!%‘œtÒ_e•=ý£KÐÄEKZ¾È ²®1¾C%¾ú]Ä«àÝ»åã¬ÔìçHɶžlÁ¥¬"Xâ^Il®ihhJš@Aì¬;±訔1ÿ˜<áE%¿Ø?+/¶»#™¥y~JðE´l”2å¹ù‰ê„jD:ß¾Em¡£©r(©ø;AÿlôÿÛþ@`œwAp‹IÔMAíœÒsBa†©J¢.=—ÏöÆÃúê¤y¤Ÿü_"W ’lN­±šRDÌû4‹œ‘>ÍÅmÌ…´¸0Ë8­¬beê½ ö0WœGÝ®=)@4¾yXÓfŸ c?h¾{²öÔ5d]¡áÞx49´5 -FåLÁVm0ék@ô^ÏŸ‰[ƨTl+z©*”lǺ7µ_˜SÄ_I†¬óDÏl!dÙ/Ñ2¡³ÑŒm#5 ¦«²öãkN¼xã.|loZÅP–úí£àÏd›AØÏvEŒÞ¢nŸ*-6ÚC4UÞðñb„ÏÏù”;5¼žíS4â0hÓ’<±o@pvw_ðÃg¥g} ¯è(¾OÜnˆ³c¢UGðö{EнgœKÔ. ´w6ZdõbäIBgœìæ´ T=©@Sœiëëp¸·…Ô{$áz»¾R»3~ xG1§ ØS`äxµ‹ÏäWùa?6qà OÒá ŒÄ逸9²ÜC=,Å ÷u_aÕ-u¹Á8ŠÂÇ\P×Éofv0L[Fr’ñàiiãeÔÌ㺆¶£Úý“0 ½ƒ¯2‘ÄÞ0…aîØÅK¼™(‰°Aˆÿµé˜KfÒìÙ¹&纪ਖ<ì„k‡X…:<™Tër*2ƒ<¾3|q¾¨¿8„´@ŒŽÃ¶˜VE=KŸ} ŠM"ío¨p~º >VT¡¹ŠukÞ³ªR·6)®)¬QÐá£íü©°QPJrÏØôýELž[0{*òÑð‚1, ǯþ&­ÇÅKߤYF§{öœˆ~Ò‘›÷Οʟˆ7 ÇX @\ê´1[²nÚëŠDöœ«¡/è<Ž)!~¡šªßÙÖ¨ºn|ƒ§´c= ‘U÷¿UVà†kÍRǵ~}‰6ØzØV)è#/+¦ÆÄéRBe`-Ä=•wäÇióu?ðV†w‰S+›þúÁ8Zhûk%!˜˜W!†þÆ/ï¶ôŽäË:‡&ç<·Ný…æùëìàh~ÛH¬4ƒ¶?,Ë2 ,álÖçóÆ’±?ž4àÛ&Ø!-¤.Îbµ8±Æà`¶×7¬#ÁÏ/ÍmHÚ<¥k/…¹¨ª4ÀJèj{#^v£®RA;΋ìì³¢o“Üý)ÕßÔZVÍ4MA©(lc•öëýW'l >•2 B¾1 <1úǨ«¿TíR,É'í{º06Ãÿhvv0…Ð wo´âNÙŠ÷žêúÙ°{‹jMoC,ˆ½žay®w©SYvÚ+ð1%0# *†H†÷  1ϔŜ‹ …DÖ#ÂM¸yóà010!0 +ãHerk(ÆO½RYd‹Ó6”®Šôφš[»“runtime-0.21.0/fixtures/certs/serial000066400000000000000000000000211413666762700174630ustar00rootroot00000000000000A125911D49DAD094 runtime-0.21.0/flagext/000077500000000000000000000000001413666762700147315ustar00rootroot00000000000000runtime-0.21.0/flagext/byte_size.go000066400000000000000000000016231413666762700172570ustar00rootroot00000000000000package flagext import ( "github.com/docker/go-units" ) // ByteSize used to pass byte sizes to a go-flags CLI type ByteSize int // MarshalFlag implements go-flags Marshaller interface func (b ByteSize) MarshalFlag() (string, error) { return units.HumanSize(float64(b)), nil } // UnmarshalFlag implements go-flags Unmarshaller interface func (b *ByteSize) UnmarshalFlag(value string) error { sz, err := units.FromHumanSize(value) if err != nil { return err } *b = ByteSize(int(sz)) return nil } // String method for a bytesize (pflag value and stringer interface) func (b ByteSize) String() string { return units.HumanSize(float64(b)) } // Set the value of this bytesize (pflag value interfaces) func (b *ByteSize) Set(value string) error { return b.UnmarshalFlag(value) } // Type returns the type of the pflag value (pflag value interface) func (b *ByteSize) Type() string { return "byte-size" } runtime-0.21.0/flagext/byte_size_test.go000066400000000000000000000015171413666762700203200ustar00rootroot00000000000000package flagext import ( "testing" "github.com/stretchr/testify/assert" ) func TestMarshalBytesize(t *testing.T) { v, err := ByteSize(1024).MarshalFlag() if assert.NoError(t, err) { assert.Equal(t, "1.024kB", v) } } func TestStringBytesize(t *testing.T) { v := ByteSize(2048).String() assert.Equal(t, "2.048kB", v) } func TestUnmarshalBytesize(t *testing.T) { var b ByteSize err := b.UnmarshalFlag("notASize") assert.Error(t, err) err = b.UnmarshalFlag("1MB") if assert.NoError(t, err) { assert.Equal(t, ByteSize(1000000), b) } } func TestSetBytesize(t *testing.T) { var b ByteSize err := b.Set("notASize") assert.Error(t, err) err = b.Set("2MB") if assert.NoError(t, err) { assert.Equal(t, ByteSize(2000000), b) } } func TestTypeBytesize(t *testing.T) { var b ByteSize assert.Equal(t, "byte-size", b.Type()) } runtime-0.21.0/go.mod000066400000000000000000000007231413666762700144070ustar00rootroot00000000000000module github.com/go-openapi/runtime require ( github.com/docker/go-units v0.4.0 github.com/go-openapi/analysis v0.20.1 github.com/go-openapi/errors v0.20.1 github.com/go-openapi/loads v0.21.0 github.com/go-openapi/spec v0.20.4 github.com/go-openapi/strfmt v0.21.0 github.com/go-openapi/swag v0.19.15 github.com/go-openapi/validate v0.20.3 github.com/opentracing/opentracing-go v1.2.0 github.com/stretchr/testify v1.7.0 gopkg.in/yaml.v2 v2.4.0 ) go 1.15 runtime-0.21.0/go.sum000066400000000000000000000736261413666762700144500ustar00rootroot00000000000000github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk= github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og= github.com/go-openapi/analysis v0.20.1 h1:zdVbw8yoD4SWZeq+cWdGgquaB0W4VrsJvDJHJND/Ktc= github.com/go-openapi/analysis v0.20.1/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og= github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.20.1 h1:j23mMDtRxMwIobkpId7sWh7Ddcx4ivaoqUbfXx5P+a8= github.com/go-openapi/errors v0.20.1/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI= github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4= github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o= github.com/go-openapi/loads v0.21.0 h1:jYtUO4wwP7psAweisP/MDoOpdzsYEESdoPcsWjHDR68= github.com/go-openapi/loads v0.21.0/go.mod h1:rHYve9nZrQ4CJhyeIIFJINGCg1tQpx2yJrrNo8sf1ws= github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo= github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98= github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ= github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= github.com/go-openapi/strfmt v0.20.2/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk= github.com/go-openapi/strfmt v0.21.0 h1:hX2qEZKmYks+t0hKeb4VTJpUm2UYsdL3+DCid5swxIs= github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8= github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI= github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= github.com/go-openapi/validate v0.20.3 h1:GZPPhhKSZrE8HjB4eEkoYAZmoWA4+tCemSgINH1/vKw= github.com/go-openapi/validate v0.20.3/go.mod h1:goDdqVGiigM3jChcrYJxD2joalke3ZXeftD16byIjA4= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= go.mongodb.org/mongo-driver v1.7.3 h1:G4l/eYY9VrQAK/AUgkV0koQKzQnyddnWxrd/Etf0jIs= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758 h1:aEpZnXcAmXkd6AvLb2OPt+EN1Zu/8Ne3pCqPjja5PXY= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= runtime-0.21.0/hack/000077500000000000000000000000001413666762700142055ustar00rootroot00000000000000runtime-0.21.0/hack/gen-self-signed-certs.sh000077500000000000000000000013771413666762700206410ustar00rootroot00000000000000#!/bin/sh # generate CA openssl genrsa -out myCA.key 4096 openssl req -x509 -new -key myCA.key -out myCA.crt -days 730 -subj /CN="Go Swagger" # generate server cert and key openssl genrsa -out mycert1.key 4096 openssl req -new -out mycert1.req -key mycert1.key -subj /CN="goswagger.local" openssl x509 -req -in mycert1.req -out mycert1.crt -CAkey myCA.key -CA myCA.crt -days 365 -CAcreateserial -CAserial serial # generate client cert, key and bundle openssl genrsa -out myclient.key 4096 openssl req -new -key myclient.key -out myclient.csr openssl x509 -req -days 730 -in myclient.csr -out myclient.crt -CAkey myCA.key -CA myCA.crt -days 365 -CAcreateserial -CAserial serial openssl pkcs12 -export -clcerts -in myclient.crt -inkey myclient.key -out myclient.p12 runtime-0.21.0/headers.go000066400000000000000000000021651413666762700152450ustar00rootroot00000000000000// 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 } runtime-0.21.0/headers_test.go000066400000000000000000000040221413666762700162760ustar00rootroot00000000000000// 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" "testing" "github.com/go-openapi/errors" "github.com/stretchr/testify/assert" ) func TestParseContentType(t *testing.T) { _, _, reason1 := mime.ParseMediaType("application(") _, _, reason2 := mime.ParseMediaType("application/json;char*") data := []struct { hdr, mt, cs string err *errors.ParseError }{ {"application/json", "application/json", "", nil}, {"text/html; charset=utf-8", "text/html", "utf-8", nil}, {"text/html;charset=utf-8", "text/html", "utf-8", nil}, {"", "application/octet-stream", "", nil}, {"text/html; charset=utf-8", "text/html", "utf-8", nil}, {"application(", "", "", errors.NewParseError("Content-Type", "header", "application(", reason1)}, {"application/json;char*", "", "", errors.NewParseError("Content-Type", "header", "application/json;char*", reason2)}, } headers := http.Header(map[string][]string{}) for _, v := range data { if v.hdr != "" { headers.Set("Content-Type", v.hdr) } else { headers.Del("Content-Type") } ct, cs, err := ContentType(headers) if v.err == nil { assert.NoError(t, err, "input: %q, err: %v", v.hdr, err) } else { assert.Error(t, err, "input: %q", v.hdr) assert.IsType(t, &errors.ParseError{}, err, "input: %q", v.hdr) assert.Equal(t, v.err.Error(), err.Error(), "input: %q", v.hdr) } assert.Equal(t, v.mt, ct, "input: %q", v.hdr) assert.Equal(t, v.cs, cs, "input: %q", v.hdr) } } runtime-0.21.0/interfaces.go000066400000000000000000000077661413666762700157710ustar00rootroot00000000000000// 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 } runtime-0.21.0/internal/000077500000000000000000000000001413666762700151135ustar00rootroot00000000000000runtime-0.21.0/internal/testing/000077500000000000000000000000001413666762700165705ustar00rootroot00000000000000runtime-0.21.0/internal/testing/data.go000066400000000000000000000445241413666762700200410ustar00rootroot00000000000000// 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 testing import ( "encoding/json" ) // PetStore20YAML yaml doc for swagger 2.0 pet store const PetStore20YAML = `swagger: '2.0' info: version: '1.0.0' title: Swagger Petstore description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification termsOfService: http://helloreverb.com/terms/ contact: name: Swagger API team email: foo@example.com url: http://swagger.io license: name: MIT url: http://opensource.org/licenses/MIT host: petstore.swagger.wordnik.com basePath: /api schemes: - http consumes: - application/json produces: - application/json paths: /pets: get: description: Returns all pets from the system that the user has access to operationId: findPets produces: - application/json - application/xml - text/xml - text/html parameters: - name: tags in: query description: tags to filter by required: false type: array items: type: string collectionFormat: csv - name: limit in: query description: maximum number of results to return required: false type: integer format: int32 responses: '200': description: pet response schema: type: array items: $ref: '#/definitions/pet' default: description: unexpected error schema: $ref: '#/definitions/errorModel' post: description: Creates a new pet in the store. Duplicates are allowed operationId: addPet produces: - application/json parameters: - name: pet in: body description: Pet to add to the store required: true schema: $ref: '#/definitions/newPet' responses: '200': description: pet response schema: $ref: '#/definitions/pet' default: description: unexpected error schema: $ref: '#/definitions/errorModel' /pets/{id}: get: description: Returns a user based on a single ID, if the user does not have access to the pet operationId: findPetById produces: - application/json - application/xml - text/xml - text/html parameters: - name: id in: path description: ID of pet to fetch required: true type: integer format: int64 responses: '200': description: pet response schema: $ref: '#/definitions/pet' default: description: unexpected error schema: $ref: '#/definitions/errorModel' delete: description: deletes a single pet based on the ID supplied operationId: deletePet parameters: - name: id in: path description: ID of pet to delete required: true type: integer format: int64 responses: '204': description: pet deleted default: description: unexpected error schema: $ref: '#/definitions/errorModel' definitions: pet: required: - id - name properties: id: type: integer format: int64 name: type: string tag: type: string newPet: allOf: - $ref: '#/definitions/pet' - required: - name properties: id: type: integer format: int64 name: type: string errorModel: required: - code - message properties: code: type: integer format: int32 message: type: string ` // PetStore20 json doc for swagger 2.0 pet store const PetStore20 = `{ "swagger": "2.0", "info": { "version": "1.0.0", "title": "Swagger Petstore", "contact": { "name": "Wordnik API Team", "url": "http://developer.wordnik.com" }, "license": { "name": "Creative Commons 4.0 International", "url": "http://creativecommons.org/licenses/by/4.0/" } }, "host": "petstore.swagger.wordnik.com", "basePath": "/api", "schemes": [ "http" ], "paths": { "/pets": { "get": { "security": [ { "basic": [] } ], "tags": [ "Pet Operations" ], "operationId": "getAllPets", "parameters": [ { "name": "status", "in": "query", "description": "The status to filter by", "type": "string" }, { "name": "limit", "in": "query", "description": "The maximum number of results to return", "type": "integer", "format": "int64" } ], "summary": "Finds all pets in the system", "responses": { "200": { "description": "Pet response", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "default": { "description": "Unexpected error", "schema": { "$ref": "#/definitions/Error" } } } }, "post": { "security": [ { "basic": [] } ], "tags": [ "Pet Operations" ], "operationId": "createPet", "summary": "Creates a new pet", "consumes": ["application/x-yaml"], "produces": ["application/x-yaml"], "parameters": [ { "name": "pet", "in": "body", "description": "The Pet to create", "required": true, "schema": { "$ref": "#/definitions/newPet" } } ], "responses": { "200": { "description": "Created Pet response", "schema": { "$ref": "#/definitions/Pet" } }, "default": { "description": "Unexpected error", "schema": { "$ref": "#/definitions/Error" } } } } }, "/pets/{id}": { "delete": { "security": [ { "apiKey": [] } ], "description": "Deletes the Pet by id", "operationId": "deletePet", "parameters": [ { "name": "id", "in": "path", "description": "ID of pet to delete", "required": true, "type": "integer", "format": "int64" } ], "responses": { "204": { "description": "pet deleted" }, "default": { "description": "unexpected error", "schema": { "$ref": "#/definitions/Error" } } } }, "get": { "tags": [ "Pet Operations" ], "operationId": "getPetById", "summary": "Finds the pet by id", "responses": { "200": { "description": "Pet response", "schema": { "$ref": "#/definitions/Pet" } }, "default": { "description": "Unexpected error", "schema": { "$ref": "#/definitions/Error" } } } }, "parameters": [ { "name": "id", "in": "path", "description": "ID of pet", "required": true, "type": "integer", "format": "int64" } ] } }, "definitions": { "Category": { "properties": { "id": { "format": "int64", "type": "integer" }, "name": { "type": "string" } } }, "Pet": { "properties": { "category": { "$ref": "#/definitions/Category" }, "id": { "description": "unique identifier for the pet", "format": "int64", "maximum": 100.0, "minimum": 0.0, "type": "integer" }, "name": { "type": "string" }, "photoUrls": { "items": { "type": "string" }, "type": "array" }, "status": { "description": "pet status in the store", "enum": [ "available", "pending", "sold" ], "type": "string" }, "tags": { "items": { "$ref": "#/definitions/Tag" }, "type": "array" } }, "required": [ "id", "name" ] }, "newPet": { "anyOf": [ { "$ref": "#/definitions/Pet" }, { "required": [ "name" ] } ] }, "Tag": { "properties": { "id": { "format": "int64", "type": "integer" }, "name": { "type": "string" } } }, "Error": { "required": [ "code", "message" ], "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" } } } }, "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml", "text/plain", "text/html" ], "securityDefinitions": { "basic": { "type": "basic" }, "apiKey": { "type": "apiKey", "in": "header", "name": "X-API-KEY" } } } ` // RootPetStore20 json doc for swagger 2.0 pet store at / const RootPetStore20 = `{ "swagger": "2.0", "info": { "version": "1.0.0", "title": "Swagger Petstore", "contact": { "name": "Wordnik API Team", "url": "http://developer.wordnik.com" }, "license": { "name": "Creative Commons 4.0 International", "url": "http://creativecommons.org/licenses/by/4.0/" } }, "host": "petstore.swagger.wordnik.com", "basePath": "/", "schemes": [ "http" ], "paths": { "/pets": { "get": { "security": [ { "basic": [] } ], "tags": [ "Pet Operations" ], "operationId": "getAllPets", "parameters": [ { "name": "status", "in": "query", "description": "The status to filter by", "type": "string" } ], "summary": "Finds all pets in the system", "responses": { "200": { "description": "Pet response", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "default": { "description": "Unexpected error", "schema": { "$ref": "#/definitions/Error" } } } }, "post": { "security": [ { "basic": [] } ], "tags": [ "Pet Operations" ], "operationId": "createPet", "summary": "Creates a new pet", "consumes": ["application/x-yaml"], "produces": ["application/x-yaml"], "parameters": [ { "name": "pet", "in": "body", "description": "The Pet to create", "required": true, "schema": { "$ref": "#/definitions/newPet" } } ], "responses": { "200": { "description": "Created Pet response", "schema": { "$ref": "#/definitions/Pet" } }, "default": { "description": "Unexpected error", "schema": { "$ref": "#/definitions/Error" } } } } }, "/pets/{id}": { "delete": { "security": [ { "apiKey": [] } ], "description": "Deletes the Pet by id", "operationId": "deletePet", "parameters": [ { "name": "id", "in": "path", "description": "ID of pet to delete", "required": true, "type": "integer", "format": "int64" } ], "responses": { "204": { "description": "pet deleted" }, "default": { "description": "unexpected error", "schema": { "$ref": "#/definitions/Error" } } } }, "get": { "tags": [ "Pet Operations" ], "operationId": "getPetById", "summary": "Finds the pet by id", "responses": { "200": { "description": "Pet response", "schema": { "$ref": "#/definitions/Pet" } }, "default": { "description": "Unexpected error", "schema": { "$ref": "#/definitions/Error" } } } }, "parameters": [ { "name": "id", "in": "path", "description": "ID of pet", "required": true, "type": "integer", "format": "int64" } ] } }, "definitions": { "Category": { "properties": { "id": { "format": "int64", "type": "integer" }, "name": { "type": "string" } } }, "Pet": { "properties": { "category": { "$ref": "#/definitions/Category" }, "id": { "description": "unique identifier for the pet", "format": "int64", "maximum": 100.0, "minimum": 0.0, "type": "integer" }, "name": { "type": "string" }, "photoUrls": { "items": { "type": "string" }, "type": "array" }, "status": { "description": "pet status in the store", "enum": [ "available", "pending", "sold" ], "type": "string" }, "tags": { "items": { "$ref": "#/definitions/Tag" }, "type": "array" } }, "required": [ "id", "name" ] }, "newPet": { "anyOf": [ { "$ref": "#/definitions/Pet" }, { "required": [ "name" ] } ] }, "Tag": { "properties": { "id": { "format": "int64", "type": "integer" }, "name": { "type": "string" } } }, "Error": { "required": [ "code", "message" ], "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" } } } }, "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml", "text/plain", "text/html" ], "securityDefinitions": { "basic": { "type": "basic" }, "apiKey": { "type": "apiKey", "in": "header", "name": "X-API-KEY" } } } ` // PetStoreJSONMessage json raw message for Petstore20 var PetStoreJSONMessage = json.RawMessage([]byte(PetStore20)) // RootPetStoreJSONMessage json raw message for Petstore20 var RootPetStoreJSONMessage = json.RawMessage([]byte(RootPetStore20)) // InvalidJSON invalid swagger 2.0 spec in JSON const InvalidJSON = `{ "apiVersion": "1.0.0", "apis": [ { "description": "Operations about pets", "path": "/pet" }, { "description": "Operations about user", "path": "/user" }, { "description": "Operations about store", "path": "/store" } ], "authorizations": { "oauth2": { "grantTypes": { "authorization_code": { "tokenEndpoint": { "tokenName": "auth_code", "url": "http://petstore.swagger.wordnik.com/oauth/token" }, "tokenRequestEndpoint": { "clientIdName": "client_id", "clientSecretName": "client_secret", "url": "http://petstore.swagger.wordnik.com/oauth/requestToken" } }, "implicit": { "loginEndpoint": { "url": "http://petstore.swagger.wordnik.com/oauth/dialog" }, "tokenName": "access_token" } }, "scopes": [ { "description": "Modify pets in your account", "scope": "write:pets" }, { "description": "Read your pets", "scope": "read:pets" }, { "description": "Anything (testing)", "scope": "test:anything" } ], "type": "oauth2" } }, "info": { "contact": "apiteam@wordnik.com", "description": "This is a sample server Petstore server. You can find out more about Swagger \n at http://swagger.wordnik.com or on irc.freenode.net, #swagger. For this sample,\n you can use the api key \"special-key\" to test the authorization filters", "license": "Apache 2.0", "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html", "termsOfServiceUrl": "http://helloreverb.com/terms/", "title": "Swagger Sample App" }, "swaggerVersion": "1.2" } ` // InvalidJSONMessage json raw message for invalid json var InvalidJSONMessage = json.RawMessage([]byte(InvalidJSON)) runtime-0.21.0/internal/testing/petstore/000077500000000000000000000000001413666762700204355ustar00rootroot00000000000000runtime-0.21.0/internal/testing/petstore/api.go000066400000000000000000000127661413666762700215510ustar00rootroot00000000000000// 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 petstore import ( goerrors "errors" "io" "net/http" "strings" gotest "testing" "github.com/go-openapi/errors" "github.com/go-openapi/loads" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" testingutil "github.com/go-openapi/runtime/internal/testing" "github.com/go-openapi/runtime/middleware/untyped" "github.com/go-openapi/runtime/security" "github.com/go-openapi/runtime/yamlpc" ) // NewAPI registers a stub api for the pet store func NewAPI(t gotest.TB) (*loads.Document, *untyped.API) { spec, err := loads.Analyzed(testingutil.PetStoreJSONMessage, "") assert.NoError(t, err) api := untyped.NewAPI(spec) api.RegisterConsumer("application/json", runtime.JSONConsumer()) api.RegisterProducer("application/json", runtime.JSONProducer()) api.RegisterConsumer("application/xml", new(stubConsumer)) api.RegisterProducer("application/xml", new(stubProducer)) api.RegisterProducer("text/plain", new(stubProducer)) api.RegisterProducer("text/html", new(stubProducer)) api.RegisterConsumer("application/x-yaml", yamlpc.YAMLConsumer()) api.RegisterProducer("application/x-yaml", yamlpc.YAMLProducer()) api.RegisterAuth("basic", security.BasicAuth(func(username, password string) (interface{}, error) { if username == "admin" && password == "admin" { return "admin", nil } else if username == "topuser" && password == "topuser" { return "topuser", nil } else if username == "anyother" && password == "anyother" { return "anyother", nil } return nil, errors.Unauthenticated("basic") })) api.RegisterAuth("apiKey", security.APIKeyAuth("X-API-KEY", "header", func(token string) (interface{}, error) { if token == "token123" { return "admin", nil } return nil, errors.Unauthenticated("token") })) api.RegisterAuthorizer(runtime.AuthorizerFunc(func(r *http.Request, user interface{}) error { if r.Method == http.MethodPost && strings.HasPrefix(r.URL.Path, "/api/pets") && user.(string) != "admin" { if user.(string) == "topuser" { return errors.CompositeValidationError(errors.New(errors.InvalidTypeCode, "unauthorized")) } else { return goerrors.New("unauthorized") } } return nil })) api.RegisterOperation("get", "/pets", new(stubOperationHandler)) api.RegisterOperation("post", "/pets", new(stubOperationHandler)) api.RegisterOperation("delete", "/pets/{id}", new(stubOperationHandler)) api.RegisterOperation("get", "/pets/{id}", new(stubOperationHandler)) api.Models["pet"] = func() interface{} { return new(Pet) } api.Models["newPet"] = func() interface{} { return new(Pet) } api.Models["tag"] = func() interface{} { return new(Tag) } return spec, api } // NewRootAPI registers a stub api for the pet store func NewRootAPI(t gotest.TB) (*loads.Document, *untyped.API) { spec, err := loads.Analyzed(testingutil.RootPetStoreJSONMessage, "") assert.NoError(t, err) api := untyped.NewAPI(spec) api.RegisterConsumer("application/json", runtime.JSONConsumer()) api.RegisterProducer("application/json", runtime.JSONProducer()) api.RegisterConsumer("application/xml", new(stubConsumer)) api.RegisterProducer("application/xml", new(stubProducer)) api.RegisterProducer("text/plain", new(stubProducer)) api.RegisterProducer("text/html", new(stubProducer)) api.RegisterConsumer("application/x-yaml", yamlpc.YAMLConsumer()) api.RegisterProducer("application/x-yaml", yamlpc.YAMLProducer()) api.RegisterAuth("basic", security.BasicAuth(func(username, password string) (interface{}, error) { if username == "admin" && password == "admin" { return "admin", nil } return nil, errors.Unauthenticated("basic") })) api.RegisterAuth("apiKey", security.APIKeyAuth("X-API-KEY", "header", func(token string) (interface{}, error) { if token == "token123" { return "admin", nil } return nil, errors.Unauthenticated("token") })) api.RegisterAuthorizer(security.Authorized()) api.RegisterOperation("get", "/pets", new(stubOperationHandler)) api.RegisterOperation("post", "/pets", new(stubOperationHandler)) api.RegisterOperation("delete", "/pets/{id}", new(stubOperationHandler)) api.RegisterOperation("get", "/pets/{id}", new(stubOperationHandler)) api.Models["pet"] = func() interface{} { return new(Pet) } api.Models["newPet"] = func() interface{} { return new(Pet) } api.Models["tag"] = func() interface{} { return new(Tag) } return spec, api } // Tag the tag model type Tag struct { ID int64 Name string } // Pet the pet model type Pet struct { ID int64 Name string PhotoURLs []string Status string Tags []Tag } type stubConsumer struct { } func (s *stubConsumer) Consume(_ io.Reader, _ interface{}) error { return nil } type stubProducer struct { } func (s *stubProducer) Produce(_ io.Writer, _ interface{}) error { return nil } type stubOperationHandler struct { } func (s *stubOperationHandler) ParameterModel() interface{} { return nil } func (s *stubOperationHandler) Handle(params interface{}) (interface{}, error) { return nil, nil } runtime-0.21.0/internal/testing/simplepetstore/000077500000000000000000000000001413666762700216475ustar00rootroot00000000000000runtime-0.21.0/internal/testing/simplepetstore/api.go000066400000000000000000000203161413666762700227510ustar00rootroot00000000000000// 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 simplepetstore import ( "encoding/json" "net/http" "sync" "sync/atomic" "github.com/go-openapi/errors" "github.com/go-openapi/loads" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/runtime/middleware/untyped" ) // NewPetstore creates a new petstore api handler func NewPetstore() (http.Handler, error) { spec, err := loads.Analyzed(json.RawMessage([]byte(swaggerJSON)), "") if err != nil { return nil, err } api := untyped.NewAPI(spec) api.RegisterOperation("get", "/pets", getAllPets) api.RegisterOperation("post", "/pets", createPet) api.RegisterOperation("delete", "/pets/{id}", deletePet) api.RegisterOperation("get", "/pets/{id}", getPetByID) return middleware.Serve(spec, api), nil } var getAllPets = runtime.OperationHandlerFunc(func(data interface{}) (interface{}, error) { return pets, nil }) var createPet = runtime.OperationHandlerFunc(func(data interface{}) (interface{}, error) { body := data.(map[string]interface{})["pet"].(map[string]interface{}) return addPet(Pet{ Name: body["name"].(string), Status: body["status"].(string), }), nil }) var deletePet = runtime.OperationHandlerFunc(func(data interface{}) (interface{}, error) { id := data.(map[string]interface{})["id"].(int64) removePet(id) return nil, nil }) var getPetByID = runtime.OperationHandlerFunc(func(data interface{}) (interface{}, error) { id := data.(map[string]interface{})["id"].(int64) return petByID(id) }) // Tag the tag model type Tag struct { ID int64 Name string } // Pet the pet model type Pet struct { ID int64 `json:"id"` Name string `json:"name"` PhotoURLs []string `json:"photoUrls,omitempty"` Status string `json:"status,omitempty"` Tags []Tag `json:"tags,omitempty"` } var pets = []Pet{ {1, "Dog", []string{}, "available", nil}, {2, "Cat", []string{}, "pending", nil}, } var petsLock = &sync.Mutex{} var lastPetID int64 = 2 func newPetID() int64 { return atomic.AddInt64(&lastPetID, 1) } func addPet(pet Pet) Pet { petsLock.Lock() defer petsLock.Unlock() pet.ID = newPetID() pets = append(pets, pet) return pet } func removePet(id int64) { petsLock.Lock() defer petsLock.Unlock() var newPets []Pet for _, pet := range pets { if pet.ID != id { newPets = append(newPets, pet) } } pets = newPets } func petByID(id int64) (*Pet, error) { for _, pet := range pets { if pet.ID == id { return &pet, nil } } return nil, errors.NotFound("not found: pet %d", id) } var swaggerJSON = `{ "swagger": "2.0", "info": { "version": "1.0.0", "title": "Swagger Petstore", "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", "termsOfService": "http://helloreverb.com/terms/", "contact": { "name": "Wordnik API Team" }, "license": { "name": "MIT" } }, "host": "localhost:8344", "basePath": "/api", "schemes": [ "http" ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": { "/pets": { "get": { "description": "Returns all pets from the system that the user has access to", "operationId": "findPets", "produces": [ "application/json", "application/xml", "text/xml", "text/html" ], "parameters": [ { "name": "tags", "in": "query", "description": "tags to filter by", "required": false, "type": "array", "items": { "type": "string" }, "collectionFormat": "csv" }, { "name": "limit", "in": "query", "description": "maximum number of results to return", "required": false, "type": "integer", "format": "int32" } ], "responses": { "200": { "description": "pet response", "schema": { "type": "array", "items": { "$ref": "#/definitions/pet" } } }, "default": { "description": "unexpected error", "schema": { "$ref": "#/definitions/errorModel" } } } }, "post": { "description": "Creates a new pet in the store. Duplicates are allowed", "operationId": "addPet", "produces": [ "application/json" ], "parameters": [ { "name": "pet", "in": "body", "description": "Pet to add to the store", "required": true, "schema": { "$ref": "#/definitions/petInput" } } ], "responses": { "200": { "description": "pet response", "schema": { "$ref": "#/definitions/pet" } }, "default": { "description": "unexpected error", "schema": { "$ref": "#/definitions/errorModel" } } } } }, "/pets/{id}": { "get": { "description": "Returns a user based on a single ID, if the user does not have access to the pet", "operationId": "findPetById", "produces": [ "application/json", "application/xml", "text/xml", "text/html" ], "parameters": [ { "name": "id", "in": "path", "description": "ID of pet to fetch", "required": true, "type": "integer", "format": "int64" } ], "responses": { "200": { "description": "pet response", "schema": { "$ref": "#/definitions/pet" } }, "default": { "description": "unexpected error", "schema": { "$ref": "#/definitions/errorModel" } } } }, "delete": { "description": "deletes a single pet based on the ID supplied", "operationId": "deletePet", "parameters": [ { "name": "id", "in": "path", "description": "ID of pet to delete", "required": true, "type": "integer", "format": "int64" } ], "responses": { "204": { "description": "pet deleted" }, "default": { "description": "unexpected error", "schema": { "$ref": "#/definitions/errorModel" } } } } } }, "definitions": { "pet": { "required": [ "name", "status" ], "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" }, "status": { "type": "string" }, "tags": { "type": "array", "items": { "type": "string" } } } }, "petInput": { "allOf": [ { "$ref": "#/definitions/pet" }, { "properties": { "id": { "type": "integer", "format": "int64" } } } ] }, "errorModel": { "required": [ "code", "message" ], "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" } } } } }` runtime-0.21.0/internal/testing/simplepetstore/api_test.go000066400000000000000000000052561413666762700240160ustar00rootroot00000000000000// 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 simplepetstore import ( "bytes" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" ) func TestSimplePetstoreSpec(t *testing.T) { handler, _ := NewPetstore() // Serves swagger spec document r, _ := runtime.JSONRequest("GET", "/swagger.json", nil) rw := httptest.NewRecorder() handler.ServeHTTP(rw, r) assert.Equal(t, 200, rw.Code) assert.Equal(t, swaggerJSON, rw.Body.String()) } func TestSimplePetstoreAllPets(t *testing.T) { handler, _ := NewPetstore() // Serves swagger spec document r, _ := runtime.JSONRequest("GET", "/api/pets", nil) rw := httptest.NewRecorder() handler.ServeHTTP(rw, r) assert.Equal(t, 200, rw.Code) assert.Equal(t, "[{\"id\":1,\"name\":\"Dog\",\"status\":\"available\"},{\"id\":2,\"name\":\"Cat\",\"status\":\"pending\"}]\n", rw.Body.String()) } func TestSimplePetstorePetByID(t *testing.T) { handler, _ := NewPetstore() // Serves swagger spec document r, _ := runtime.JSONRequest("GET", "/api/pets/1", nil) rw := httptest.NewRecorder() handler.ServeHTTP(rw, r) assert.Equal(t, 200, rw.Code) assert.Equal(t, "{\"id\":1,\"name\":\"Dog\",\"status\":\"available\"}\n", rw.Body.String()) } func TestSimplePetstoreAddPet(t *testing.T) { handler, _ := NewPetstore() // Serves swagger spec document r, _ := runtime.JSONRequest("POST", "/api/pets", bytes.NewBuffer([]byte(`{"name": "Fish","status": "available"}`))) rw := httptest.NewRecorder() handler.ServeHTTP(rw, r) assert.Equal(t, 200, rw.Code) assert.Equal(t, "{\"id\":3,\"name\":\"Fish\",\"status\":\"available\"}\n", rw.Body.String()) } func TestSimplePetstoreDeletePet(t *testing.T) { handler, _ := NewPetstore() // Serves swagger spec document r, _ := runtime.JSONRequest("DELETE", "/api/pets/1", nil) rw := httptest.NewRecorder() handler.ServeHTTP(rw, r) assert.Equal(t, 204, rw.Code) assert.Equal(t, "", rw.Body.String()) r, _ = runtime.JSONRequest("GET", "/api/pets/1", nil) rw = httptest.NewRecorder() handler.ServeHTTP(rw, r) assert.Equal(t, 404, rw.Code) assert.Equal(t, "{\"code\":404,\"message\":\"not found: pet 1\"}", rw.Body.String()) } runtime-0.21.0/json.go000066400000000000000000000021701413666762700145770ustar00rootroot00000000000000// 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) }) } runtime-0.21.0/json_test.go000066400000000000000000000027421413666762700156430ustar00rootroot00000000000000// 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" "io" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) var consProdJSON = `{"name":"Somebody","id":1}` type eofRdr struct { } func (r *eofRdr) Read(d []byte) (int, error) { return 0, io.EOF } func TestJSONConsumer(t *testing.T) { cons := JSONConsumer() var data struct { Name string ID int } err := cons.Consume(bytes.NewBuffer([]byte(consProdJSON)), &data) if assert.NoError(t, err) { assert.Equal(t, "Somebody", data.Name) assert.Equal(t, 1, data.ID) err = cons.Consume(new(eofRdr), &data) assert.Error(t, err) } } func TestJSONProducer(t *testing.T) { prod := JSONProducer() data := struct { Name string `json:"name"` ID int `json:"id"` }{Name: "Somebody", ID: 1} rw := httptest.NewRecorder() err := prod.Produce(rw, data) assert.NoError(t, err) assert.Equal(t, consProdJSON+"\n", rw.Body.String()) } runtime-0.21.0/logger/000077500000000000000000000000001413666762700145565ustar00rootroot00000000000000runtime-0.21.0/logger/logger.go000066400000000000000000000005521413666762700163660ustar00rootroot00000000000000package 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 } runtime-0.21.0/logger/standard.go000066400000000000000000000006741413666762700167140ustar00rootroot00000000000000package 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...) } runtime-0.21.0/middleware/000077500000000000000000000000001413666762700154145ustar00rootroot00000000000000runtime-0.21.0/middleware/body_test.go000066400000000000000000000046601413666762700177450ustar00rootroot00000000000000package middleware import ( "io" "net/http" "path" "testing" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/internal/testing/petstore" ) type eofReader struct { } func (r *eofReader) Read(b []byte) (int, error) { return 0, io.EOF } func (r *eofReader) Close() error { return nil } type rbn func(*http.Request, *MatchedRoute) error func (b rbn) BindRequest(r *http.Request, rr *MatchedRoute) error { return b(r, rr) } func TestBindRequest_BodyValidation(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) api.DefaultConsumes = runtime.JSONMime ctx.router = DefaultRouter(spec, ctx.api) req, err := http.NewRequest("GET", path.Join(spec.BasePath(), "/pets"), new(eofReader)) if assert.NoError(t, err) { req.Header.Set("Content-Type", runtime.JSONMime) ri, rCtx, ok := ctx.RouteInfo(req) if assert.True(t, ok) { req = rCtx err := ctx.BindValidRequest(req, ri, rbn(func(r *http.Request, rr *MatchedRoute) error { defer r.Body.Close() var data interface{} err := runtime.JSONConsumer().Consume(r.Body, &data) _ = data return err })) assert.Error(t, err) assert.Equal(t, io.EOF, err) } } } func TestBindRequest_DeleteNoBody(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) api.DefaultConsumes = runtime.JSONMime ctx.router = DefaultRouter(spec, ctx.api) req, err := http.NewRequest("DELETE", path.Join(spec.BasePath(), "/pets/123"), new(eofReader)) if assert.NoError(t, err) { req.Header.Set("Accept", "*/*") ri, rCtx, ok := ctx.RouteInfo(req) if assert.True(t, ok) { req = rCtx bverr := ctx.BindValidRequest(req, ri, rbn(func(r *http.Request, rr *MatchedRoute) error { return nil })) assert.NoError(t, bverr) //assert.Equal(t, io.EOF, bverr) } } req, err = http.NewRequest("DELETE", path.Join(spec.BasePath(), "/pets/123"), new(eofReader)) if assert.NoError(t, err) { req.Header.Set("Accept", "*/*") req.Header.Set("Content-Type", runtime.JSONMime) req.ContentLength = 1 ri, rCtx, ok := ctx.RouteInfo(req) if assert.True(t, ok) { req = rCtx err := ctx.BindValidRequest(req, ri, rbn(func(r *http.Request, rr *MatchedRoute) error { defer r.Body.Close() var data interface{} err := runtime.JSONConsumer().Consume(r.Body, &data) _ = data return err })) assert.Error(t, err) assert.Equal(t, io.EOF, err) } } } runtime-0.21.0/middleware/context.go000066400000000000000000000453701413666762700174400ustar00rootroot00000000000000// 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()) } 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 prod, ok := producers[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))) } runtime-0.21.0/middleware/context_test.go000066400000000000000000000410141413666762700204660ustar00rootroot00000000000000// 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 ( "errors" "net/http" "net/http/httptest" "strings" "testing" apierrors "github.com/go-openapi/errors" "github.com/go-openapi/loads" "github.com/go-openapi/loads/fmts" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/internal/testing/petstore" "github.com/go-openapi/runtime/middleware/untyped" ) type stubBindRequester struct { } func (s *stubBindRequester) BindRequest(*http.Request, *MatchedRoute) error { return nil } type stubOperationHandler struct { } func (s *stubOperationHandler) ParameterModel() interface{} { return nil } func (s *stubOperationHandler) Handle(params interface{}) (interface{}, error) { return nil, nil } func init() { loads.AddLoader(fmts.YAMLMatcher, fmts.YAMLDoc) } func assertAPIError(t *testing.T, wantCode int, err error) { t.Helper() assert.NotNil(t, err) ce, ok := err.(*apierrors.CompositeError) assert.True(t, ok) assert.NotEmpty(t, ce.Errors) ae, ok := ce.Errors[0].(apierrors.Error) assert.True(t, ok) assert.Equal(t, wantCode, int(ae.Code())) } func TestContentType_Issue264(t *testing.T) { swspec, err := loads.Spec("../fixtures/bugs/264/swagger.yml") if assert.NoError(t, err) { api := untyped.NewAPI(swspec) api.RegisterConsumer("application/json", runtime.JSONConsumer()) api.RegisterProducer("application/json", runtime.JSONProducer()) api.RegisterOperation("delete", "/key/{id}", new(stubOperationHandler)) handler := Serve(swspec, api) request, _ := http.NewRequest("DELETE", "/key/1", nil) recorder := httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) } } func TestContentType_Issue172(t *testing.T) { swspec, err := loads.Spec("../fixtures/bugs/172/swagger.yml") if assert.NoError(t, err) { api := untyped.NewAPI(swspec) api.RegisterConsumer("application/vnd.cia.v1+json", runtime.JSONConsumer()) api.RegisterProducer("application/vnd.cia.v1+json", runtime.JSONProducer()) api.RegisterOperation("get", "/pets", new(stubOperationHandler)) handler := Serve(swspec, api) request, _ := http.NewRequest("GET", "/pets", nil) request.Header.Add("Accept", "application/json+special") recorder := httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, http.StatusNotAcceptable, recorder.Code) // acceptable as defined as default by the API (not explicit in the spec) request.Header.Add("Accept", "application/json") recorder = httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, http.StatusOK, recorder.Code) } } func TestContentType_Issue174(t *testing.T) { swspec, err := loads.Spec("../fixtures/bugs/174/swagger.yml") if assert.NoError(t, err) { api := untyped.NewAPI(swspec) api.RegisterConsumer("application/json", runtime.JSONConsumer()) api.RegisterProducer("application/json", runtime.JSONProducer()) api.RegisterOperation("get", "/pets", new(stubOperationHandler)) handler := Serve(swspec, api) request, _ := http.NewRequest("GET", "/pets", nil) recorder := httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, http.StatusOK, recorder.Code) } } func TestServe(t *testing.T) { spec, api := petstore.NewAPI(t) handler := Serve(spec, api) // serve spec document request, _ := http.NewRequest("GET", "http://localhost:8080/swagger.json", nil) request.Header.Add("Content-Type", runtime.JSONMime) request.Header.Add("Accept", runtime.JSONMime) recorder := httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) request, _ = http.NewRequest("GET", "http://localhost:8080/swagger-ui", nil) recorder = httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, 404, recorder.Code) } func TestContextAuthorize(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := runtime.JSONRequest("GET", "/api/pets", nil) ri, reqWithCtx, ok := ctx.RouteInfo(request) assert.True(t, ok) assert.NotNil(t, reqWithCtx) request = reqWithCtx p, reqWithCtx, err := ctx.Authorize(request, ri) assert.Error(t, err) assert.Nil(t, p) assert.Nil(t, reqWithCtx) v := request.Context().Value(ctxSecurityPrincipal) assert.Nil(t, v) request.SetBasicAuth("wrong", "wrong") p, reqWithCtx, err = ctx.Authorize(request, ri) assert.Error(t, err) assert.Nil(t, p) assert.Nil(t, reqWithCtx) v = request.Context().Value(ctxSecurityPrincipal) assert.Nil(t, v) request.SetBasicAuth("admin", "admin") p, reqWithCtx, err = ctx.Authorize(request, ri) assert.NoError(t, err) assert.Equal(t, "admin", p) assert.NotNil(t, reqWithCtx) // Assign the new returned request to follow with the test request = reqWithCtx v, ok = request.Context().Value(ctxSecurityPrincipal).(string) assert.True(t, ok) assert.Equal(t, "admin", v) // Once the request context contains the principal the authentication // isn't rechecked request.SetBasicAuth("doesn't matter", "doesn't") pp, reqCtx, rr := ctx.Authorize(request, ri) assert.Equal(t, p, pp) assert.Equal(t, err, rr) assert.Equal(t, request, reqCtx) } func TestContextAuthorize_WithAuthorizer(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := runtime.JSONRequest("POST", "/api/pets", nil) ri, reqWithCtx, ok := ctx.RouteInfo(request) assert.True(t, ok) assert.NotNil(t, reqWithCtx) request = reqWithCtx request.SetBasicAuth("topuser", "topuser") p, reqWithCtx, err := ctx.Authorize(request, ri) assertAPIError(t, apierrors.InvalidTypeCode, err) assert.Nil(t, p) assert.Nil(t, reqWithCtx) request.SetBasicAuth("admin", "admin") p, reqWithCtx, err = ctx.Authorize(request, ri) assert.NoError(t, err) assert.Equal(t, "admin", p) assert.NotNil(t, reqWithCtx) request.SetBasicAuth("anyother", "anyother") p, reqWithCtx, err = ctx.Authorize(request, ri) assert.Error(t, err) ae, ok := err.(apierrors.Error) assert.True(t, ok) assert.Equal(t, http.StatusForbidden, int(ae.Code())) assert.Nil(t, p) assert.Nil(t, reqWithCtx) } func TestContextNegotiateContentType(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("POST", "/api/pets", nil) // request.Header.Add("Accept", "*/*") request.Header.Add("content-type", "text/html") v := request.Context().Value(ctxBoundParams) assert.Nil(t, v) ri, request, _ := ctx.RouteInfo(request) res := NegotiateContentType(request, ri.Produces, "text/plain") assert.Equal(t, ri.Produces[0], res) } func TestContextBindValidRequest(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) // invalid content-type value request, _ := http.NewRequest("POST", "/api/pets", strings.NewReader(`{"name":"dog"}`)) request.Header.Add("content-type", "/json") ri, request, _ := ctx.RouteInfo(request) assertAPIError(t, 400, ctx.BindValidRequest(request, ri, new(stubBindRequester))) // unsupported content-type value request, _ = http.NewRequest("POST", "/api/pets", strings.NewReader(`{"name":"dog"}`)) request.Header.Add("content-type", "text/html") ri, request, _ = ctx.RouteInfo(request) assertAPIError(t, http.StatusUnsupportedMediaType, ctx.BindValidRequest(request, ri, new(stubBindRequester))) // unacceptable accept value request, _ = http.NewRequest("POST", "/api/pets", nil) request.Header.Add("Accept", "application/vnd.cia.v1+json") request.Header.Add("content-type", "application/json") ri, request, _ = ctx.RouteInfo(request) assertAPIError(t, http.StatusNotAcceptable, ctx.BindValidRequest(request, ri, new(stubBindRequester))) } func TestContextBindValidRequest_Issue174(t *testing.T) { spec, err := loads.Spec("../fixtures/bugs/174/swagger.yml") assert.NoError(t, err) api := untyped.NewAPI(spec) api.RegisterConsumer("application/json", runtime.JSONConsumer()) api.RegisterProducer("application/json", runtime.JSONProducer()) api.RegisterOperation("get", "/pets", new(stubOperationHandler)) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("GET", "/pets", nil) ri, request, _ := ctx.RouteInfo(request) assert.NoError(t, ctx.BindValidRequest(request, ri, new(stubBindRequester))) } func TestContextBindAndValidate(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("POST", "/api/pets", nil) request.Header.Add("Accept", "*/*") request.Header.Add("content-type", "text/html") request.ContentLength = 1 v := request.Context().Value(ctxBoundParams) assert.Nil(t, v) ri, request, _ := ctx.RouteInfo(request) data, request, result := ctx.BindAndValidate(request, ri) // this requires a much more thorough test assert.NotNil(t, data) assert.NotNil(t, result) v, ok := request.Context().Value(ctxBoundParams).(*validation) assert.True(t, ok) assert.NotNil(t, v) dd, rCtx, rr := ctx.BindAndValidate(request, ri) assert.Equal(t, data, dd) assert.Equal(t, result, rr) assert.Equal(t, rCtx, request) } func TestContextRender(t *testing.T) { ct := runtime.JSONMime spec, api := petstore.NewAPI(t) assert.NotNil(t, spec) assert.NotNil(t, api) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("GET", "/api/pets", nil) request.Header.Set(runtime.HeaderAccept, ct) ri, request, _ := ctx.RouteInfo(request) recorder := httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, map[string]interface{}{"name": "hello"}) assert.Equal(t, 200, recorder.Code) assert.Equal(t, "{\"name\":\"hello\"}\n", recorder.Body.String()) recorder = httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, errors.New("this went wrong")) assert.Equal(t, 500, recorder.Code) // recorder = httptest.NewRecorder() // assert.Panics(t, func() { ctx.Respond(recorder, request, []string{ct}, ri, map[int]interface{}{1: "hello"}) }) // Panic when route is nil and there is not a producer for the requested response format recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/api/pets", nil) request.Header.Set(runtime.HeaderAccept, "text/xml") assert.Panics(t, func() { ctx.Respond(recorder, request, []string{}, nil, map[string]interface{}{"name": "hello"}) }) request, _ = http.NewRequest("GET", "/api/pets", nil) request.Header.Set(runtime.HeaderAccept, ct) ri, request, _ = ctx.RouteInfo(request) recorder = httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, map[string]interface{}{"name": "hello"}) assert.Equal(t, 200, recorder.Code) assert.Equal(t, "{\"name\":\"hello\"}\n", recorder.Body.String()) recorder = httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, errors.New("this went wrong")) assert.Equal(t, 500, recorder.Code) // recorder = httptest.NewRecorder() // assert.Panics(t, func() { ctx.Respond(recorder, request, []string{ct}, ri, map[int]interface{}{1: "hello"}) }) // recorder = httptest.NewRecorder() // request, _ = http.NewRequest("GET", "/pets", nil) // assert.Panics(t, func() { ctx.Respond(recorder, request, []string{}, ri, map[string]interface{}{"name": "hello"}) }) recorder = httptest.NewRecorder() request, _ = http.NewRequest("DELETE", "/api/pets/1", nil) ri, request, _ = ctx.RouteInfo(request) ctx.Respond(recorder, request, ri.Produces, ri, nil) assert.Equal(t, 204, recorder.Code) } func TestContextValidResponseFormat(t *testing.T) { ct := "application/json" spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("GET", "http://localhost:8080", nil) request.Header.Set(runtime.HeaderAccept, ct) // check there's nothing there cached, ok := request.Context().Value(ctxResponseFormat).(string) assert.False(t, ok) assert.Empty(t, cached) // trigger the parse mt, request := ctx.ResponseFormat(request, []string{ct}) assert.Equal(t, ct, mt) // check it was cached cached, ok = request.Context().Value(ctxResponseFormat).(string) assert.True(t, ok) assert.Equal(t, ct, cached) // check if the cast works and fetch from cache too mt, _ = ctx.ResponseFormat(request, []string{ct}) assert.Equal(t, ct, mt) } func TestContextInvalidResponseFormat(t *testing.T) { ct := "application/x-yaml" other := "application/sgml" spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("GET", "http://localhost:8080", nil) request.Header.Set(runtime.HeaderAccept, ct) // check there's nothing there cached, ok := request.Context().Value(ctxResponseFormat).(string) assert.False(t, ok) assert.Empty(t, cached) // trigger the parse mt, request := ctx.ResponseFormat(request, []string{other}) assert.Empty(t, mt) // check it was cached cached, ok = request.Context().Value(ctxResponseFormat).(string) assert.False(t, ok) assert.Empty(t, cached) // check if the cast works and fetch from cache too mt, rCtx := ctx.ResponseFormat(request, []string{other}) assert.Empty(t, mt) assert.Equal(t, request, rCtx) } func TestContextValidRoute(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("GET", "/api/pets", nil) // check there's nothing there cached := request.Context().Value(ctxMatchedRoute) assert.Nil(t, cached) matched, rCtx, ok := ctx.RouteInfo(request) assert.True(t, ok) assert.NotNil(t, matched) assert.NotNil(t, rCtx) assert.NotEqual(t, request, rCtx) request = rCtx // check it was cached _, ok = request.Context().Value(ctxMatchedRoute).(*MatchedRoute) assert.True(t, ok) matched, rCtx, ok = ctx.RouteInfo(request) assert.True(t, ok) assert.NotNil(t, matched) assert.Equal(t, request, rCtx) } func TestContextInvalidRoute(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("DELETE", "pets", nil) // check there's nothing there cached := request.Context().Value(ctxMatchedRoute) assert.Nil(t, cached) matched, rCtx, ok := ctx.RouteInfo(request) assert.False(t, ok) assert.Nil(t, matched) assert.Nil(t, rCtx) // check it was not cached cached = request.Context().Value(ctxMatchedRoute) assert.Nil(t, cached) matched, rCtx, ok = ctx.RouteInfo(request) assert.False(t, ok) assert.Nil(t, matched) assert.Nil(t, rCtx) } func TestContextValidContentType(t *testing.T) { ct := "application/json" ctx := NewContext(nil, nil, nil) request, _ := http.NewRequest("GET", "http://localhost:8080", nil) request.Header.Set(runtime.HeaderContentType, ct) // check there's nothing there cached := request.Context().Value(ctxContentType) assert.Nil(t, cached) // trigger the parse mt, _, rCtx, err := ctx.ContentType(request) assert.NoError(t, err) assert.Equal(t, ct, mt) assert.NotNil(t, rCtx) assert.NotEqual(t, request, rCtx) request = rCtx // check it was cached cached = request.Context().Value(ctxContentType) assert.NotNil(t, cached) // check if the cast works and fetch from cache too mt, _, rCtx, err = ctx.ContentType(request) assert.NoError(t, err) assert.Equal(t, ct, mt) assert.Equal(t, request, rCtx) } func TestContextInvalidContentType(t *testing.T) { ct := "application(" ctx := NewContext(nil, nil, nil) request, _ := http.NewRequest("GET", "http://localhost:8080", nil) request.Header.Set(runtime.HeaderContentType, ct) // check there's nothing there cached := request.Context().Value(ctxContentType) assert.Nil(t, cached) // trigger the parse mt, _, rCtx, err := ctx.ContentType(request) assert.Error(t, err) assert.Empty(t, mt) assert.Nil(t, rCtx) // check it was not cached cached = request.Context().Value(ctxContentType) assert.Nil(t, cached) // check if the failure continues _, _, rCtx, err = ctx.ContentType(request) assert.Error(t, err) assert.Nil(t, rCtx) } runtime-0.21.0/middleware/denco/000077500000000000000000000000001413666762700165045ustar00rootroot00000000000000runtime-0.21.0/middleware/denco/LICENSE000066400000000000000000000020621413666762700175110ustar00rootroot00000000000000Copyright (c) 2014 Naoya Inada 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. runtime-0.21.0/middleware/denco/README.md000066400000000000000000000111651413666762700177670ustar00rootroot00000000000000# Denco [![Build Status](https://travis-ci.org/naoina/denco.png?branch=master)](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. runtime-0.21.0/middleware/denco/router.go000066400000000000000000000264721413666762700203660ustar00rootroot00000000000000// 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] } runtime-0.21.0/middleware/denco/router_bench_test.go000066400000000000000000000107261413666762700225570ustar00rootroot00000000000000package denco_test import ( "bytes" "crypto/rand" "fmt" "math/big" "testing" "github.com/go-openapi/runtime/middleware/denco" ) func BenchmarkRouterLookupStatic100(b *testing.B) { benchmarkRouterLookupStatic(b, 100) } func BenchmarkRouterLookupStatic300(b *testing.B) { benchmarkRouterLookupStatic(b, 300) } func BenchmarkRouterLookupStatic700(b *testing.B) { benchmarkRouterLookupStatic(b, 700) } func BenchmarkRouterLookupSingleParam100(b *testing.B) { records := makeTestSingleParamRecords(100) benchmarkRouterLookupSingleParam(b, records) } func BenchmarkRouterLookupSingleParam300(b *testing.B) { records := makeTestSingleParamRecords(300) benchmarkRouterLookupSingleParam(b, records) } func BenchmarkRouterLookupSingleParam700(b *testing.B) { records := makeTestSingleParamRecords(700) benchmarkRouterLookupSingleParam(b, records) } func BenchmarkRouterLookupSingle2Param100(b *testing.B) { records := makeTestSingle2ParamRecords(100) benchmarkRouterLookupSingleParam(b, records) } func BenchmarkRouterLookupSingle2Param300(b *testing.B) { records := makeTestSingle2ParamRecords(300) benchmarkRouterLookupSingleParam(b, records) } func BenchmarkRouterLookupSingle2Param700(b *testing.B) { records := makeTestSingle2ParamRecords(700) benchmarkRouterLookupSingleParam(b, records) } func BenchmarkRouterBuildStatic100(b *testing.B) { records := makeTestStaticRecords(100) benchmarkRouterBuild(b, records) } func BenchmarkRouterBuildStatic300(b *testing.B) { records := makeTestStaticRecords(300) benchmarkRouterBuild(b, records) } func BenchmarkRouterBuildStatic700(b *testing.B) { records := makeTestStaticRecords(700) benchmarkRouterBuild(b, records) } func BenchmarkRouterBuildSingleParam100(b *testing.B) { records := makeTestSingleParamRecords(100) benchmarkRouterBuild(b, records) } func BenchmarkRouterBuildSingleParam300(b *testing.B) { records := makeTestSingleParamRecords(300) benchmarkRouterBuild(b, records) } func BenchmarkRouterBuildSingleParam700(b *testing.B) { records := makeTestSingleParamRecords(700) benchmarkRouterBuild(b, records) } func BenchmarkRouterBuildSingle2Param100(b *testing.B) { records := makeTestSingle2ParamRecords(100) benchmarkRouterBuild(b, records) } func BenchmarkRouterBuildSingle2Param300(b *testing.B) { records := makeTestSingle2ParamRecords(300) benchmarkRouterBuild(b, records) } func BenchmarkRouterBuildSingle2Param700(b *testing.B) { records := makeTestSingle2ParamRecords(700) benchmarkRouterBuild(b, records) } func benchmarkRouterLookupStatic(b *testing.B, n int) { b.StopTimer() router := denco.New() records := makeTestStaticRecords(n) if err := router.Build(records); err != nil { b.Fatal(err) } record := pickTestRecord(records) b.StartTimer() for i := 0; i < b.N; i++ { if r, _, _ := router.Lookup(record.Key); r != record.Value { b.Fail() } } } func benchmarkRouterLookupSingleParam(b *testing.B, records []denco.Record) { router := denco.New() if err := router.Build(records); err != nil { b.Fatal(err) } record := pickTestRecord(records) b.ResetTimer() for i := 0; i < b.N; i++ { if _, _, found := router.Lookup(record.Key); !found { b.Fail() } } } func benchmarkRouterBuild(b *testing.B, records []denco.Record) { for i := 0; i < b.N; i++ { router := denco.New() if err := router.Build(records); err != nil { b.Fatal(err) } } } func makeTestStaticRecords(n int) []denco.Record { records := make([]denco.Record, n) for i := 0; i < n; i++ { records[i] = denco.NewRecord("/"+randomString(50), fmt.Sprintf("testroute%d", i)) } return records } func makeTestSingleParamRecords(n int) []denco.Record { records := make([]denco.Record, n) for i := 0; i < len(records); i++ { records[i] = denco.NewRecord(fmt.Sprintf("/user%d/:name", i), fmt.Sprintf("testroute%d", i)) } return records } func makeTestSingle2ParamRecords(n int) []denco.Record { records := make([]denco.Record, n) for i := 0; i < len(records); i++ { records[i] = denco.NewRecord(fmt.Sprintf("/user%d/:name/comment/:id", i), fmt.Sprintf("testroute%d", i)) } return records } func pickTestRecord(records []denco.Record) denco.Record { return records[len(records)/2] } func randomString(n int) string { const srcStrings = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/" var buf bytes.Buffer for i := 0; i < n; i++ { num, err := rand.Int(rand.Reader, big.NewInt(int64(len(srcStrings)-1))) if err != nil { panic(err) } buf.WriteByte(srcStrings[num.Int64()]) } return buf.String() } runtime-0.21.0/middleware/denco/router_test.go000066400000000000000000000775001413666762700214230ustar00rootroot00000000000000package denco_test import ( "fmt" "math/rand" "reflect" "testing" "time" "github.com/go-openapi/runtime/middleware/denco" ) func routes() []denco.Record { return []denco.Record{ {"/", "testroute0"}, {"/path/to/route", "testroute1"}, {"/path/to/other", "testroute2"}, {"/path/to/route/a", "testroute3"}, {"/path/to/:param", "testroute4"}, {"/gists/:param1/foo/:param2", "testroute12"}, {"/gists/:param1/foo/bar", "testroute11"}, {"/:param1/:param2/foo/:param3", "testroute13"}, {"/path/to/wildcard/*routepath", "testroute5"}, {"/path/to/:param1/:param2", "testroute6"}, {"/path/to/:param1/sep/:param2", "testroute7"}, {"/:year/:month/:day", "testroute8"}, {"/user/:id", "testroute9"}, {"/a/to/b/:param/*routepath", "testroute10"}, {"/path/with/key=:value", "testroute14"}, } } var realURIs = []denco.Record{ {"/authorizations", "/authorizations"}, {"/authorizations/:id", "/authorizations/:id"}, {"/applications/:client_id/tokens/:access_token", "/applications/:client_id/tokens/:access_token"}, {"/events", "/events"}, {"/repos/:owner/:repo/events", "/repos/:owner/:repo/events"}, {"/networks/:owner/:repo/events", "/networks/:owner/:repo/events"}, {"/orgs/:org/events", "/orgs/:org/events"}, {"/users/:user/received_events", "/users/:user/received_events"}, {"/users/:user/received_events/public", "/users/:user/received_events/public"}, {"/users/:user/events", "/users/:user/events"}, {"/users/:user/events/public", "/users/:user/events/public"}, {"/users/:user/events/orgs/:org", "/users/:user/events/orgs/:org"}, {"/feeds", "/feeds"}, {"/notifications", "/notifications"}, {"/repos/:owner/:repo/notifications", "/repos/:owner/:repo/notifications"}, {"/notifications/threads/:id", "/notifications/threads/:id"}, {"/notifications/threads/:id/subscription", "/notifications/threads/:id/subscription"}, {"/repos/:owner/:repo/stargazers", "/repos/:owner/:repo/stargazers"}, {"/users/:user/starred", "/users/:user/starred"}, {"/user/starred", "/user/starred"}, {"/user/starred/:owner/:repo", "/user/starred/:owner/:repo"}, {"/repos/:owner/:repo/subscribers", "/repos/:owner/:repo/subscribers"}, {"/users/:user/subscriptions", "/users/:user/subscriptions"}, {"/user/subscriptions", "/user/subscriptions"}, {"/repos/:owner/:repo/subscription", "/repos/:owner/:repo/subscription"}, {"/user/subscriptions/:owner/:repo", "/user/subscriptions/:owner/:repo"}, {"/users/:user/gists", "/users/:user/gists"}, {"/gists", "/gists"}, {"/gists/:id", "/gists/:id"}, {"/gists/:id/star", "/gists/:id/star"}, {"/repos/:owner/:repo/git/blobs/:sha", "/repos/:owner/:repo/git/blobs/:sha"}, {"/repos/:owner/:repo/git/commits/:sha", "/repos/:owner/:repo/git/commits/:sha"}, {"/repos/:owner/:repo/git/refs", "/repos/:owner/:repo/git/refs"}, {"/repos/:owner/:repo/git/tags/:sha", "/repos/:owner/:repo/git/tags/:sha"}, {"/repos/:owner/:repo/git/trees/:sha", "/repos/:owner/:repo/git/trees/:sha"}, {"/issues", "/issues"}, {"/user/issues", "/user/issues"}, {"/orgs/:org/issues", "/orgs/:org/issues"}, {"/repos/:owner/:repo/issues", "/repos/:owner/:repo/issues"}, {"/repos/:owner/:repo/issues/:number", "/repos/:owner/:repo/issues/:number"}, {"/repos/:owner/:repo/assignees", "/repos/:owner/:repo/assignees"}, {"/repos/:owner/:repo/assignees/:assignee", "/repos/:owner/:repo/assignees/:assignee"}, {"/repos/:owner/:repo/issues/:number/comments", "/repos/:owner/:repo/issues/:number/comments"}, {"/repos/:owner/:repo/issues/:number/events", "/repos/:owner/:repo/issues/:number/events"}, {"/repos/:owner/:repo/labels", "/repos/:owner/:repo/labels"}, {"/repos/:owner/:repo/labels/:name", "/repos/:owner/:repo/labels/:name"}, {"/repos/:owner/:repo/issues/:number/labels", "/repos/:owner/:repo/issues/:number/labels"}, {"/repos/:owner/:repo/milestones/:number/labels", "/repos/:owner/:repo/milestones/:number/labels"}, {"/repos/:owner/:repo/milestones", "/repos/:owner/:repo/milestones"}, {"/repos/:owner/:repo/milestones/:number", "/repos/:owner/:repo/milestones/:number"}, {"/emojis", "/emojis"}, {"/gitignore/templates", "/gitignore/templates"}, {"/gitignore/templates/:name", "/gitignore/templates/:name"}, {"/meta", "/meta"}, {"/rate_limit", "/rate_limit"}, {"/users/:user/orgs", "/users/:user/orgs"}, {"/user/orgs", "/user/orgs"}, {"/orgs/:org", "/orgs/:org"}, {"/orgs/:org/members", "/orgs/:org/members"}, {"/orgs/:org/members/:user", "/orgs/:org/members/:user"}, {"/orgs/:org/public_members", "/orgs/:org/public_members"}, {"/orgs/:org/public_members/:user", "/orgs/:org/public_members/:user"}, {"/orgs/:org/teams", "/orgs/:org/teams"}, {"/teams/:id", "/teams/:id"}, {"/teams/:id/members", "/teams/:id/members"}, {"/teams/:id/members/:user", "/teams/:id/members/:user"}, {"/teams/:id/repos", "/teams/:id/repos"}, {"/teams/:id/repos/:owner/:repo", "/teams/:id/repos/:owner/:repo"}, {"/user/teams", "/user/teams"}, {"/repos/:owner/:repo/pulls", "/repos/:owner/:repo/pulls"}, {"/repos/:owner/:repo/pulls/:number", "/repos/:owner/:repo/pulls/:number"}, {"/repos/:owner/:repo/pulls/:number/commits", "/repos/:owner/:repo/pulls/:number/commits"}, {"/repos/:owner/:repo/pulls/:number/files", "/repos/:owner/:repo/pulls/:number/files"}, {"/repos/:owner/:repo/pulls/:number/merge", "/repos/:owner/:repo/pulls/:number/merge"}, {"/repos/:owner/:repo/pulls/:number/comments", "/repos/:owner/:repo/pulls/:number/comments"}, {"/user/repos", "/user/repos"}, {"/users/:user/repos", "/users/:user/repos"}, {"/orgs/:org/repos", "/orgs/:org/repos"}, {"/repositories", "/repositories"}, {"/repos/:owner/:repo", "/repos/:owner/:repo"}, {"/repos/:owner/:repo/contributors", "/repos/:owner/:repo/contributors"}, {"/repos/:owner/:repo/languages", "/repos/:owner/:repo/languages"}, {"/repos/:owner/:repo/teams", "/repos/:owner/:repo/teams"}, {"/repos/:owner/:repo/tags", "/repos/:owner/:repo/tags"}, {"/repos/:owner/:repo/branches", "/repos/:owner/:repo/branches"}, {"/repos/:owner/:repo/branches/:branch", "/repos/:owner/:repo/branches/:branch"}, {"/repos/:owner/:repo/collaborators", "/repos/:owner/:repo/collaborators"}, {"/repos/:owner/:repo/collaborators/:user", "/repos/:owner/:repo/collaborators/:user"}, {"/repos/:owner/:repo/comments", "/repos/:owner/:repo/comments"}, {"/repos/:owner/:repo/commits/:sha/comments", "/repos/:owner/:repo/commits/:sha/comments"}, {"/repos/:owner/:repo/comments/:id", "/repos/:owner/:repo/comments/:id"}, {"/repos/:owner/:repo/commits", "/repos/:owner/:repo/commits"}, {"/repos/:owner/:repo/commits/:sha", "/repos/:owner/:repo/commits/:sha"}, {"/repos/:owner/:repo/readme", "/repos/:owner/:repo/readme"}, {"/repos/:owner/:repo/keys", "/repos/:owner/:repo/keys"}, {"/repos/:owner/:repo/keys/:id", "/repos/:owner/:repo/keys/:id"}, {"/repos/:owner/:repo/downloads", "/repos/:owner/:repo/downloads"}, {"/repos/:owner/:repo/downloads/:id", "/repos/:owner/:repo/downloads/:id"}, {"/repos/:owner/:repo/forks", "/repos/:owner/:repo/forks"}, {"/repos/:owner/:repo/hooks", "/repos/:owner/:repo/hooks"}, {"/repos/:owner/:repo/hooks/:id", "/repos/:owner/:repo/hooks/:id"}, {"/repos/:owner/:repo/releases", "/repos/:owner/:repo/releases"}, {"/repos/:owner/:repo/releases/:id", "/repos/:owner/:repo/releases/:id"}, {"/repos/:owner/:repo/releases/:id/assets", "/repos/:owner/:repo/releases/:id/assets"}, {"/repos/:owner/:repo/stats/contributors", "/repos/:owner/:repo/stats/contributors"}, {"/repos/:owner/:repo/stats/commit_activity", "/repos/:owner/:repo/stats/commit_activity"}, {"/repos/:owner/:repo/stats/code_frequency", "/repos/:owner/:repo/stats/code_frequency"}, {"/repos/:owner/:repo/stats/participation", "/repos/:owner/:repo/stats/participation"}, {"/repos/:owner/:repo/stats/punch_card", "/repos/:owner/:repo/stats/punch_card"}, {"/repos/:owner/:repo/statuses/:ref", "/repos/:owner/:repo/statuses/:ref"}, {"/search/repositories", "/search/repositories"}, {"/search/code", "/search/code"}, {"/search/issues", "/search/issues"}, {"/search/users", "/search/users"}, {"/legacy/issues/search/:owner/:repository/:state/:keyword", "/legacy/issues/search/:owner/:repository/:state/:keyword"}, {"/legacy/repos/search/:keyword", "/legacy/repos/search/:keyword"}, {"/legacy/user/search/:keyword", "/legacy/user/search/:keyword"}, {"/legacy/user/email/:email", "/legacy/user/email/:email"}, {"/users/:user", "/users/:user"}, {"/user", "/user"}, {"/users", "/users"}, {"/user/emails", "/user/emails"}, {"/users/:user/followers", "/users/:user/followers"}, {"/user/followers", "/user/followers"}, {"/users/:user/following", "/users/:user/following"}, {"/user/following", "/user/following"}, {"/user/following/:user", "/user/following/:user"}, {"/users/:user/following/:target_user", "/users/:user/following/:target_user"}, {"/users/:user/keys", "/users/:user/keys"}, {"/user/keys", "/user/keys"}, {"/user/keys/:id", "/user/keys/:id"}, {"/people/:userId", "/people/:userId"}, {"/people", "/people"}, {"/activities/:activityId/people/:collection", "/activities/:activityId/people/:collection"}, {"/people/:userId/people/:collection", "/people/:userId/people/:collection"}, {"/people/:userId/openIdConnect", "/people/:userId/openIdConnect"}, {"/people/:userId/activities/:collection", "/people/:userId/activities/:collection"}, {"/activities/:activityId", "/activities/:activityId"}, {"/activities", "/activities"}, {"/activities/:activityId/comments", "/activities/:activityId/comments"}, {"/comments/:commentId", "/comments/:commentId"}, {"/people/:userId/moments/:collection", "/people/:userId/moments/:collection"}, } type testcase struct { path string value interface{} params []denco.Param found bool } func runLookupTest(t *testing.T, records []denco.Record, testcases []testcase) { r := denco.New() if err := r.Build(records); err != nil { t.Fatal(err) } for _, testcase := range testcases { data, params, found := r.Lookup(testcase.path) if !reflect.DeepEqual(data, testcase.value) || !reflect.DeepEqual(params, denco.Params(testcase.params)) || !reflect.DeepEqual(found, testcase.found) { t.Errorf("Router.Lookup(%q) => (%#v, %#v, %#v), want (%#v, %#v, %#v)", testcase.path, data, params, found, testcase.value, denco.Params(testcase.params), testcase.found) } } } func TestRouter_Lookup(t *testing.T) { testcases := []testcase{ {"/", "testroute0", nil, true}, {"/gists/1323/foo/bar", "testroute11", []denco.Param{{"param1", "1323"}}, true}, {"/gists/1323/foo/133", "testroute12", []denco.Param{{"param1", "1323"}, {"param2", "133"}}, true}, {"/234/1323/foo/133", "testroute13", []denco.Param{{"param1", "234"}, {"param2", "1323"}, {"param3", "133"}}, true}, {"/path/to/route", "testroute1", nil, true}, {"/path/to/other", "testroute2", nil, true}, {"/path/to/route/a", "testroute3", nil, true}, {"/path/to/hoge", "testroute4", []denco.Param{{"param", "hoge"}}, true}, {"/path/to/wildcard/some/params", "testroute5", []denco.Param{{"routepath", "some/params"}}, true}, {"/path/to/o1/o2", "testroute6", []denco.Param{{"param1", "o1"}, {"param2", "o2"}}, true}, {"/path/to/p1/sep/p2", "testroute7", []denco.Param{{"param1", "p1"}, {"param2", "p2"}}, true}, {"/2014/01/06", "testroute8", []denco.Param{{"year", "2014"}, {"month", "01"}, {"day", "06"}}, true}, {"/user/777", "testroute9", []denco.Param{{"id", "777"}}, true}, {"/a/to/b/p1/some/wildcard/params", "testroute10", []denco.Param{{"param", "p1"}, {"routepath", "some/wildcard/params"}}, true}, {"/missing", nil, nil, false}, {"/path/with/key=value", "testroute14", []denco.Param{{"value", "value"}}, true}, } runLookupTest(t, routes(), testcases) records := []denco.Record{ {"/", "testroute0"}, {"/:b", "testroute1"}, {"/*wildcard", "testroute2"}, } testcases = []testcase{ {"/", "testroute0", nil, true}, {"/true", "testroute1", []denco.Param{{"b", "true"}}, true}, {"/foo/bar", "testroute2", []denco.Param{{"wildcard", "foo/bar"}}, true}, } runLookupTest(t, records, testcases) records = []denco.Record{ {"/networks/:owner/:repo/events", "testroute0"}, {"/orgs/:org/events", "testroute1"}, {"/notifications/threads/:id", "testroute2"}, {"/mypathisgreat/:thing-id", "testroute3"}, } testcases = []testcase{ {"/networks/:owner/:repo/events", "testroute0", []denco.Param{{"owner", ":owner"}, {"repo", ":repo"}}, true}, {"/orgs/:org/events", "testroute1", []denco.Param{{"org", ":org"}}, true}, {"/notifications/threads/:id", "testroute2", []denco.Param{{"id", ":id"}}, true}, {"/mypathisgreat/:thing-id", "testroute3", []denco.Param{{"thing-id", ":thing-id"}}, true}, } runLookupTest(t, records, testcases) runLookupTest(t, []denco.Record{ {"/", "route2"}, }, []testcase{ {"/user/alice", nil, nil, false}, }) runLookupTest(t, []denco.Record{ {"/user/:name", "route1"}, }, []testcase{ {"/", nil, nil, false}, }) runLookupTest(t, []denco.Record{ {"/*wildcard", "testroute0"}, {"/a/:b", "testroute1"}, }, []testcase{ {"/a", "testroute0", []denco.Param{{"wildcard", "a"}}, true}, }) } func TestRouter_Lookup_withManyRoutes(t *testing.T) { n := 1000 rand.Seed(time.Now().UnixNano()) records := make([]denco.Record, n) for i := 0; i < n; i++ { records[i] = denco.Record{Key: "/" + randomString(rand.Intn(50)+10), Value: fmt.Sprintf("route%d", i)} } router := denco.New() if err := router.Build(records); err != nil { t.Fatal(err) } for _, r := range records { data, params, found := router.Lookup(r.Key) if !reflect.DeepEqual(data, r.Value) || len(params) != 0 || !reflect.DeepEqual(found, true) { t.Errorf("Router.Lookup(%q) => (%#v, %#v, %#v), want (%#v, %#v, %#v)", r.Key, data, len(params), found, r.Value, 0, true) } } } func TestRouter_Lookup_realURIs(t *testing.T) { testcases := []testcase{ {"/authorizations", "/authorizations", nil, true}, {"/authorizations/1", "/authorizations/:id", []denco.Param{{"id", "1"}}, true}, {"/applications/1/tokens/zohRoo7e", "/applications/:client_id/tokens/:access_token", []denco.Param{{"client_id", "1"}, {"access_token", "zohRoo7e"}}, true}, {"/events", "/events", nil, true}, {"/repos/naoina/denco/events", "/repos/:owner/:repo/events", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/networks/naoina/denco/events", "/networks/:owner/:repo/events", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/orgs/something/events", "/orgs/:org/events", []denco.Param{{"org", "something"}}, true}, {"/users/naoina/received_events", "/users/:user/received_events", []denco.Param{{"user", "naoina"}}, true}, {"/users/naoina/received_events/public", "/users/:user/received_events/public", []denco.Param{{"user", "naoina"}}, true}, {"/users/naoina/events", "/users/:user/events", []denco.Param{{"user", "naoina"}}, true}, {"/users/naoina/events/public", "/users/:user/events/public", []denco.Param{{"user", "naoina"}}, true}, {"/users/naoina/events/orgs/something", "/users/:user/events/orgs/:org", []denco.Param{{"user", "naoina"}, {"org", "something"}}, true}, {"/feeds", "/feeds", nil, true}, {"/notifications", "/notifications", nil, true}, {"/repos/naoina/denco/notifications", "/repos/:owner/:repo/notifications", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/notifications/threads/1", "/notifications/threads/:id", []denco.Param{{"id", "1"}}, true}, {"/notifications/threads/2/subscription", "/notifications/threads/:id/subscription", []denco.Param{{"id", "2"}}, true}, {"/repos/naoina/denco/stargazers", "/repos/:owner/:repo/stargazers", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/users/naoina/starred", "/users/:user/starred", []denco.Param{{"user", "naoina"}}, true}, {"/user/starred", "/user/starred", nil, true}, {"/user/starred/naoina/denco", "/user/starred/:owner/:repo", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/subscribers", "/repos/:owner/:repo/subscribers", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/users/naoina/subscriptions", "/users/:user/subscriptions", []denco.Param{{"user", "naoina"}}, true}, {"/user/subscriptions", "/user/subscriptions", nil, true}, {"/repos/naoina/denco/subscription", "/repos/:owner/:repo/subscription", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/user/subscriptions/naoina/denco", "/user/subscriptions/:owner/:repo", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/users/naoina/gists", "/users/:user/gists", []denco.Param{{"user", "naoina"}}, true}, {"/gists", "/gists", nil, true}, {"/gists/1", "/gists/:id", []denco.Param{{"id", "1"}}, true}, {"/gists/2/star", "/gists/:id/star", []denco.Param{{"id", "2"}}, true}, {"/repos/naoina/denco/git/blobs/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/blobs/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true}, {"/repos/naoina/denco/git/commits/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/commits/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true}, {"/repos/naoina/denco/git/refs", "/repos/:owner/:repo/git/refs", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/git/tags/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/tags/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true}, {"/repos/naoina/denco/git/trees/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/trees/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true}, {"/issues", "/issues", nil, true}, {"/user/issues", "/user/issues", nil, true}, {"/orgs/something/issues", "/orgs/:org/issues", []denco.Param{{"org", "something"}}, true}, {"/repos/naoina/denco/issues", "/repos/:owner/:repo/issues", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/issues/1", "/repos/:owner/:repo/issues/:number", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true}, {"/repos/naoina/denco/assignees", "/repos/:owner/:repo/assignees", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/assignees/foo", "/repos/:owner/:repo/assignees/:assignee", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"assignee", "foo"}}, true}, {"/repos/naoina/denco/issues/1/comments", "/repos/:owner/:repo/issues/:number/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true}, {"/repos/naoina/denco/issues/1/events", "/repos/:owner/:repo/issues/:number/events", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true}, {"/repos/naoina/denco/labels", "/repos/:owner/:repo/labels", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/labels/bug", "/repos/:owner/:repo/labels/:name", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"name", "bug"}}, true}, {"/repos/naoina/denco/issues/1/labels", "/repos/:owner/:repo/issues/:number/labels", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true}, {"/repos/naoina/denco/milestones/1/labels", "/repos/:owner/:repo/milestones/:number/labels", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true}, {"/repos/naoina/denco/milestones", "/repos/:owner/:repo/milestones", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/milestones/1", "/repos/:owner/:repo/milestones/:number", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true}, {"/emojis", "/emojis", nil, true}, {"/gitignore/templates", "/gitignore/templates", nil, true}, {"/gitignore/templates/Go", "/gitignore/templates/:name", []denco.Param{{"name", "Go"}}, true}, {"/meta", "/meta", nil, true}, {"/rate_limit", "/rate_limit", nil, true}, {"/users/naoina/orgs", "/users/:user/orgs", []denco.Param{{"user", "naoina"}}, true}, {"/user/orgs", "/user/orgs", nil, true}, {"/orgs/something", "/orgs/:org", []denco.Param{{"org", "something"}}, true}, {"/orgs/something/members", "/orgs/:org/members", []denco.Param{{"org", "something"}}, true}, {"/orgs/something/members/naoina", "/orgs/:org/members/:user", []denco.Param{{"org", "something"}, {"user", "naoina"}}, true}, {"/orgs/something/public_members", "/orgs/:org/public_members", []denco.Param{{"org", "something"}}, true}, {"/orgs/something/public_members/naoina", "/orgs/:org/public_members/:user", []denco.Param{{"org", "something"}, {"user", "naoina"}}, true}, {"/orgs/something/teams", "/orgs/:org/teams", []denco.Param{{"org", "something"}}, true}, {"/teams/1", "/teams/:id", []denco.Param{{"id", "1"}}, true}, {"/teams/2/members", "/teams/:id/members", []denco.Param{{"id", "2"}}, true}, {"/teams/3/members/naoina", "/teams/:id/members/:user", []denco.Param{{"id", "3"}, {"user", "naoina"}}, true}, {"/teams/4/repos", "/teams/:id/repos", []denco.Param{{"id", "4"}}, true}, {"/teams/5/repos/naoina/denco", "/teams/:id/repos/:owner/:repo", []denco.Param{{"id", "5"}, {"owner", "naoina"}, {"repo", "denco"}}, true}, {"/user/teams", "/user/teams", nil, true}, {"/repos/naoina/denco/pulls", "/repos/:owner/:repo/pulls", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/pulls/1", "/repos/:owner/:repo/pulls/:number", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true}, {"/repos/naoina/denco/pulls/1/commits", "/repos/:owner/:repo/pulls/:number/commits", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true}, {"/repos/naoina/denco/pulls/1/files", "/repos/:owner/:repo/pulls/:number/files", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true}, {"/repos/naoina/denco/pulls/1/merge", "/repos/:owner/:repo/pulls/:number/merge", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true}, {"/repos/naoina/denco/pulls/1/comments", "/repos/:owner/:repo/pulls/:number/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true}, {"/user/repos", "/user/repos", nil, true}, {"/users/naoina/repos", "/users/:user/repos", []denco.Param{{"user", "naoina"}}, true}, {"/orgs/something/repos", "/orgs/:org/repos", []denco.Param{{"org", "something"}}, true}, {"/repositories", "/repositories", nil, true}, {"/repos/naoina/denco", "/repos/:owner/:repo", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/contributors", "/repos/:owner/:repo/contributors", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/languages", "/repos/:owner/:repo/languages", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/teams", "/repos/:owner/:repo/teams", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/tags", "/repos/:owner/:repo/tags", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/branches", "/repos/:owner/:repo/branches", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/branches/master", "/repos/:owner/:repo/branches/:branch", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"branch", "master"}}, true}, {"/repos/naoina/denco/collaborators", "/repos/:owner/:repo/collaborators", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/collaborators/something", "/repos/:owner/:repo/collaborators/:user", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"user", "something"}}, true}, {"/repos/naoina/denco/comments", "/repos/:owner/:repo/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/commits/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9/comments", "/repos/:owner/:repo/commits/:sha/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true}, {"/repos/naoina/denco/comments/1", "/repos/:owner/:repo/comments/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true}, {"/repos/naoina/denco/commits", "/repos/:owner/:repo/commits", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/commits/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/commits/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true}, {"/repos/naoina/denco/readme", "/repos/:owner/:repo/readme", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/keys", "/repos/:owner/:repo/keys", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/keys/1", "/repos/:owner/:repo/keys/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true}, {"/repos/naoina/denco/downloads", "/repos/:owner/:repo/downloads", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/downloads/2", "/repos/:owner/:repo/downloads/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "2"}}, true}, {"/repos/naoina/denco/forks", "/repos/:owner/:repo/forks", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/hooks", "/repos/:owner/:repo/hooks", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/hooks/2", "/repos/:owner/:repo/hooks/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "2"}}, true}, {"/repos/naoina/denco/releases", "/repos/:owner/:repo/releases", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/releases/1", "/repos/:owner/:repo/releases/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true}, {"/repos/naoina/denco/releases/1/assets", "/repos/:owner/:repo/releases/:id/assets", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true}, {"/repos/naoina/denco/stats/contributors", "/repos/:owner/:repo/stats/contributors", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/stats/commit_activity", "/repos/:owner/:repo/stats/commit_activity", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/stats/code_frequency", "/repos/:owner/:repo/stats/code_frequency", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/stats/participation", "/repos/:owner/:repo/stats/participation", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/stats/punch_card", "/repos/:owner/:repo/stats/punch_card", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true}, {"/repos/naoina/denco/statuses/master", "/repos/:owner/:repo/statuses/:ref", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"ref", "master"}}, true}, {"/search/repositories", "/search/repositories", nil, true}, {"/search/code", "/search/code", nil, true}, {"/search/issues", "/search/issues", nil, true}, {"/search/users", "/search/users", nil, true}, {"/legacy/issues/search/naoina/denco/closed/test", "/legacy/issues/search/:owner/:repository/:state/:keyword", []denco.Param{{"owner", "naoina"}, {"repository", "denco"}, {"state", "closed"}, {"keyword", "test"}}, true}, {"/legacy/repos/search/test", "/legacy/repos/search/:keyword", []denco.Param{{"keyword", "test"}}, true}, {"/legacy/user/search/test", "/legacy/user/search/:keyword", []denco.Param{{"keyword", "test"}}, true}, {"/legacy/user/email/naoina@kuune.org", "/legacy/user/email/:email", []denco.Param{{"email", "naoina@kuune.org"}}, true}, {"/users/naoina", "/users/:user", []denco.Param{{"user", "naoina"}}, true}, {"/user", "/user", nil, true}, {"/users", "/users", nil, true}, {"/user/emails", "/user/emails", nil, true}, {"/users/naoina/followers", "/users/:user/followers", []denco.Param{{"user", "naoina"}}, true}, {"/user/followers", "/user/followers", nil, true}, {"/users/naoina/following", "/users/:user/following", []denco.Param{{"user", "naoina"}}, true}, {"/user/following", "/user/following", nil, true}, {"/user/following/naoina", "/user/following/:user", []denco.Param{{"user", "naoina"}}, true}, {"/users/naoina/following/target", "/users/:user/following/:target_user", []denco.Param{{"user", "naoina"}, {"target_user", "target"}}, true}, {"/users/naoina/keys", "/users/:user/keys", []denco.Param{{"user", "naoina"}}, true}, {"/user/keys", "/user/keys", nil, true}, {"/user/keys/1", "/user/keys/:id", []denco.Param{{"id", "1"}}, true}, {"/people/me", "/people/:userId", []denco.Param{{"userId", "me"}}, true}, {"/people", "/people", nil, true}, {"/activities/foo/people/vault", "/activities/:activityId/people/:collection", []denco.Param{{"activityId", "foo"}, {"collection", "vault"}}, true}, {"/people/me/people/vault", "/people/:userId/people/:collection", []denco.Param{{"userId", "me"}, {"collection", "vault"}}, true}, {"/people/me/openIdConnect", "/people/:userId/openIdConnect", []denco.Param{{"userId", "me"}}, true}, {"/people/me/activities/vault", "/people/:userId/activities/:collection", []denco.Param{{"userId", "me"}, {"collection", "vault"}}, true}, {"/activities/foo", "/activities/:activityId", []denco.Param{{"activityId", "foo"}}, true}, {"/activities", "/activities", nil, true}, {"/activities/foo/comments", "/activities/:activityId/comments", []denco.Param{{"activityId", "foo"}}, true}, {"/comments/hoge", "/comments/:commentId", []denco.Param{{"commentId", "hoge"}}, true}, {"/people/me/moments/vault", "/people/:userId/moments/:collection", []denco.Param{{"userId", "me"}, {"collection", "vault"}}, true}, } runLookupTest(t, realURIs, testcases) } func TestRouter_Build(t *testing.T) { // test for duplicate name of path parameters. func() { r := denco.New() if err := r.Build([]denco.Record{ {"/:user/:id/:id", "testroute0"}, {"/:user/:user/:id", "testroute0"}, }); err == nil { t.Errorf("no error returned by duplicate name of path parameters") } }() } func TestRouter_Build_withoutSizeHint(t *testing.T) { for _, v := range []struct { keys []string sizeHint int }{ {[]string{"/user"}, 0}, {[]string{"/user/:id"}, 1}, {[]string{"/user/:id/post"}, 1}, {[]string{"/user/:id/post:validate"}, 2}, {[]string{"/user/:id/:group"}, 2}, {[]string{"/user/:id/post/:cid"}, 2}, {[]string{"/user/:id/post/:cid", "/admin/:id/post/:cid"}, 2}, {[]string{"/user/:id", "/admin/:id/post/:cid"}, 2}, {[]string{"/user/:id/post/:cid", "/admin/:id/post/:cid/:type"}, 3}, } { r := denco.New() actual := r.SizeHint expect := -1 if !reflect.DeepEqual(actual, expect) { t.Errorf(`before Build; Router.SizeHint => (%[1]T=%#[1]v); want (%[2]T=%#[2]v)`, actual, expect) } records := make([]denco.Record, len(v.keys)) for i, k := range v.keys { records[i] = denco.Record{Key: k, Value: "value"} } if err := r.Build(records); err != nil { t.Fatal(err) } actual = r.SizeHint expect = v.sizeHint if !reflect.DeepEqual(actual, expect) { t.Errorf(`Router.Build(%#v); Router.SizeHint => (%[2]T=%#[2]v); want (%[3]T=%#[3]v)`, records, actual, expect) } } } func TestRouter_Build_withSizeHint(t *testing.T) { for _, v := range []struct { key string sizeHint int expect int }{ {"/user", 0, 0}, {"/user", 1, 1}, {"/user", 2, 2}, {"/user/:id", 3, 3}, {"/user/:id/:group", 0, 0}, {"/user/:id/:group", 1, 1}, {"/user/:id/:group:validate", 1, 1}, } { r := denco.New() r.SizeHint = v.sizeHint records := []denco.Record{ {v.key, "value"}, } if err := r.Build(records); err != nil { t.Fatal(err) } actual := r.SizeHint expect := v.expect if !reflect.DeepEqual(actual, expect) { t.Errorf(`Router.Build(%#v); Router.SizeHint => (%[2]T=%#[2]v); want (%[3]T=%#[3]v)`, records, actual, expect) } } } func TestParams_Get(t *testing.T) { params := denco.Params([]denco.Param{ {"name1", "value1"}, {"name2", "value2"}, {"name3", "value3"}, {"name1", "value4"}, }) for _, v := range []struct{ value, expected string }{ {"name1", "value1"}, {"name2", "value2"}, {"name3", "value3"}, {"name4", ""}, } { actual := params.Get(v.value) expected := v.expected if !reflect.DeepEqual(actual, expected) { t.Errorf("Params.Get(%q) => %#v, want %#v", v.value, actual, expected) } } } runtime-0.21.0/middleware/denco/server.go000066400000000000000000000053431413666762700203460ustar00rootroot00000000000000package 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) } runtime-0.21.0/middleware/denco/server_test.go000066400000000000000000000062341413666762700214050ustar00rootroot00000000000000package denco_test import ( "fmt" "io/ioutil" "net/http" "net/http/httptest" "testing" "github.com/go-openapi/runtime/middleware/denco" ) func testHandlerFunc(w http.ResponseWriter, r *http.Request, params denco.Params) { fmt.Fprintf(w, "method: %s, path: %s, params: %v", r.Method, r.URL.Path, params) } func TestMux(t *testing.T) { mux := denco.NewMux() handler, err := mux.Build([]denco.Handler{ mux.GET("/", testHandlerFunc), mux.GET("/user/:name", testHandlerFunc), mux.POST("/user/:name", testHandlerFunc), mux.HEAD("/user/:name", testHandlerFunc), mux.PUT("/user/:name", testHandlerFunc), mux.Handler("GET", "/user/handler", testHandlerFunc), mux.Handler("POST", "/user/handler", testHandlerFunc), mux.Handler("PUT", "/user/inference", testHandlerFunc), }) if err != nil { t.Fatal(err) } server := httptest.NewServer(handler) defer server.Close() for _, v := range []struct { status int method, path, expected string }{ {200, "GET", "/", "method: GET, path: /, params: []"}, {200, "GET", "/user/alice", "method: GET, path: /user/alice, params: [{name alice}]"}, {200, "POST", "/user/bob", "method: POST, path: /user/bob, params: [{name bob}]"}, {200, "HEAD", "/user/alice", ""}, {200, "PUT", "/user/bob", "method: PUT, path: /user/bob, params: [{name bob}]"}, {404, "POST", "/", "404 page not found\n"}, {404, "GET", "/unknown", "404 page not found\n"}, {404, "POST", "/user/alice/1", "404 page not found\n"}, {200, "GET", "/user/handler", "method: GET, path: /user/handler, params: []"}, {200, "POST", "/user/handler", "method: POST, path: /user/handler, params: []"}, {200, "PUT", "/user/inference", "method: PUT, path: /user/inference, params: []"}, } { req, err := http.NewRequest(v.method, server.URL+v.path, nil) if err != nil { t.Error(err) continue } res, err := http.DefaultClient.Do(req) if err != nil { t.Error(err) continue } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { t.Error(err) continue } actual := string(body) expected := v.expected if res.StatusCode != v.status || actual != expected { t.Errorf(`%s "%s" => %#v %#v, want %#v %#v`, v.method, v.path, res.StatusCode, actual, v.status, expected) } } } func TestNotFound(t *testing.T) { mux := denco.NewMux() handler, err := mux.Build([]denco.Handler{}) if err != nil { t.Fatal(err) } server := httptest.NewServer(handler) defer server.Close() origNotFound := denco.NotFound defer func() { denco.NotFound = origNotFound }() denco.NotFound = func(w http.ResponseWriter, r *http.Request, params denco.Params) { w.WriteHeader(http.StatusServiceUnavailable) fmt.Fprintf(w, "method: %s, path: %s, params: %v", r.Method, r.URL.Path, params) } res, err := http.Get(server.URL) if err != nil { t.Fatal(err) } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatal(err) } actual := string(body) expected := "method: GET, path: /, params: []" if res.StatusCode != http.StatusServiceUnavailable || actual != expected { t.Errorf(`GET "/" => %#v %#v, want %#v %#v`, res.StatusCode, actual, http.StatusServiceUnavailable, expected) } } runtime-0.21.0/middleware/denco/util.go000066400000000000000000000003771413666762700200170ustar00rootroot00000000000000package 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 } runtime-0.21.0/middleware/denco/util_test.go000066400000000000000000000013271413666762700210520ustar00rootroot00000000000000package denco_test import ( "reflect" "testing" "github.com/go-openapi/runtime/middleware/denco" ) func TestNextSeparator(t *testing.T) { for _, testcase := range []struct { path string start int expected interface{} }{ {"/path/to/route", 0, 0}, {"/path/to/route", 1, 5}, {"/path/to/route", 9, 14}, {"/path.html", 1, 10}, {"/foo/bar.html", 1, 4}, {"/foo/bar.html/baz.png", 5, 13}, {"/foo/bar.html/baz.png", 14, 21}, {"path#", 0, 4}, } { actual := denco.NextSeparator(testcase.path, testcase.start) expected := testcase.expected if !reflect.DeepEqual(actual, expected) { t.Errorf("path = %q, start = %v expect %v, but %v", testcase.path, testcase.start, expected, actual) } } } runtime-0.21.0/middleware/doc.go000066400000000000000000000036441413666762700165170ustar00rootroot00000000000000// 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 runtime-0.21.0/middleware/go18.go000066400000000000000000000002111413666762700165130ustar00rootroot00000000000000// +build go1.8 package middleware import "net/url" func pathUnescape(path string) (string, error) { return url.PathUnescape(path) } runtime-0.21.0/middleware/header/000077500000000000000000000000001413666762700166445ustar00rootroot00000000000000runtime-0.21.0/middleware/header/header.go000066400000000000000000000156061413666762700204330ustar00rootroot00000000000000// 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 = // CHAR = // CTL = // CR = // LF = // SP = // HT = // <"> = // CRLF = CR LF // LWS = [CRLF] 1*( SP | HT ) // TEXT = // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT // token = 1* // qdtext = > 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 "", "" } runtime-0.21.0/middleware/negotiate.go000066400000000000000000000052361413666762700177300ustar00rootroot00000000000000// 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] } runtime-0.21.0/middleware/negotiate_test.go000066400000000000000000000076131413666762700207700ustar00rootroot00000000000000// 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. package middleware import ( "net/http" "testing" ) var negotiateContentEncodingTests = []struct { s string offers []string expect string }{ {"", []string{"identity", "gzip"}, "identity"}, {"*;q=0", []string{"identity", "gzip"}, ""}, {"gzip", []string{"identity", "gzip"}, "gzip"}, } func TestNegotiateContentEnoding(t *testing.T) { for _, tt := range negotiateContentEncodingTests { r := &http.Request{Header: http.Header{"Accept-Encoding": {tt.s}}} actual := NegotiateContentEncoding(r, tt.offers) if actual != tt.expect { t.Errorf("NegotiateContentEncoding(%q, %#v)=%q, want %q", tt.s, tt.offers, actual, tt.expect) } } } var negotiateContentTypeTests = []struct { s string offers []string defaultOffer string expect string }{ {"text/html, */*;q=0", []string{"x/y"}, "", ""}, {"text/html, */*", []string{"x/y"}, "", "x/y"}, {"text/html, image/png", []string{"text/html", "image/png"}, "", "text/html"}, {"text/html, image/png", []string{"image/png", "text/html"}, "", "image/png"}, {"text/html, image/png; q=0.5", []string{"image/png"}, "", "image/png"}, {"text/html, image/png; q=0.5", []string{"text/html"}, "", "text/html"}, {"text/html, image/png; q=0.5", []string{"foo/bar"}, "", ""}, {"text/html, image/png; q=0.5", []string{"image/png", "text/html"}, "", "text/html"}, {"text/html, image/png; q=0.5", []string{"text/html", "image/png"}, "", "text/html"}, {"text/html;q=0.5, image/png", []string{"image/png"}, "", "image/png"}, {"text/html;q=0.5, image/png", []string{"text/html"}, "", "text/html"}, {"text/html;q=0.5, image/png", []string{"image/png", "text/html"}, "", "image/png"}, {"text/html;q=0.5, image/png", []string{"text/html", "image/png"}, "", "image/png"}, {"text/html;q=0.5, image/png", []string{"text/html", "image/png"}, "", "image/png"}, {"image/png, image/*;q=0.5", []string{"image/jpg", "image/png"}, "", "image/png"}, {"image/png, image/*;q=0.5", []string{"image/jpg"}, "", "image/jpg"}, {"image/png, image/*;q=0.5", []string{"image/jpg", "image/gif"}, "", "image/jpg"}, {"image/png, image/*", []string{"image/jpg", "image/gif"}, "", "image/jpg"}, {"image/png, image/*", []string{"image/gif", "image/jpg"}, "", "image/gif"}, {"image/png, image/*", []string{"image/gif", "image/png"}, "", "image/png"}, {"image/png, image/*", []string{"image/png", "image/gif"}, "", "image/png"}, {"application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,text/plain;version=0.0.4;q=0.3", []string{"text/plain"}, "", "text/plain"}, {"application/json", []string{"application/json; charset=utf-8", "image/png"}, "", "application/json; charset=utf-8"}, {"application/json; charset=utf-8", []string{"application/json; charset=utf-8", "image/png"}, "", "application/json; charset=utf-8"}, {"application/json", []string{"application/vnd.cia.v1+json"}, "", ""}, // Default header of java clients {"text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2", []string{"application/json"}, "", "application/json"}, } func TestNegotiateContentType(t *testing.T) { for _, tt := range negotiateContentTypeTests { r := &http.Request{Header: http.Header{"Accept": {tt.s}}} actual := NegotiateContentType(r, tt.offers, tt.defaultOffer) if actual != tt.expect { t.Errorf("NegotiateContentType(%q, %#v, %q)=%q, want %q", tt.s, tt.offers, tt.defaultOffer, actual, tt.expect) } } } func TestNegotiateContentTypeNoAcceptHeader(t *testing.T) { r := &http.Request{Header: http.Header{}} offers := []string{"application/json", "text/xml"} actual := NegotiateContentType(r, offers, "") if actual != "application/json" { t.Errorf("NegotiateContentType(empty, %#v, empty)=%q, want %q", offers, actual, "application/json") } } runtime-0.21.0/middleware/not_implemented.go000066400000000000000000000033571413666762700211360ustar00rootroot00000000000000// 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, } } runtime-0.21.0/middleware/not_implemented_test.go000066400000000000000000000007651413666762700221750ustar00rootroot00000000000000package middleware import ( "net/http" "net/http/httptest" "testing" "github.com/go-openapi/runtime" "github.com/stretchr/testify/require" ) func TestErrorResponder(t *testing.T) { resp := Error(http.StatusBadRequest, map[string]string{"message": "this is the error body"}) rec := httptest.NewRecorder() resp.WriteResponse(rec, runtime.JSONProducer()) require.Equal(t, http.StatusBadRequest, rec.Code) require.Equal(t, "{\"message\":\"this is the error body\"}\n", rec.Body.String()) } runtime-0.21.0/middleware/operation.go000066400000000000000000000017651413666762700177540ustar00rootroot00000000000000// 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) }) } runtime-0.21.0/middleware/operation_test.go000066400000000000000000000042671413666762700210130ustar00rootroot00000000000000// 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" "net/http/httptest" "testing" "github.com/go-openapi/errors" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/internal/testing/petstore" ) func TestOperationExecutor(t *testing.T) { spec, api := petstore.NewAPI(t) api.RegisterOperation("get", "/pets", runtime.OperationHandlerFunc(func(params interface{}) (interface{}, error) { return []interface{}{ map[string]interface{}{"id": 1, "name": "a dog"}, }, nil })) context := NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) mw := NewOperationExecutor(context) recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/api/pets", nil) request.Header.Add("Accept", "application/json") request.SetBasicAuth("admin", "admin") mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) assert.Equal(t, `[{"id":1,"name":"a dog"}]`+"\n", recorder.Body.String()) spec, api = petstore.NewAPI(t) api.RegisterOperation("get", "/pets", runtime.OperationHandlerFunc(func(params interface{}) (interface{}, error) { return nil, errors.New(422, "expected") })) context = NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) mw = NewOperationExecutor(context) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/api/pets", nil) request.Header.Add("Accept", "application/json") request.SetBasicAuth("admin", "admin") mw.ServeHTTP(recorder, request) assert.Equal(t, 422, recorder.Code) assert.Equal(t, `{"code":422,"message":"expected"}`, recorder.Body.String()) } runtime-0.21.0/middleware/parameter.go000066400000000000000000000301031413666762700177200ustar00rootroot00000000000000// 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 { return errors.NewParseError(p.Name, p.parameter.In, "", ffErr) } 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 } runtime-0.21.0/middleware/parameter_test.go000066400000000000000000000267511413666762700207750ustar00rootroot00000000000000// 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 ( "math" "net/url" "reflect" "strconv" "testing" "github.com/go-openapi/errors" "github.com/go-openapi/spec" "github.com/go-openapi/strfmt" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" ) // type email struct { // Address string // } type paramFactory func(string) *spec.Parameter var paramFactories = []paramFactory{ spec.QueryParam, spec.HeaderParam, spec.PathParam, spec.FormDataParam, } func np(param *spec.Parameter) *untypedParamBinder { return newUntypedParamBinder(*param, new(spec.Swagger), strfmt.Default) } var stringItems = new(spec.Items) func init() { stringItems.Type = "string" } func testCollectionFormat(t *testing.T, param *spec.Parameter, valid bool) { binder := &untypedParamBinder{ parameter: param, } _, _, _, err := binder.readValue(runtime.Values(nil), reflect.ValueOf(nil)) if valid { assert.NoError(t, err) } else { assert.Error(t, err) assert.Equal(t, errors.InvalidCollectionFormat(param.Name, param.In, param.CollectionFormat), err) } } func requiredError(param *spec.Parameter, data interface{}) *errors.Validation { return errors.Required(param.Name, param.In, data) } func validateRequiredTest(t *testing.T, param *spec.Parameter, value reflect.Value) { binder := np(param) err := binder.bindValue([]string{}, true, value) assert.Error(t, err) assert.NotNil(t, param) assert.EqualError(t, requiredError(param, value.Interface()), err.Error()) err = binder.bindValue([]string{""}, true, value) if assert.Error(t, err) { assert.EqualError(t, requiredError(param, value.Interface()), err.Error()) } // should be impossible data, but let's go with it err = binder.bindValue([]string{"a"}, false, value) assert.Error(t, err) assert.EqualError(t, requiredError(param, value.Interface()), err.Error()) err = binder.bindValue([]string{""}, false, value) assert.Error(t, err) assert.EqualError(t, requiredError(param, value.Interface()), err.Error()) } func validateRequiredAllowEmptyTest(t *testing.T, param *spec.Parameter, value reflect.Value) { param.AllowEmptyValue = true binder := np(param) err := binder.bindValue([]string{}, true, value) assert.NoError(t, err) if assert.NotNil(t, param) { err = binder.bindValue([]string{""}, true, value) assert.NoError(t, err) err = binder.bindValue([]string{"1"}, false, value) assert.Error(t, err) assert.EqualError(t, requiredError(param, value.Interface()), err.Error()) err = binder.bindValue([]string{""}, false, value) assert.Error(t, err) assert.EqualError(t, requiredError(param, value.Interface()), err.Error()) } } func TestRequiredValidation(t *testing.T) { strParam := spec.QueryParam("name").Typed("string", "").AsRequired() validateRequiredTest(t, strParam, reflect.ValueOf("")) validateRequiredAllowEmptyTest(t, strParam, reflect.ValueOf("")) intParam := spec.QueryParam("id").Typed("integer", "int32").AsRequired() validateRequiredTest(t, intParam, reflect.ValueOf(int32(0))) validateRequiredAllowEmptyTest(t, intParam, reflect.ValueOf(int32(0))) longParam := spec.QueryParam("id").Typed("integer", "int64").AsRequired() validateRequiredTest(t, longParam, reflect.ValueOf(int64(0))) validateRequiredAllowEmptyTest(t, longParam, reflect.ValueOf(int64(0))) floatParam := spec.QueryParam("score").Typed("number", "float").AsRequired() validateRequiredTest(t, floatParam, reflect.ValueOf(float32(0))) validateRequiredAllowEmptyTest(t, floatParam, reflect.ValueOf(float32(0))) doubleParam := spec.QueryParam("score").Typed("number", "double").AsRequired() validateRequiredTest(t, doubleParam, reflect.ValueOf(float64(0))) validateRequiredAllowEmptyTest(t, doubleParam, reflect.ValueOf(float64(0))) dateTimeParam := spec.QueryParam("registered").Typed("string", "date-time").AsRequired() validateRequiredTest(t, dateTimeParam, reflect.ValueOf(strfmt.DateTime{})) // validateRequiredAllowEmptyTest(t, dateTimeParam, reflect.ValueOf(strfmt.DateTime{})) dateParam := spec.QueryParam("registered").Typed("string", "date").AsRequired() validateRequiredTest(t, dateParam, reflect.ValueOf(strfmt.Date{})) // validateRequiredAllowEmptyTest(t, dateParam, reflect.ValueOf(strfmt.DateTime{})) sliceParam := spec.QueryParam("tags").CollectionOf(stringItems, "").AsRequired() validateRequiredTest(t, sliceParam, reflect.MakeSlice(reflect.TypeOf([]string{}), 0, 0)) validateRequiredAllowEmptyTest(t, sliceParam, reflect.MakeSlice(reflect.TypeOf([]string{}), 0, 0)) } func TestInvalidCollectionFormat(t *testing.T) { validCf1 := spec.QueryParam("validFmt").CollectionOf(stringItems, "multi") validCf2 := spec.FormDataParam("validFmt2").CollectionOf(stringItems, "multi") invalidCf1 := spec.HeaderParam("invalidHdr").CollectionOf(stringItems, "multi") invalidCf2 := spec.PathParam("invalidPath").CollectionOf(stringItems, "multi") testCollectionFormat(t, validCf1, true) testCollectionFormat(t, validCf2, true) testCollectionFormat(t, invalidCf1, false) testCollectionFormat(t, invalidCf2, false) } func invalidTypeError(param *spec.Parameter, data interface{}) *errors.Validation { tpe := param.Type if param.Format != "" { tpe = param.Format } return errors.InvalidType(param.Name, param.In, tpe, data) } func TestTypeValidation(t *testing.T) { for _, newParam := range paramFactories { intParam := newParam("badInt").Typed("integer", "int32") value := reflect.ValueOf(int32(0)) binder := np(intParam) err := binder.bindValue([]string{"yada"}, true, value) // fails for invalid string assert.Error(t, err) assert.Equal(t, invalidTypeError(intParam, "yada"), err) // fails for overflow val := int64(math.MaxInt32) str := strconv.FormatInt(val, 10) + "0" v := int32(0) value = reflect.ValueOf(&v).Elem() binder = np(intParam) err = binder.bindValue([]string{str}, true, value) assert.Error(t, err) assert.Equal(t, invalidTypeError(intParam, str), err) longParam := newParam("badLong").Typed("integer", "int64") value = reflect.ValueOf(int64(0)) binder = np(longParam) err = binder.bindValue([]string{"yada"}, true, value) // fails for invalid string assert.Error(t, err) assert.Equal(t, invalidTypeError(longParam, "yada"), err) // fails for overflow str2 := strconv.FormatInt(math.MaxInt64, 10) + "0" v2 := int64(0) vv2 := reflect.ValueOf(&v2).Elem() binder = np(longParam) err = binder.bindValue([]string{str2}, true, vv2) assert.Error(t, err) assert.Equal(t, invalidTypeError(longParam, str2), err) floatParam := newParam("badFloat").Typed("number", "float") value = reflect.ValueOf(float64(0)) binder = np(floatParam) err = binder.bindValue([]string{"yada"}, true, value) // fails for invalid string assert.Error(t, err) assert.Equal(t, invalidTypeError(floatParam, "yada"), err) // fails for overflow str3 := strconv.FormatFloat(math.MaxFloat64, 'f', 5, 64) v3 := reflect.TypeOf(float32(0)) value = reflect.New(v3).Elem() binder = np(floatParam) err = binder.bindValue([]string{str3}, true, value) assert.Error(t, err) assert.Equal(t, invalidTypeError(floatParam, str3), err) doubleParam := newParam("badDouble").Typed("number", "double") value = reflect.ValueOf(float64(0)) binder = np(doubleParam) err = binder.bindValue([]string{"yada"}, true, value) // fails for invalid string assert.Error(t, err) assert.Equal(t, invalidTypeError(doubleParam, "yada"), err) // fails for overflow str4 := "9" + strconv.FormatFloat(math.MaxFloat64, 'f', 5, 64) v4 := reflect.TypeOf(float64(0)) value = reflect.New(v4).Elem() binder = np(doubleParam) err = binder.bindValue([]string{str4}, true, value) assert.Error(t, err) assert.Equal(t, invalidTypeError(doubleParam, str4), err) dateParam := newParam("badDate").Typed("string", "date") value = reflect.ValueOf(strfmt.Date{}) binder = np(dateParam) err = binder.bindValue([]string{"yada"}, true, value) // fails for invalid string assert.Error(t, err) assert.Equal(t, invalidTypeError(dateParam, "yada"), err) dateTimeParam := newParam("badDateTime").Typed("string", "date-time") value = reflect.ValueOf(strfmt.DateTime{}) binder = np(dateTimeParam) err = binder.bindValue([]string{"yada"}, true, value) // fails for invalid string assert.Error(t, err) assert.Equal(t, invalidTypeError(dateTimeParam, "yada"), err) byteParam := newParam("badByte").Typed("string", "byte") values := url.Values(map[string][]string{}) values.Add("badByte", "yaüda") v5 := []byte{} value = reflect.ValueOf(&v5).Elem() binder = np(byteParam) err = binder.bindValue([]string{"yaüda"}, true, value) // fails for invalid string assert.Error(t, err) assert.Equal(t, invalidTypeError(byteParam, "yaüda"), err) } } func TestTypeDetectionInvalidItems(t *testing.T) { withoutItems := spec.QueryParam("without").CollectionOf(nil, "") binder := &untypedParamBinder{ Name: "without", parameter: withoutItems, } assert.Nil(t, binder.Type()) items := new(spec.Items) items.Type = "array" withInvalidItems := spec.QueryParam("invalidItems").CollectionOf(items, "") binder = &untypedParamBinder{ Name: "invalidItems", parameter: withInvalidItems, } assert.Nil(t, binder.Type()) noType := spec.QueryParam("invalidType") noType.Type = "invalid" binder = &untypedParamBinder{ Name: "invalidType", parameter: noType, } assert.Nil(t, binder.Type()) } // type emailStrFmt struct { // name string // tpe reflect.Type // validator FormatValidator // } // // func (e *emailStrFmt) Name() string { // return e.name // } // // func (e *emailStrFmt) Type() reflect.Type { // return e.tpe // } // // func (e *emailStrFmt) Matches(str string) bool { // return e.validator(str) // } // // func TestTypeDetectionValid(t *testing.T) { // // emlFmt := &emailStrFmt{ // // name: "email", // // tpe: reflect.TypeOf(email{}), // // } // // formats := []StringFormat{emlFmt} // // expected := map[string]reflect.Type{ // "name": reflect.TypeOf(""), // "id": reflect.TypeOf(int64(0)), // "age": reflect.TypeOf(int32(0)), // "score": reflect.TypeOf(float32(0)), // "factor": reflect.TypeOf(float64(0)), // "friend": reflect.TypeOf(map[string]interface{}{}), // "X-Request-Id": reflect.TypeOf(int64(0)), // "tags": reflect.TypeOf([]string{}), // "confirmed": reflect.TypeOf(true), // "planned": reflect.TypeOf(swagger.Date{}), // "delivered": reflect.TypeOf(swagger.DateTime{}), // "email": reflect.TypeOf(email{}), // "picture": reflect.TypeOf([]byte{}), // "file": reflect.TypeOf(&swagger.File{}).Elem(), // } // // params := parametersForAllTypes("") // emailParam := spec.QueryParam("email").Typed("string", "email") // params["email"] = *emailParam // // fileParam := spec.FileParam("file") // params["file"] = *fileParam // // for _, v := range params { // binder := ¶mBinder{ // formats: formats, // name: v.Name, // parameter: &v, // } // assert.Equal(t, expected[v.Name], binder.Type(), "name: %s", v.Name) // } // } runtime-0.21.0/middleware/pre_go18.go000066400000000000000000000002131413666762700173630ustar00rootroot00000000000000// +build !go1.8 package middleware import "net/url" func pathUnescape(path string) (string, error) { return url.QueryUnescape(path) } runtime-0.21.0/middleware/rapidoc.go000066400000000000000000000042461413666762700173720ustar00rootroot00000000000000package 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 = ` {{ .Title }} ` ) runtime-0.21.0/middleware/rapidoc_test.go000066400000000000000000000012431413666762700204230ustar00rootroot00000000000000package middleware import ( "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) func TestRapiDocMiddleware(t *testing.T) { rapidoc := RapiDoc(RapiDocOpts{}, nil) req, _ := http.NewRequest("GET", "/docs", nil) recorder := httptest.NewRecorder() rapidoc.ServeHTTP(recorder, req) assert.Equal(t, 200, recorder.Code) assert.Equal(t, "text/html; charset=utf-8", recorder.Header().Get("Content-Type")) assert.Contains(t, recorder.Body.String(), "API documentation") assert.Contains(t, recorder.Body.String(), "") assert.Contains(t, recorder.Body.String(), rapidocLatest) } runtime-0.21.0/middleware/redoc.go000066400000000000000000000047131413666762700170440ustar00rootroot00000000000000package 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 = ` {{ .Title }} ` ) runtime-0.21.0/middleware/redoc_test.go000066400000000000000000000012171413666762700200770ustar00rootroot00000000000000package middleware import ( "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) func TestRedocMiddleware(t *testing.T) { redoc := Redoc(RedocOpts{}, nil) req, _ := http.NewRequest("GET", "/docs", nil) recorder := httptest.NewRecorder() redoc.ServeHTTP(recorder, req) assert.Equal(t, 200, recorder.Code) assert.Equal(t, "text/html; charset=utf-8", recorder.Header().Get("Content-Type")) assert.Contains(t, recorder.Body.String(), "API documentation") assert.Contains(t, recorder.Body.String(), "") assert.Contains(t, recorder.Body.String(), redocLatest) } runtime-0.21.0/middleware/request.go000066400000000000000000000060541413666762700174400ustar00rootroot00000000000000// 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 } runtime-0.21.0/middleware/request_test.go000066400000000000000000000370351413666762700205020ustar00rootroot00000000000000// 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 ( "bytes" "io" "io/ioutil" "mime/multipart" "net/http" "net/url" "strings" "testing" "time" "github.com/stretchr/testify/require" "github.com/go-openapi/spec" "github.com/go-openapi/strfmt" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" ) type stubConsumer struct { } func (s *stubConsumer) Consume(_ io.Reader, _ interface{}) error { return nil } type friend struct { Name string `json:"name"` Age int `json:"age"` } type jsonRequestParams struct { ID int64 // path Name string // query Friend friend // body RequestID int64 // header Tags []string // csv } type jsonRequestPtr struct { ID int64 // path Name string // query RequestID int64 // header Tags []string // csv Friend *friend } type jsonRequestSlice struct { ID int64 // path Name string // query RequestID int64 // header Tags []string // csv Friend []friend } func parametersForAllTypes(fmt string) map[string]spec.Parameter { if fmt == "" { fmt = "csv" } nameParam := spec.QueryParam("name").Typed("string", "") idParam := spec.PathParam("id").Typed("integer", "int64") ageParam := spec.QueryParam("age").Typed("integer", "int32") scoreParam := spec.QueryParam("score").Typed("number", "float") factorParam := spec.QueryParam("factor").Typed("number", "double") friendSchema := new(spec.Schema).Typed("object", "") friendParam := spec.BodyParam("friend", friendSchema) requestIDParam := spec.HeaderParam("X-Request-Id").Typed("integer", "int64") requestIDParam.Extensions = spec.Extensions(map[string]interface{}{}) requestIDParam.Extensions.Add("go-name", "RequestID") items := new(spec.Items) items.Type = "string" tagsParam := spec.QueryParam("tags").CollectionOf(items, fmt) confirmedParam := spec.QueryParam("confirmed").Typed("boolean", "") plannedParam := spec.QueryParam("planned").Typed("string", "date") deliveredParam := spec.QueryParam("delivered").Typed("string", "date-time") pictureParam := spec.QueryParam("picture").Typed("string", "byte") // base64 encoded during transport return map[string]spec.Parameter{ "ID": *idParam, "Name": *nameParam, "RequestID": *requestIDParam, "Friend": *friendParam, "Tags": *tagsParam, "Age": *ageParam, "Score": *scoreParam, "Factor": *factorParam, "Confirmed": *confirmedParam, "Planned": *plannedParam, "Delivered": *deliveredParam, "Picture": *pictureParam, } } func parametersForJSONRequestParams(fmt string) map[string]spec.Parameter { if fmt == "" { fmt = "csv" } nameParam := spec.QueryParam("name").Typed("string", "") idParam := spec.PathParam("id").Typed("integer", "int64") friendSchema := new(spec.Schema).Typed("object", "") friendParam := spec.BodyParam("friend", friendSchema) requestIDParam := spec.HeaderParam("X-Request-Id").Typed("integer", "int64") requestIDParam.Extensions = spec.Extensions(map[string]interface{}{}) requestIDParam.Extensions.Add("go-name", "RequestID") items := new(spec.Items) items.Type = "string" tagsParam := spec.QueryParam("tags").CollectionOf(items, fmt) return map[string]spec.Parameter{ "ID": *idParam, "Name": *nameParam, "RequestID": *requestIDParam, "Friend": *friendParam, "Tags": *tagsParam, } } func parametersForJSONRequestSliceParams(fmt string) map[string]spec.Parameter { if fmt == "" { fmt = "csv" } nameParam := spec.QueryParam("name").Typed("string", "") idParam := spec.PathParam("id").Typed("integer", "int64") friendSchema := new(spec.Schema).Typed("object", "") friendParam := spec.BodyParam("friend", spec.ArrayProperty(friendSchema)) requestIDParam := spec.HeaderParam("X-Request-Id").Typed("integer", "int64") requestIDParam.Extensions = spec.Extensions(map[string]interface{}{}) requestIDParam.Extensions.Add("go-name", "RequestID") items := new(spec.Items) items.Type = "string" tagsParam := spec.QueryParam("tags").CollectionOf(items, fmt) return map[string]spec.Parameter{ "ID": *idParam, "Name": *nameParam, "RequestID": *requestIDParam, "Friend": *friendParam, "Tags": *tagsParam, } } func TestRequestBindingDefaultValue(t *testing.T) { confirmed := true name := "thomas" friend := map[string]interface{}{"name": "toby", "age": float64(32)} id, age, score, factor := int64(7575), int32(348), float32(5.309), float64(37.403) requestID := 19394858 tags := []string{"one", "two", "three"} dt1 := time.Date(2014, 8, 9, 0, 0, 0, 0, time.UTC) planned := strfmt.Date(dt1) dt2 := time.Date(2014, 10, 12, 8, 5, 5, 0, time.UTC) delivered := strfmt.DateTime(dt2) uri, _ := url.Parse("http://localhost:8002/hello") defaults := map[string]interface{}{ "id": id, "age": age, "score": score, "factor": factor, "name": name, "friend": friend, "X-Request-Id": requestID, "tags": tags, "confirmed": confirmed, "planned": planned, "delivered": delivered, "picture": []byte("hello"), } op2 := parametersForAllTypes("") op3 := make(map[string]spec.Parameter) for k, p := range op2 { p.Default = defaults[p.Name] op3[k] = p } req, _ := http.NewRequest("POST", uri.String(), bytes.NewBuffer(nil)) req.Header.Set("Content-Type", "application/json") binder := NewUntypedRequestBinder(op3, new(spec.Swagger), strfmt.Default) data := make(map[string]interface{}) err := binder.Bind(req, RouteParams(nil), runtime.JSONConsumer(), &data) assert.NoError(t, err) assert.Equal(t, defaults["id"], data["id"]) assert.Equal(t, name, data["name"]) assert.Equal(t, friend, data["friend"]) assert.EqualValues(t, requestID, data["X-Request-Id"]) assert.Equal(t, tags, data["tags"]) assert.Equal(t, planned, data["planned"]) assert.Equal(t, delivered, data["delivered"]) assert.Equal(t, confirmed, data["confirmed"]) assert.Equal(t, age, data["age"]) assert.Equal(t, factor, data["factor"]) assert.Equal(t, score, data["score"]) assert.Equal(t, "hello", string(data["picture"].(strfmt.Base64))) } func TestRequestBindingForInvalid(t *testing.T) { invalidParam := spec.QueryParam("some") op1 := map[string]spec.Parameter{"Some": *invalidParam} binder := NewUntypedRequestBinder(op1, new(spec.Swagger), strfmt.Default) req, _ := http.NewRequest("GET", "http://localhost:8002/hello?name=the-name", nil) err := binder.Bind(req, nil, new(stubConsumer), new(jsonRequestParams)) assert.Error(t, err) op2 := parametersForJSONRequestParams("") binder = NewUntypedRequestBinder(op2, new(spec.Swagger), strfmt.Default) req, _ = http.NewRequest("POST", "http://localhost:8002/hello/1?name=the-name", bytes.NewBuffer([]byte(`{"name":"toby","age":32}`))) req.Header.Set("Content-Type", "application(") data := jsonRequestParams{} err = binder.Bind(req, RouteParams([]RouteParam{{"id", "1"}}), runtime.JSONConsumer(), &data) assert.Error(t, err) req, _ = http.NewRequest("POST", "http://localhost:8002/hello/1?name=the-name", bytes.NewBuffer([]byte(`{]`))) req.Header.Set("Content-Type", "application/json") data = jsonRequestParams{} err = binder.Bind(req, RouteParams([]RouteParam{{"id", "1"}}), runtime.JSONConsumer(), &data) assert.Error(t, err) invalidMultiParam := spec.HeaderParam("tags").CollectionOf(new(spec.Items), "multi") op3 := map[string]spec.Parameter{"Tags": *invalidMultiParam} binder = NewUntypedRequestBinder(op3, new(spec.Swagger), strfmt.Default) req, _ = http.NewRequest("POST", "http://localhost:8002/hello/1?name=the-name", bytes.NewBuffer([]byte(`{}`))) req.Header.Set("Content-Type", "application/json") data = jsonRequestParams{} err = binder.Bind(req, RouteParams([]RouteParam{{"id", "1"}}), runtime.JSONConsumer(), &data) assert.Error(t, err) invalidMultiParam = spec.PathParam("").CollectionOf(new(spec.Items), "multi") op4 := map[string]spec.Parameter{"Tags": *invalidMultiParam} binder = NewUntypedRequestBinder(op4, new(spec.Swagger), strfmt.Default) req, _ = http.NewRequest("POST", "http://localhost:8002/hello/1?name=the-name", bytes.NewBuffer([]byte(`{}`))) req.Header.Set("Content-Type", "application/json") data = jsonRequestParams{} err = binder.Bind(req, RouteParams([]RouteParam{{"id", "1"}}), runtime.JSONConsumer(), &data) assert.Error(t, err) invalidInParam := spec.HeaderParam("tags").Typed("string", "") invalidInParam.In = "invalid" op5 := map[string]spec.Parameter{"Tags": *invalidInParam} binder = NewUntypedRequestBinder(op5, new(spec.Swagger), strfmt.Default) req, _ = http.NewRequest("POST", "http://localhost:8002/hello/1?name=the-name", bytes.NewBuffer([]byte(`{}`))) req.Header.Set("Content-Type", "application/json") data = jsonRequestParams{} err = binder.Bind(req, RouteParams([]RouteParam{{"id", "1"}}), runtime.JSONConsumer(), &data) assert.Error(t, err) } func TestRequestBindingForValid(t *testing.T) { for _, fmt := range []string{"csv", "pipes", "tsv", "ssv", "multi"} { op1 := parametersForJSONRequestParams(fmt) binder := NewUntypedRequestBinder(op1, new(spec.Swagger), strfmt.Default) lval := []string{"one", "two", "three"} var queryString string var skipEscape bool switch fmt { case "multi": skipEscape = true queryString = strings.Join(lval, "&tags=") case "ssv": queryString = strings.Join(lval, " ") case "pipes": queryString = strings.Join(lval, "|") case "tsv": queryString = strings.Join(lval, "\t") default: queryString = strings.Join(lval, ",") } if !skipEscape { queryString = url.QueryEscape(queryString) } urlStr := "http://localhost:8002/hello/1?name=the-name&tags=" + queryString req, err := http.NewRequest("POST", urlStr, bytes.NewBuffer([]byte(`{"name":"toby","age":32}`))) require.NoError(t, err) req.Header.Set("Content-Type", "application/json;charset=utf-8") req.Header.Set("X-Request-Id", "1325959595") data := jsonRequestParams{} err = binder.Bind(req, RouteParams([]RouteParam{{"id", "1"}}), runtime.JSONConsumer(), &data) expected := jsonRequestParams{ ID: 1, Name: "the-name", Friend: friend{"toby", 32}, RequestID: 1325959595, Tags: []string{"one", "two", "three"}, } assert.NoError(t, err) assert.Equal(t, expected, data) } op1 := parametersForJSONRequestParams("") binder := NewUntypedRequestBinder(op1, new(spec.Swagger), strfmt.Default) urlStr := "http://localhost:8002/hello/1?name=the-name&tags=one,two,three" req, _ := http.NewRequest("POST", urlStr, bytes.NewBuffer([]byte(`{"name":"toby","age":32}`))) req.Header.Set("Content-Type", "application/json;charset=utf-8") req.Header.Set("X-Request-Id", "1325959595") data2 := jsonRequestPtr{} err := binder.Bind(req, []RouteParam{{"id", "1"}}, runtime.JSONConsumer(), &data2) expected2 := jsonRequestPtr{ Friend: &friend{"toby", 32}, Tags: []string{"one", "two", "three"}, } assert.NoError(t, err) if data2.Friend == nil { t.Fatal("friend is nil") } assert.Equal(t, *expected2.Friend, *data2.Friend) assert.Equal(t, expected2.Tags, data2.Tags) req, _ = http.NewRequest("POST", urlStr, bytes.NewBuffer([]byte(`[{"name":"toby","age":32}]`))) req.Header.Set("Content-Type", "application/json;charset=utf-8") req.Header.Set("X-Request-Id", "1325959595") op2 := parametersForJSONRequestSliceParams("") binder = NewUntypedRequestBinder(op2, new(spec.Swagger), strfmt.Default) data3 := jsonRequestSlice{} err = binder.Bind(req, []RouteParam{{"id", "1"}}, runtime.JSONConsumer(), &data3) expected3 := jsonRequestSlice{ Friend: []friend{{"toby", 32}}, Tags: []string{"one", "two", "three"}, } assert.NoError(t, err) assert.Equal(t, expected3.Friend, data3.Friend) assert.Equal(t, expected3.Tags, data3.Tags) } type formRequest struct { Name string Age int } func parametersForFormUpload() map[string]spec.Parameter { nameParam := spec.FormDataParam("name").Typed("string", "") ageParam := spec.FormDataParam("age").Typed("integer", "int32") return map[string]spec.Parameter{"Name": *nameParam, "Age": *ageParam} } func TestFormUpload(t *testing.T) { params := parametersForFormUpload() binder := NewUntypedRequestBinder(params, new(spec.Swagger), strfmt.Default) urlStr := "http://localhost:8002/hello" req, _ := http.NewRequest("POST", urlStr, bytes.NewBufferString(`name=the-name&age=32`)) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") data := formRequest{} res := binder.Bind(req, nil, runtime.JSONConsumer(), &data) assert.NoError(t, res) assert.Equal(t, "the-name", data.Name) assert.Equal(t, 32, data.Age) req, _ = http.NewRequest("POST", urlStr, bytes.NewBufferString(`name=%3&age=32`)) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") data = formRequest{} assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) } type fileRequest struct { Name string // body File runtime.File // upload } func paramsForFileUpload() *UntypedRequestBinder { nameParam := spec.FormDataParam("name").Typed("string", "") fileParam := spec.FileParam("file") params := map[string]spec.Parameter{"Name": *nameParam, "File": *fileParam} return NewUntypedRequestBinder(params, new(spec.Swagger), strfmt.Default) } func TestBindingFileUpload(t *testing.T) { binder := paramsForFileUpload() body := bytes.NewBuffer(nil) writer := multipart.NewWriter(body) part, err := writer.CreateFormFile("file", "plain-jane.txt") assert.NoError(t, err) _, _ = part.Write([]byte("the file contents")) _ = writer.WriteField("name", "the-name") assert.NoError(t, writer.Close()) urlStr := "http://localhost:8002/hello" req, _ := http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", writer.FormDataContentType()) data := fileRequest{} assert.NoError(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) assert.Equal(t, "the-name", data.Name) assert.NotNil(t, data.File) assert.NotNil(t, data.File.Header) assert.Equal(t, "plain-jane.txt", data.File.Header.Filename) bb, err := ioutil.ReadAll(data.File.Data) assert.NoError(t, err) assert.Equal(t, []byte("the file contents"), bb) req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", "application/json") data = fileRequest{} assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", "application(") data = fileRequest{} assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) body = bytes.NewBuffer(nil) writer = multipart.NewWriter(body) part, err = writer.CreateFormFile("bad-name", "plain-jane.txt") assert.NoError(t, err) _, _ = part.Write([]byte("the file contents")) _ = writer.WriteField("name", "the-name") assert.NoError(t, writer.Close()) req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", writer.FormDataContentType()) data = fileRequest{} assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", writer.FormDataContentType()) _, _ = req.MultipartReader() data = fileRequest{} assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) } runtime-0.21.0/middleware/route_authenticator_test.go000066400000000000000000000151351413666762700230770ustar00rootroot00000000000000package middleware import ( "errors" "net/http" "testing" "github.com/stretchr/testify/require" "github.com/go-openapi/runtime" ) type countAuthenticator struct { count int applies bool principal interface{} err error } func (c *countAuthenticator) Authenticate(params interface{}) (bool, interface{}, error) { c.count++ return c.applies, c.principal, c.err } func newCountAuthenticator(applies bool, principal interface{}, err error) *countAuthenticator { return &countAuthenticator{applies: applies, principal: principal, err: err} } var ( successAuth = runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) { return true, "the user", nil }) failAuth = runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) { return true, nil, errors.New("unauthenticated") }) noApplyAuth = runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) { return false, nil, nil }) ) func TestAuthenticateSingle(t *testing.T) { ra := RouteAuthenticator{ Authenticator: map[string]runtime.Authenticator{ "auth1": successAuth, }, Schemes: []string{"auth1"}, Scopes: map[string][]string{"auth1": nil}, } ras := RouteAuthenticators([]RouteAuthenticator{ra}) require.False(t, ras.AllowsAnonymous()) req, _ := http.NewRequest("GET", "/", nil) route := &MatchedRoute{} ok, prin, err := ras.Authenticate(req, route) require.NoError(t, err) require.True(t, ok) require.Equal(t, "the user", prin) require.Equal(t, ra, *route.Authenticator) } func TestAuthenticateLogicalOr(t *testing.T) { ra1 := RouteAuthenticator{ Authenticator: map[string]runtime.Authenticator{ "auth1": noApplyAuth, }, Schemes: []string{"auth1"}, Scopes: map[string][]string{"auth1": nil}, } ra2 := RouteAuthenticator{ Authenticator: map[string]runtime.Authenticator{ "auth2": successAuth, }, Schemes: []string{"auth2"}, Scopes: map[string][]string{"auth2": nil}, } // right side matches ras := RouteAuthenticators([]RouteAuthenticator{ra1, ra2}) require.False(t, ras.AllowsAnonymous()) req, _ := http.NewRequest("GET", "/", nil) route := &MatchedRoute{} ok, prin, err := ras.Authenticate(req, route) require.NoError(t, err) require.True(t, ok) require.Equal(t, "the user", prin) require.Equal(t, ra2, *route.Authenticator) // left side matches ras = RouteAuthenticators([]RouteAuthenticator{ra2, ra1}) require.False(t, ras.AllowsAnonymous()) req, _ = http.NewRequest("GET", "/", nil) route = &MatchedRoute{} ok, prin, err = ras.Authenticate(req, route) require.NoError(t, err) require.True(t, ok) require.Equal(t, "the user", prin) require.Equal(t, ra2, *route.Authenticator) } func TestAuthenticateLogicalAnd(t *testing.T) { ra1 := RouteAuthenticator{ Authenticator: map[string]runtime.Authenticator{ "auth1": noApplyAuth, }, Schemes: []string{"auth1"}, Scopes: map[string][]string{"auth1": nil}, } auther := newCountAuthenticator(true, "the user", nil) ra2 := RouteAuthenticator{ Authenticator: map[string]runtime.Authenticator{ "auth2": auther, "auth3": auther, }, Schemes: []string{"auth2", "auth3"}, Scopes: map[string][]string{"auth2": nil}, } ras := RouteAuthenticators([]RouteAuthenticator{ra1, ra2}) require.False(t, ras.AllowsAnonymous()) req, _ := http.NewRequest("GET", "/", nil) route := &MatchedRoute{} ok, prin, err := ras.Authenticate(req, route) require.NoError(t, err) require.True(t, ok) require.Equal(t, "the user", prin) require.Equal(t, ra2, *route.Authenticator) require.Equal(t, 2, auther.count) var count int successA := runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) { count++ return true, "the user", nil }) failA := runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) { count++ return true, nil, errors.New("unauthenticated") }) ra3 := RouteAuthenticator{ Authenticator: map[string]runtime.Authenticator{ "auth2": successA, "auth3": failA, "auth4": successA, }, Schemes: []string{"auth2", "auth3", "auth4"}, Scopes: map[string][]string{"auth2": nil}, } ras = RouteAuthenticators([]RouteAuthenticator{ra1, ra3}) require.False(t, ras.AllowsAnonymous()) req, _ = http.NewRequest("GET", "/", nil) route = &MatchedRoute{} ok, prin, err = ras.Authenticate(req, route) require.Error(t, err) require.True(t, ok) require.Nil(t, prin) require.Equal(t, ra3, *route.Authenticator) require.Equal(t, 2, count) ra4 := RouteAuthenticator{ Authenticator: map[string]runtime.Authenticator{ "auth2": successA, "auth3": successA, "auth4": failA, }, Schemes: []string{"auth2", "auth3", "auth4"}, Scopes: map[string][]string{"auth2": nil}, } ras = RouteAuthenticators([]RouteAuthenticator{ra1, ra4}) require.False(t, ras.AllowsAnonymous()) req, _ = http.NewRequest("GET", "/", nil) route = &MatchedRoute{} ok, prin, err = ras.Authenticate(req, route) require.Error(t, err) require.True(t, ok) require.Nil(t, prin) require.Equal(t, ra4, *route.Authenticator) require.Equal(t, 5, count) } func TestAuthenticateOptional(t *testing.T) { ra1 := RouteAuthenticator{ Authenticator: map[string]runtime.Authenticator{ "auth1": noApplyAuth, }, Schemes: []string{"auth1"}, Scopes: map[string][]string{"auth1": nil}, } ra2 := RouteAuthenticator{ allowAnonymous: true, Schemes: []string{""}, Scopes: map[string][]string{"": {}}, } ra3 := RouteAuthenticator{ Authenticator: map[string]runtime.Authenticator{ "auth2": noApplyAuth, }, Schemes: []string{"auth2"}, Scopes: map[string][]string{"auth2": nil}, } ras := RouteAuthenticators([]RouteAuthenticator{ra1, ra2, ra3}) require.True(t, ras.AllowsAnonymous()) req, _ := http.NewRequest("GET", "/", nil) route := &MatchedRoute{} ok, prin, err := ras.Authenticate(req, route) require.NoError(t, err) require.True(t, ok) require.Nil(t, prin) require.Equal(t, ra2, *route.Authenticator) ra4 := RouteAuthenticator{ Authenticator: map[string]runtime.Authenticator{ "auth1": noApplyAuth, }, Schemes: []string{"auth1"}, Scopes: map[string][]string{"auth1": nil}, } ra5 := RouteAuthenticator{ allowAnonymous: true, } ra6 := RouteAuthenticator{ Authenticator: map[string]runtime.Authenticator{ "auth2": failAuth, }, Schemes: []string{"auth2"}, Scopes: map[string][]string{"auth2": nil}, } ras = RouteAuthenticators([]RouteAuthenticator{ra4, ra5, ra6}) require.True(t, ras.AllowsAnonymous()) req, _ = http.NewRequest("GET", "/", nil) route = &MatchedRoute{} ok, prin, err = ras.Authenticate(req, route) require.Error(t, err) require.True(t, ok) require.Nil(t, prin) require.Equal(t, ra6, *route.Authenticator) } runtime-0.21.0/middleware/route_param_test.go000066400000000000000000000017721413666762700213270ustar00rootroot00000000000000// 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 ( "testing" "github.com/stretchr/testify/assert" ) func TestRouteParams(t *testing.T) { coll1 := RouteParams([]RouteParam{ {"blah", "foo"}, {"abc", "bar"}, {"ccc", "efg"}, }) v := coll1.Get("blah") assert.Equal(t, v, "foo") v2 := coll1.Get("abc") assert.Equal(t, v2, "bar") v3 := coll1.Get("ccc") assert.Equal(t, v3, "efg") v4 := coll1.Get("ydkdk") assert.Empty(t, v4) } runtime-0.21.0/middleware/router.go000066400000000000000000000346731413666762700173000ustar00rootroot00000000000000// 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, } } runtime-0.21.0/middleware/router_test.go000066400000000000000000000172541413666762700203330ustar00rootroot00000000000000// 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" "net/http/httptest" "sort" "strings" "testing" "github.com/go-openapi/analysis" "github.com/go-openapi/loads" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime/internal/testing/petstore" "github.com/go-openapi/runtime/middleware/untyped" ) func terminator(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusOK) } func TestRouterMiddleware(t *testing.T) { spec, api := petstore.NewAPI(t) context := NewContext(spec, api, nil) mw := NewRouter(context, http.HandlerFunc(terminator)) recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/api/pets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) recorder = httptest.NewRecorder() request, _ = http.NewRequest("DELETE", "/api/pets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusMethodNotAllowed, recorder.Code) methods := strings.Split(recorder.Header().Get("Allow"), ",") sort.Strings(methods) assert.Equal(t, "GET,POST", strings.Join(methods, ",")) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/nopets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusNotFound, recorder.Code) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/pets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusNotFound, recorder.Code) spec, api = petstore.NewRootAPI(t) context = NewContext(spec, api, nil) mw = NewRouter(context, http.HandlerFunc(terminator)) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/pets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) recorder = httptest.NewRecorder() request, _ = http.NewRequest("DELETE", "/pets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusMethodNotAllowed, recorder.Code) methods = strings.Split(recorder.Header().Get("Allow"), ",") sort.Strings(methods) assert.Equal(t, "GET,POST", strings.Join(methods, ",")) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/nopets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusNotFound, recorder.Code) } func TestRouterBuilder(t *testing.T) { spec, api := petstore.NewAPI(t) analyzed := analysis.New(spec.Spec()) assert.Len(t, analyzed.RequiredConsumes(), 3) assert.Len(t, analyzed.RequiredProduces(), 5) assert.Len(t, analyzed.OperationIDs(), 4) // context := NewContext(spec, api) builder := petAPIRouterBuilder(spec, api, analyzed) getRecords := builder.records["GET"] postRecords := builder.records["POST"] deleteRecords := builder.records["DELETE"] assert.Len(t, getRecords, 2) assert.Len(t, postRecords, 1) assert.Len(t, deleteRecords, 1) assert.Empty(t, builder.records["PATCH"]) assert.Empty(t, builder.records["OPTIONS"]) assert.Empty(t, builder.records["HEAD"]) assert.Empty(t, builder.records["PUT"]) rec := postRecords[0] assert.Equal(t, rec.Key, "/pets") val := rec.Value.(*routeEntry) assert.Len(t, val.Consumers, 2) assert.Len(t, val.Producers, 2) assert.Len(t, val.Consumes, 2) assert.Len(t, val.Produces, 2) assert.Contains(t, val.Consumers, "application/json") assert.Contains(t, val.Producers, "application/x-yaml") assert.Contains(t, val.Consumes, "application/json") assert.Contains(t, val.Produces, "application/x-yaml") assert.Len(t, val.Parameters, 1) recG := getRecords[0] assert.Equal(t, recG.Key, "/pets") valG := recG.Value.(*routeEntry) assert.Len(t, valG.Consumers, 2) assert.Len(t, valG.Producers, 4) assert.Len(t, valG.Consumes, 2) assert.Len(t, valG.Produces, 4) assert.Len(t, valG.Parameters, 2) } func TestRouterCanonicalBasePath(t *testing.T) { spec, api := petstore.NewAPI(t) spec.Spec().BasePath = "/api///" context := NewContext(spec, api, nil) mw := NewRouter(context, http.HandlerFunc(terminator)) recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/api/pets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) } func TestRouter_EscapedPath(t *testing.T) { spec, api := petstore.NewAPI(t) spec.Spec().BasePath = "/api/" context := NewContext(spec, api, nil) mw := NewRouter(context, http.HandlerFunc(terminator)) recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/api/pets/123", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/api/pets/abc%2Fdef", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) ri, _, _ := context.RouteInfo(request) if assert.NotNil(t, ri) { if assert.NotNil(t, ri.Params) { assert.Equal(t, "abc/def", ri.Params.Get("id")) } } } func TestRouterStruct(t *testing.T) { spec, api := petstore.NewAPI(t) router := DefaultRouter(spec, newRoutableUntypedAPI(spec, api, new(Context))) methods := router.OtherMethods("post", "/api/pets/{id}") assert.Len(t, methods, 2) entry, ok := router.Lookup("delete", "/api/pets/{id}") assert.True(t, ok) assert.NotNil(t, entry) assert.Len(t, entry.Params, 1) assert.Equal(t, "id", entry.Params[0].Name) _, ok = router.Lookup("delete", "/pets") assert.False(t, ok) _, ok = router.Lookup("post", "/no-pets") assert.False(t, ok) } func petAPIRouterBuilder(spec *loads.Document, api *untyped.API, analyzed *analysis.Spec) *defaultRouteBuilder { builder := newDefaultRouteBuilder(spec, newRoutableUntypedAPI(spec, api, new(Context))) builder.AddRoute("GET", "/pets", analyzed.AllPaths()["/pets"].Get) builder.AddRoute("POST", "/pets", analyzed.AllPaths()["/pets"].Post) builder.AddRoute("DELETE", "/pets/{id}", analyzed.AllPaths()["/pets/{id}"].Delete) builder.AddRoute("GET", "/pets/{id}", analyzed.AllPaths()["/pets/{id}"].Get) return builder } func TestPathConverter(t *testing.T) { cases := []struct { swagger string denco string }{ {"/", "/"}, {"/something", "/something"}, {"/{id}", "/:id"}, {"/{id}/something/{anotherId}", "/:id/something/:anotherId"}, {"/{petid}", "/:petid"}, {"/{pet_id}", "/:pet_id"}, {"/{petId}", "/:petId"}, {"/{pet-id}", "/:pet-id"}, // composit parameters tests {"/p_{pet_id}", "/p_:pet_id"}, {"/p_{petId}.{petSubId}", "/p_:petId"}, } for _, tc := range cases { actual := pathConverter.ReplaceAllString(tc.swagger, ":$1") assert.Equal(t, tc.denco, actual, "expected swagger path %s to match %s but got %s", tc.swagger, tc.denco, actual) } } func TestExtractCompositParameters(t *testing.T) { // name is the composite parameter's name, value is the value of this composit parameter, pattern is the pattern to be matched cases := []struct { name string value string pattern string names []string values []string }{ {name: "fragment", value: "gie", pattern: "e", names: []string{"fragment"}, values: []string{"gi"}}, {name: "fragment", value: "t.simpson", pattern: ".{subfragment}", names: []string{"fragment", "subfragment"}, values: []string{"t", "simpson"}}, } for _, tc := range cases { names, values := decodeCompositParams(tc.name, tc.value, tc.pattern, nil, nil) assert.EqualValues(t, tc.names, names) assert.EqualValues(t, tc.values, values) } } runtime-0.21.0/middleware/security.go000066400000000000000000000021111413666762700176050ustar00rootroot00000000000000// 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) }) } runtime-0.21.0/middleware/security_test.go000066400000000000000000000034051413666762700206530ustar00rootroot00000000000000// 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" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime/internal/testing/petstore" ) func TestSecurityMiddleware(t *testing.T) { spec, api := petstore.NewAPI(t) context := NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) mw := newSecureAPI(context, http.HandlerFunc(terminator)) recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/api/pets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, 401, recorder.Code) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/api/pets", nil) request.SetBasicAuth("admin", "wrong") mw.ServeHTTP(recorder, request) assert.Equal(t, 401, recorder.Code) assert.NotEmpty(t, recorder.Header().Get("WWW-Authenticate")) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/api/pets", nil) request.SetBasicAuth("admin", "admin") mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "//apipets/1", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) } runtime-0.21.0/middleware/spec.go000066400000000000000000000026221413666762700166770ustar00rootroot00000000000000// 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) }) } runtime-0.21.0/middleware/spec_test.go000066400000000000000000000035511413666762700177400ustar00rootroot00000000000000// 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" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/internal/testing/petstore" ) func TestServeSpecMiddleware(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) handler := Spec("", ctx.spec.Raw(), nil) // serves spec request, _ := http.NewRequest("GET", "/swagger.json", nil) request.Header.Add(runtime.HeaderContentType, runtime.JSONMime) recorder := httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) // returns 404 when no next handler request, _ = http.NewRequest("GET", "/api/pets", nil) request.Header.Add(runtime.HeaderContentType, runtime.JSONMime) recorder = httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, 404, recorder.Code) // forwards to next handler for other url handler = Spec("", ctx.spec.Raw(), http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusOK) })) request, _ = http.NewRequest("GET", "/api/pets", nil) request.Header.Add(runtime.HeaderContentType, runtime.JSONMime) recorder = httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) } runtime-0.21.0/middleware/string_conversion_test.go000066400000000000000000000241501413666762700225570ustar00rootroot00000000000000// 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 ( "errors" "reflect" "strings" "testing" "time" "github.com/go-openapi/spec" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/stretchr/testify/assert" ) var evaluatesAsTrue = []string{"true", "1", "yes", "ok", "y", "on", "selected", "checked", "t", "enabled"} type unmarshallerSlice []string func (u *unmarshallerSlice) UnmarshalText(data []byte) error { if len(data) == 0 { return errors.New("an error") } *u = strings.Split(string(data), ",") return nil } type SomeOperationParams struct { Name string ID int64 Confirmed bool Age int Visits int32 Count int16 Seq int8 UID uint64 UAge uint UVisits uint32 UCount uint16 USeq uint8 Score float32 Rate float64 Timestamp strfmt.DateTime Birthdate strfmt.Date LastFailure *strfmt.DateTime Unsupported struct{} Tags []string Prefs []int32 Categories unmarshallerSlice } func FloatParamTest(t *testing.T, fName, pName, format string, val reflect.Value, defVal, expectedDef interface{}, actual func() interface{}) { fld := val.FieldByName(pName) binder := &untypedParamBinder{ parameter: spec.QueryParam(pName).Typed("number", "double").WithDefault(defVal), Name: pName, } err := binder.setFieldValue(fld, defVal, "5", true) assert.NoError(t, err) assert.EqualValues(t, 5, actual()) err = binder.setFieldValue(fld, defVal, "", true) assert.NoError(t, err) assert.EqualValues(t, expectedDef, actual()) err = binder.setFieldValue(fld, defVal, "yada", true) assert.Error(t, err) } func IntParamTest(t *testing.T, pName string, val reflect.Value, defVal, expectedDef interface{}, actual func() interface{}) { fld := val.FieldByName(pName) binder := &untypedParamBinder{ parameter: spec.QueryParam(pName).Typed("integer", "int64").WithDefault(defVal), Name: pName, } err := binder.setFieldValue(fld, defVal, "5", true) assert.NoError(t, err) assert.EqualValues(t, 5, actual()) err = binder.setFieldValue(fld, defVal, "", true) assert.NoError(t, err) assert.EqualValues(t, expectedDef, actual()) err = binder.setFieldValue(fld, defVal, "yada", true) assert.Error(t, err) } func TestParamBinding(t *testing.T) { actual := new(SomeOperationParams) val := reflect.ValueOf(actual).Elem() pName := "Name" fld := val.FieldByName(pName) binder := &untypedParamBinder{ parameter: spec.QueryParam(pName).Typed("string", "").WithDefault("some-name"), Name: pName, } err := binder.setFieldValue(fld, "some-name", "the name value", true) assert.NoError(t, err) assert.Equal(t, "the name value", actual.Name) err = binder.setFieldValue(fld, "some-name", "", true) assert.NoError(t, err) assert.Equal(t, "some-name", actual.Name) IntParamTest(t, "ID", val, 1, 1, func() interface{} { return actual.ID }) IntParamTest(t, "ID", val, nil, 0, func() interface{} { return actual.ID }) IntParamTest(t, "Age", val, 1, 1, func() interface{} { return actual.Age }) IntParamTest(t, "Age", val, nil, 0, func() interface{} { return actual.Age }) IntParamTest(t, "Visits", val, 1, 1, func() interface{} { return actual.Visits }) IntParamTest(t, "Visits", val, nil, 0, func() interface{} { return actual.Visits }) IntParamTest(t, "Count", val, 1, 1, func() interface{} { return actual.Count }) IntParamTest(t, "Count", val, nil, 0, func() interface{} { return actual.Count }) IntParamTest(t, "Seq", val, 1, 1, func() interface{} { return actual.Seq }) IntParamTest(t, "Seq", val, nil, 0, func() interface{} { return actual.Seq }) IntParamTest(t, "UID", val, uint64(1), 1, func() interface{} { return actual.UID }) IntParamTest(t, "UID", val, uint64(0), 0, func() interface{} { return actual.UID }) IntParamTest(t, "UAge", val, uint(1), 1, func() interface{} { return actual.UAge }) IntParamTest(t, "UAge", val, nil, 0, func() interface{} { return actual.UAge }) IntParamTest(t, "UVisits", val, uint32(1), 1, func() interface{} { return actual.UVisits }) IntParamTest(t, "UVisits", val, nil, 0, func() interface{} { return actual.UVisits }) IntParamTest(t, "UCount", val, uint16(1), 1, func() interface{} { return actual.UCount }) IntParamTest(t, "UCount", val, nil, 0, func() interface{} { return actual.UCount }) IntParamTest(t, "USeq", val, uint8(1), 1, func() interface{} { return actual.USeq }) IntParamTest(t, "USeq", val, nil, 0, func() interface{} { return actual.USeq }) FloatParamTest(t, "score", "Score", "float", val, 1.0, 1, func() interface{} { return actual.Score }) FloatParamTest(t, "score", "Score", "float", val, nil, 0, func() interface{} { return actual.Score }) FloatParamTest(t, "rate", "Rate", "double", val, 1.0, 1, func() interface{} { return actual.Rate }) FloatParamTest(t, "rate", "Rate", "double", val, nil, 0, func() interface{} { return actual.Rate }) pName = "Confirmed" confirmedField := val.FieldByName(pName) binder = &untypedParamBinder{ parameter: spec.QueryParam(pName).Typed("boolean", "").WithDefault(true), Name: pName, } for _, tv := range evaluatesAsTrue { err = binder.setFieldValue(confirmedField, true, tv, true) assert.NoError(t, err) assert.True(t, actual.Confirmed) } err = binder.setFieldValue(confirmedField, true, "", true) assert.NoError(t, err) assert.True(t, actual.Confirmed) err = binder.setFieldValue(confirmedField, true, "0", true) assert.NoError(t, err) assert.False(t, actual.Confirmed) pName = "Timestamp" timeField := val.FieldByName(pName) dt := strfmt.DateTime(time.Date(2014, 3, 19, 2, 9, 0, 0, time.UTC)) binder = &untypedParamBinder{ parameter: spec.QueryParam(pName).Typed("string", "date-time").WithDefault(dt), Name: pName, } exp := strfmt.DateTime(time.Date(2014, 5, 14, 2, 9, 0, 0, time.UTC)) err = binder.setFieldValue(timeField, dt, exp.String(), true) assert.NoError(t, err) assert.Equal(t, exp, actual.Timestamp) err = binder.setFieldValue(timeField, dt, "", true) assert.NoError(t, err) assert.Equal(t, dt, actual.Timestamp) err = binder.setFieldValue(timeField, dt, "yada", true) assert.Error(t, err) ddt := strfmt.Date(time.Date(2014, 3, 19, 0, 0, 0, 0, time.UTC)) pName = "Birthdate" dateField := val.FieldByName(pName) binder = &untypedParamBinder{ parameter: spec.QueryParam(pName).Typed("string", "date").WithDefault(ddt), Name: pName, } expd := strfmt.Date(time.Date(2014, 5, 14, 0, 0, 0, 0, time.UTC)) err = binder.setFieldValue(dateField, ddt, expd.String(), true) assert.NoError(t, err) assert.Equal(t, expd, actual.Birthdate) err = binder.setFieldValue(dateField, ddt, "", true) assert.NoError(t, err) assert.Equal(t, ddt, actual.Birthdate) err = binder.setFieldValue(dateField, ddt, "yada", true) assert.Error(t, err) dt = strfmt.DateTime(time.Date(2014, 3, 19, 2, 9, 0, 0, time.UTC)) fdt := &dt pName = "LastFailure" ftimeField := val.FieldByName(pName) binder = &untypedParamBinder{ parameter: spec.QueryParam(pName).Typed("string", "date").WithDefault(fdt), Name: pName, } exp = strfmt.DateTime(time.Date(2014, 5, 14, 2, 9, 0, 0, time.UTC)) fexp := &exp err = binder.setFieldValue(ftimeField, fdt, fexp.String(), true) assert.NoError(t, err) assert.Equal(t, fexp, actual.LastFailure) err = binder.setFieldValue(ftimeField, fdt, "", true) assert.NoError(t, err) assert.Equal(t, fdt, actual.LastFailure) err = binder.setFieldValue(ftimeField, fdt, "", true) assert.NoError(t, err) assert.Equal(t, fdt, actual.LastFailure) actual.LastFailure = nil err = binder.setFieldValue(ftimeField, fdt, "yada", true) assert.Error(t, err) assert.Nil(t, actual.LastFailure) pName = "Unsupported" unsupportedField := val.FieldByName(pName) binder = &untypedParamBinder{ parameter: spec.QueryParam(pName).Typed("string", ""), Name: pName, } err = binder.setFieldValue(unsupportedField, nil, "", true) assert.Error(t, err) } func TestSliceConversion(t *testing.T) { actual := new(SomeOperationParams) val := reflect.ValueOf(actual).Elem() // prefsField := val.FieldByName("Prefs") // cData := "yada,2,3" // _, _, err := readFormattedSliceFieldValue("Prefs", prefsField, cData, "csv", nil) // assert.Error(t, err) sliced := []string{"some", "string", "values"} seps := map[string]string{"ssv": " ", "tsv": "\t", "pipes": "|", "csv": ",", "": ","} tagsField := val.FieldByName("Tags") for k, sep := range seps { binder := &untypedParamBinder{ Name: "Tags", parameter: spec.QueryParam("tags").CollectionOf(stringItems, k), } actual.Tags = nil cData := strings.Join(sliced, sep) tags, _, err := binder.readFormattedSliceFieldValue(cData, tagsField) assert.NoError(t, err) assert.Equal(t, sliced, tags) cData = strings.Join(sliced, " "+sep+" ") tags, _, err = binder.readFormattedSliceFieldValue(cData, tagsField) assert.NoError(t, err) assert.Equal(t, sliced, tags) tags, _, err = binder.readFormattedSliceFieldValue("", tagsField) assert.NoError(t, err) assert.Empty(t, tags) } assert.Nil(t, swag.SplitByFormat("yada", "multi")) assert.Nil(t, swag.SplitByFormat("", "")) categoriesField := val.FieldByName("Categories") binder := &untypedParamBinder{ Name: "Categories", parameter: spec.QueryParam("categories").CollectionOf(stringItems, "csv"), } cData := strings.Join(sliced, ",") categories, custom, err := binder.readFormattedSliceFieldValue(cData, categoriesField) assert.NoError(t, err) assert.EqualValues(t, sliced, actual.Categories) assert.True(t, custom) assert.Empty(t, categories) categories, custom, err = binder.readFormattedSliceFieldValue("", categoriesField) assert.Error(t, err) assert.True(t, custom) assert.Empty(t, categories) } runtime-0.21.0/middleware/swaggerui.go000066400000000000000000000075401413666762700177460ustar00rootroot00000000000000package 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 // 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.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 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 = ` {{ .Title }}
` ) runtime-0.21.0/middleware/swaggerui_test.go000066400000000000000000000016261413666762700210040ustar00rootroot00000000000000package middleware import ( "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) func TestSwaggerUIMiddleware(t *testing.T) { redoc := SwaggerUI(SwaggerUIOpts{}, nil) req, _ := http.NewRequest("GET", "/docs", nil) recorder := httptest.NewRecorder() redoc.ServeHTTP(recorder, req) assert.Equal(t, 200, recorder.Code) assert.Equal(t, "text/html; charset=utf-8", recorder.Header().Get("Content-Type")) assert.Contains(t, recorder.Body.String(), "API documentation") assert.Contains(t, recorder.Body.String(), "url: '\\/swagger.json',") assert.Contains(t, recorder.Body.String(), swaggerLatest) assert.Contains(t, recorder.Body.String(), swaggerPresetLatest) assert.Contains(t, recorder.Body.String(), swaggerStylesLatest) assert.Contains(t, recorder.Body.String(), swaggerFavicon16Latest) assert.Contains(t, recorder.Body.String(), swaggerFavicon32Latest) } runtime-0.21.0/middleware/untyped/000077500000000000000000000000001413666762700171045ustar00rootroot00000000000000runtime-0.21.0/middleware/untyped/api.go000066400000000000000000000177571413666762700202250ustar00rootroot00000000000000// 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 } runtime-0.21.0/middleware/untyped/api_test.go000066400000000000000000000203431413666762700212450ustar00rootroot00000000000000// 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 ( "io" "net/http" "sort" "testing" "github.com/go-openapi/analysis" "github.com/go-openapi/errors" "github.com/go-openapi/loads" swaggerspec "github.com/go-openapi/spec" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" ) func stubAutenticator() runtime.Authenticator { return runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) { return false, nil, nil }) } func stubAuthorizer() runtime.Authorizer { return runtime.AuthorizerFunc(func(_ *http.Request, _ interface{}) error { return nil }) } type stubConsumer struct { } func (s *stubConsumer) Consume(_ io.Reader, _ interface{}) error { return nil } type stubProducer struct { } func (s *stubProducer) Produce(_ io.Writer, _ interface{}) error { return nil } type stubOperationHandler struct { } func (s *stubOperationHandler) ParameterModel() interface{} { return nil } func (s *stubOperationHandler) Handle(params interface{}) (interface{}, error) { return nil, nil } func TestUntypedAPIRegistrations(t *testing.T) { api := NewAPI(new(loads.Document)).WithJSONDefaults() api.RegisterConsumer("application/yada", new(stubConsumer)) api.RegisterProducer("application/yada-2", new(stubProducer)) api.RegisterOperation("get", "/{someId}", new(stubOperationHandler)) api.RegisterAuth("basic", stubAutenticator()) api.RegisterAuthorizer(stubAuthorizer()) assert.NotNil(t, api.authorizer) assert.NotEmpty(t, api.authenticators) _, ok := api.authenticators["basic"] assert.True(t, ok) _, ok = api.consumers["application/yada"] assert.True(t, ok) _, ok = api.producers["application/yada-2"] assert.True(t, ok) _, ok = api.consumers["application/json"] assert.True(t, ok) _, ok = api.producers["application/json"] assert.True(t, ok) _, ok = api.operations["GET"]["/{someId}"] assert.True(t, ok) authorizer := api.Authorizer() assert.NotNil(t, authorizer) h, ok := api.OperationHandlerFor("get", "/{someId}") assert.True(t, ok) assert.NotNil(t, h) _, ok = api.OperationHandlerFor("doesntExist", "/{someId}") assert.False(t, ok) } func TestUntypedAppValidation(t *testing.T) { invalidSpecStr := `{ "consumes": ["application/json"], "produces": ["application/json"], "security": [ {"apiKey":[]} ], "parameters": { "format": { "in": "query", "name": "format", "type": "string" } }, "paths": { "/": { "parameters": [ { "name": "limit", "type": "integer", "format": "int32", "x-go-name": "Limit" } ], "get": { "consumes": ["application/x-yaml"], "produces": ["application/x-yaml"], "security": [ {"basic":[]} ], "parameters": [ { "name": "skip", "type": "integer", "format": "int32" } ] } } } }` specStr := `{ "consumes": ["application/json"], "produces": ["application/json"], "security": [ {"apiKey":[]} ], "securityDefinitions": { "basic": { "type": "basic" }, "apiKey": { "type": "apiKey", "in":"header", "name":"X-API-KEY" } }, "parameters": { "format": { "in": "query", "name": "format", "type": "string" } }, "paths": { "/": { "parameters": [ { "name": "limit", "type": "integer", "format": "int32", "x-go-name": "Limit" } ], "get": { "consumes": ["application/x-yaml"], "produces": ["application/x-yaml"], "security": [ {"basic":[]} ], "parameters": [ { "name": "skip", "type": "integer", "format": "int32" } ] } } } }` validSpec, err := loads.Analyzed([]byte(specStr), "") assert.NoError(t, err) assert.NotNil(t, validSpec) spec, err := loads.Analyzed([]byte(invalidSpecStr), "") assert.NoError(t, err) assert.NotNil(t, spec) analyzed := analysis.New(spec.Spec()) analyzedValid := analysis.New(validSpec.Spec()) cons := analyzed.ConsumesFor(analyzed.AllPaths()["/"].Get) assert.Len(t, cons, 1) prods := analyzed.RequiredProduces() assert.Len(t, prods, 2) api1 := NewAPI(spec) err = api1.Validate() assert.Error(t, err) assert.Equal(t, "missing [application/x-yaml] consumes registrations", err.Error()) api1.RegisterConsumer("application/x-yaml", new(stubConsumer)) err = api1.validate() assert.Error(t, err) assert.Equal(t, "missing [application/x-yaml] produces registrations", err.Error()) api1.RegisterProducer("application/x-yaml", new(stubProducer)) err = api1.validate() assert.Error(t, err) assert.Equal(t, "missing [GET /] operation registrations", err.Error()) api1.RegisterOperation("get", "/", new(stubOperationHandler)) err = api1.validate() assert.Error(t, err) assert.Equal(t, "missing [apiKey, basic] auth scheme registrations", err.Error()) api1.RegisterAuth("basic", stubAutenticator()) api1.RegisterAuth("apiKey", stubAutenticator()) err = api1.validate() assert.Error(t, err) assert.Equal(t, "missing [apiKey, basic] security definitions registrations", err.Error()) api3 := NewAPI(validSpec) api3.RegisterConsumer("application/x-yaml", new(stubConsumer)) api3.RegisterProducer("application/x-yaml", new(stubProducer)) api3.RegisterOperation("get", "/", new(stubOperationHandler)) api3.RegisterAuth("basic", stubAutenticator()) api3.RegisterAuth("apiKey", stubAutenticator()) err = api3.validate() assert.NoError(t, err) api3.RegisterConsumer("application/something", new(stubConsumer)) err = api3.validate() assert.Error(t, err) assert.Equal(t, "missing from spec file [application/something] consumes", err.Error()) api2 := NewAPI(spec) api2.RegisterConsumer("application/something", new(stubConsumer)) err = api2.validate() assert.Error(t, err) assert.Equal(t, "missing [application/x-yaml] consumes registrations\nmissing from spec file [application/something] consumes", err.Error()) api2.RegisterConsumer("application/x-yaml", new(stubConsumer)) delete(api2.consumers, "application/something") api2.RegisterProducer("application/something", new(stubProducer)) err = api2.validate() assert.Error(t, err) assert.Equal(t, "missing [application/x-yaml] produces registrations\nmissing from spec file [application/something] produces", err.Error()) delete(api2.producers, "application/something") api2.RegisterProducer("application/x-yaml", new(stubProducer)) expected := []string{"application/x-yaml"} sort.Strings(expected) consumes := analyzed.ConsumesFor(analyzed.AllPaths()["/"].Get) sort.Strings(consumes) assert.Equal(t, expected, consumes) consumers := api1.ConsumersFor(consumes) assert.Len(t, consumers, 1) produces := analyzed.ProducesFor(analyzed.AllPaths()["/"].Get) sort.Strings(produces) assert.Equal(t, expected, produces) producers := api1.ProducersFor(produces) assert.Len(t, producers, 1) definitions := analyzedValid.SecurityDefinitionsFor(analyzedValid.AllPaths()["/"].Get) expectedSchemes := map[string]swaggerspec.SecurityScheme{"basic": *swaggerspec.BasicAuth()} assert.Equal(t, expectedSchemes, definitions) authenticators := api3.AuthenticatorsFor(definitions) assert.Len(t, authenticators, 1) opHandler := runtime.OperationHandlerFunc(func(data interface{}) (interface{}, error) { return data, nil }) d, err := opHandler.Handle(1) assert.NoError(t, err) assert.Equal(t, 1, d) authenticator := runtime.AuthenticatorFunc(func(params interface{}) (bool, interface{}, error) { if str, ok := params.(string); ok { return ok, str, nil } return true, nil, errors.Unauthenticated("authenticator") }) ok, p, err := authenticator.Authenticate("hello") assert.True(t, ok) assert.NoError(t, err) assert.Equal(t, "hello", p) } runtime-0.21.0/middleware/untyped_request_test.go000066400000000000000000000131761413666762700222520ustar00rootroot00000000000000// 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 ( "bytes" "encoding/base64" "encoding/json" "io/ioutil" "mime/multipart" "net/http" "net/url" "strings" "testing" "time" "github.com/go-openapi/strfmt" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" ) func TestUntypedFormPost(t *testing.T) { params := parametersForFormUpload() binder := NewUntypedRequestBinder(params, nil, strfmt.Default) urlStr := "http://localhost:8002/hello" req, _ := http.NewRequest("POST", urlStr, bytes.NewBufferString(`name=the-name&age=32`)) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") data := make(map[string]interface{}) assert.NoError(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) assert.Equal(t, "the-name", data["name"]) assert.EqualValues(t, 32, data["age"]) req, _ = http.NewRequest("POST", urlStr, bytes.NewBufferString(`name=%3&age=32`)) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") data = make(map[string]interface{}) assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) } func TestUntypedFileUpload(t *testing.T) { binder := paramsForFileUpload() body := bytes.NewBuffer(nil) writer := multipart.NewWriter(body) part, err := writer.CreateFormFile("file", "plain-jane.txt") assert.NoError(t, err) _, _ = part.Write([]byte("the file contents")) _ = writer.WriteField("name", "the-name") assert.NoError(t, writer.Close()) urlStr := "http://localhost:8002/hello" req, _ := http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", writer.FormDataContentType()) data := make(map[string]interface{}) assert.NoError(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) assert.Equal(t, "the-name", data["name"]) assert.NotNil(t, data["file"]) assert.IsType(t, runtime.File{}, data["file"]) file := data["file"].(runtime.File) assert.NotNil(t, file.Header) assert.Equal(t, "plain-jane.txt", file.Header.Filename) bb, err := ioutil.ReadAll(file.Data) assert.NoError(t, err) assert.Equal(t, []byte("the file contents"), bb) req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", "application/json") data = make(map[string]interface{}) assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", "application(") data = make(map[string]interface{}) assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) body = bytes.NewBuffer(nil) writer = multipart.NewWriter(body) part, err = writer.CreateFormFile("bad-name", "plain-jane.txt") assert.NoError(t, err) _, _ = part.Write([]byte("the file contents")) _ = writer.WriteField("name", "the-name") assert.NoError(t, writer.Close()) req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", writer.FormDataContentType()) data = make(map[string]interface{}) assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", writer.FormDataContentType()) _, _ = req.MultipartReader() data = make(map[string]interface{}) assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) } func TestUntypedBindingTypesForValid(t *testing.T) { op2 := parametersForAllTypes("") binder := NewUntypedRequestBinder(op2, nil, strfmt.Default) confirmed := true name := "thomas" friend := map[string]interface{}{"name": "toby", "age": json.Number("32")} id, age, score, factor := int64(7575), int32(348), float32(5.309), float64(37.403) requestID := 19394858 tags := []string{"one", "two", "three"} dt1 := time.Date(2014, 8, 9, 0, 0, 0, 0, time.UTC) planned := strfmt.Date(dt1) dt2 := time.Date(2014, 10, 12, 8, 5, 5, 0, time.UTC) delivered := strfmt.DateTime(dt2) picture := base64.URLEncoding.EncodeToString([]byte("hello")) uri, _ := url.Parse("http://localhost:8002/hello/7575") qs := uri.Query() qs.Add("name", name) qs.Add("confirmed", "true") qs.Add("age", "348") qs.Add("score", "5.309") qs.Add("factor", "37.403") qs.Add("tags", strings.Join(tags, ",")) qs.Add("planned", planned.String()) qs.Add("delivered", delivered.String()) qs.Add("picture", picture) req, _ := http.NewRequest("POST", uri.String()+"?"+qs.Encode(), bytes.NewBuffer([]byte(`{"name":"toby","age":32}`))) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Request-Id", "19394858") data := make(map[string]interface{}) err := binder.Bind(req, RouteParams([]RouteParam{{"id", "7575"}}), runtime.JSONConsumer(), &data) assert.NoError(t, err) assert.Equal(t, id, data["id"]) assert.Equal(t, name, data["name"]) assert.Equal(t, friend, data["friend"]) assert.EqualValues(t, requestID, data["X-Request-Id"]) assert.Equal(t, tags, data["tags"]) assert.Equal(t, planned, data["planned"]) assert.Equal(t, delivered, data["delivered"]) assert.Equal(t, confirmed, data["confirmed"]) assert.Equal(t, age, data["age"]) assert.Equal(t, factor, data["factor"]) assert.Equal(t, score, data["score"]) pb, _ := base64.URLEncoding.DecodeString(picture) assert.EqualValues(t, pb, data["picture"].(strfmt.Base64)) } runtime-0.21.0/middleware/validation.go000066400000000000000000000072721413666762700201050ustar00rootroot00000000000000// 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)) } } runtime-0.21.0/middleware/validation_test.go000066400000000000000000000150761413666762700211450ustar00rootroot00000000000000// 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 ( "bytes" "net/http" "net/http/httptest" "strings" "testing" "github.com/go-openapi/errors" "github.com/stretchr/testify/assert" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/internal/testing/petstore" ) func newTestValidation(ctx *Context, next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { matched, rCtx, _ := ctx.RouteInfo(r) if rCtx != nil { r = rCtx } if matched == nil { ctx.NotFound(rw, r) return } _, r, result := ctx.BindAndValidate(r, matched) if result != nil { ctx.Respond(rw, r, matched.Produces, matched, result) return } next.ServeHTTP(rw, r) }) } func TestContentTypeValidation(t *testing.T) { spec, api := petstore.NewAPI(t) context := NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) mw := newTestValidation(context, http.HandlerFunc(terminator)) recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/api/pets", nil) request.Header.Add("Accept", "*/*") mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusOK, recorder.Code) recorder = httptest.NewRecorder() request, _ = http.NewRequest("POST", "/api/pets", nil) request.Header.Add("content-type", "application(") request.Header.Add("Accept", "application/json") request.ContentLength = 1 mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusBadRequest, recorder.Code) assert.Equal(t, "application/json", recorder.Header().Get("content-type")) recorder = httptest.NewRecorder() request, _ = http.NewRequest("POST", "/api/pets", nil) request.Header.Add("Accept", "application/json") request.Header.Add("content-type", "text/html") request.ContentLength = 1 mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusUnsupportedMediaType, recorder.Code) assert.Equal(t, "application/json", recorder.Header().Get("content-type")) recorder = httptest.NewRecorder() request, _ = http.NewRequest("POST", "/api/pets", strings.NewReader(`{"name":"dog"}`)) request.Header.Add("Accept", "application/json") request.Header.Add("content-type", "text/html") request.TransferEncoding = []string{"chunked"} mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusUnsupportedMediaType, recorder.Code) assert.Equal(t, "application/json", recorder.Header().Get("content-type")) recorder = httptest.NewRecorder() request, _ = http.NewRequest("POST", "/api/pets", nil) request.Header.Add("Accept", "application/json+special") request.Header.Add("content-type", "text/html") mw.ServeHTTP(recorder, request) assert.Equal(t, 406, recorder.Code) assert.Equal(t, "application/json", recorder.Header().Get("content-type")) // client sends data with unsupported mime recorder = httptest.NewRecorder() request, _ = http.NewRequest("POST", "/api/pets", nil) request.Header.Add("Accept", "application/json") // this content type is served by default by the API request.Header.Add("content-type", "application/json+special") request.ContentLength = 1 mw.ServeHTTP(recorder, request) assert.Equal(t, 415, recorder.Code) // Unsupported media type assert.Equal(t, "application/json", recorder.Header().Get("content-type")) // client sends a body of data with no mime: breaks recorder = httptest.NewRecorder() request, _ = http.NewRequest("POST", "/api/pets", nil) request.Header.Add("Accept", "application/json") request.ContentLength = 1 mw.ServeHTTP(recorder, request) assert.Equal(t, 415, recorder.Code) assert.Equal(t, "application/json", recorder.Header().Get("content-type")) } func TestResponseFormatValidation(t *testing.T) { spec, api := petstore.NewAPI(t) context := NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) mw := newTestValidation(context, http.HandlerFunc(terminator)) recorder := httptest.NewRecorder() request, _ := http.NewRequest("POST", "/api/pets", bytes.NewBuffer([]byte(`name: Dog`))) request.Header.Set(runtime.HeaderContentType, "application/x-yaml") request.Header.Set(runtime.HeaderAccept, "application/x-yaml") mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code, recorder.Body.String()) recorder = httptest.NewRecorder() request, _ = http.NewRequest("POST", "/api/pets", bytes.NewBuffer([]byte(`name: Dog`))) request.Header.Set(runtime.HeaderContentType, "application/x-yaml") request.Header.Set(runtime.HeaderAccept, "application/sml") mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusNotAcceptable, recorder.Code) } func TestValidateContentType(t *testing.T) { data := []struct { hdr string allowed []string err *errors.Validation }{ {"application/json", []string{"application/json"}, nil}, {"application/json", []string{"application/x-yaml", "text/html"}, errors.InvalidContentType("application/json", []string{"application/x-yaml", "text/html"})}, {"text/html; charset=utf-8", []string{"text/html"}, nil}, {"text/html;charset=utf-8", []string{"text/html"}, nil}, {"", []string{"application/json"}, errors.InvalidContentType("", []string{"application/json"})}, {"text/html; charset=utf-8", []string{"application/json"}, errors.InvalidContentType("text/html; charset=utf-8", []string{"application/json"})}, {"application(", []string{"application/json"}, errors.InvalidContentType("application(", []string{"application/json"})}, {"application/json;char*", []string{"application/json"}, errors.InvalidContentType("application/json;char*", []string{"application/json"})}, {"application/octet-stream", []string{"image/jpeg", "application/*"}, nil}, {"image/png", []string{"*/*", "application/json"}, nil}, } for _, v := range data { err := validateContentType(v.allowed, v.hdr) if v.err == nil { assert.NoError(t, err, "input: %q", v.hdr) } else { assert.Error(t, err, "input: %q", v.hdr) assert.IsType(t, &errors.Validation{}, err, "input: %q", v.hdr) assert.Equal(t, v.err.Error(), err.Error(), "input: %q", v.hdr) assert.EqualValues(t, http.StatusUnsupportedMediaType, err.(*errors.Validation).Code()) } } } runtime-0.21.0/request.go000066400000000000000000000065531413666762700153270ustar00rootroot00000000000000// 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(http.CanonicalHeaderKey("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) } runtime-0.21.0/request_test.go000066400000000000000000000121261413666762700163570ustar00rootroot00000000000000// 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" "bytes" "io" "io/ioutil" "net/http" "net/url" "strings" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/assert" ) type eofReader struct{} func (e *eofReader) Read(d []byte) (int, error) { return 0, io.EOF } func closeReader(rdr io.Reader) *closeCounting { return &closeCounting{ rdr: rdr, } } type closeCounting struct { rdr io.Reader closed int } func (c *closeCounting) Read(d []byte) (int, error) { return c.rdr.Read(d) } func (c *closeCounting) Close() error { c.closed++ if cr, ok := c.rdr.(io.ReadCloser); ok { return cr.Close() } return nil } type countingBufioReader struct { buffereds int peeks int reads int br interface { Buffered() int Peek(int) ([]byte, error) Read([]byte) (int, error) } } func (c *countingBufioReader) Buffered() int { c.buffereds++ return c.br.Buffered() } func (c *countingBufioReader) Peek(v int) ([]byte, error) { c.peeks++ return c.br.Peek(v) } func (c *countingBufioReader) Read(p []byte) (int, error) { c.reads++ return c.br.Read(p) } func TestPeekingReader(t *testing.T) { // just passes to original reader when nothing called exp1 := []byte("original") pr1 := newPeekingReader(closeReader(bytes.NewReader(exp1))) b1, err := ioutil.ReadAll(pr1) if assert.NoError(t, err) { assert.Equal(t, exp1, b1) } // uses actual when there was some buffering exp2 := []byte("actual") pr2 := newPeekingReader(closeReader(bytes.NewReader(exp2))) peeked, err := pr2.underlying.Peek(1) require.NoError(t, err) require.Equal(t, "a", string(peeked)) b2, err := ioutil.ReadAll(pr2) if assert.NoError(t, err) { assert.Equal(t, string(exp2), string(b2)) } // passes close call through to original reader cr := closeReader(closeReader(bytes.NewReader(exp2))) pr3 := newPeekingReader(cr) require.NoError(t, pr3.Close()) require.Equal(t, 1, cr.closed) // returns false when the stream is empty pr4 := newPeekingReader(closeReader(&eofReader{})) require.False(t, pr4.HasContent()) // returns true when the stream has content rdr := closeReader(strings.NewReader("hello")) pr := newPeekingReader(rdr) cbr := &countingBufioReader{ br: bufio.NewReader(rdr), } pr.underlying = cbr require.True(t, pr.HasContent()) require.Equal(t, 1, cbr.buffereds) require.Equal(t, 1, cbr.peeks) require.Equal(t, 0, cbr.reads) require.True(t, pr.HasContent()) require.Equal(t, 2, cbr.buffereds) require.Equal(t, 1, cbr.peeks) require.Equal(t, 0, cbr.reads) b, err := ioutil.ReadAll(pr) require.NoError(t, err) require.Equal(t, "hello", string(b)) require.Equal(t, 2, cbr.buffereds) require.Equal(t, 1, cbr.peeks) require.Equal(t, 2, cbr.reads) require.Equal(t, 0, cbr.br.Buffered()) } func TestJSONRequest(t *testing.T) { req, err := JSONRequest("GET", "/swagger.json", nil) assert.NoError(t, err) assert.Equal(t, "GET", req.Method) assert.Equal(t, JSONMime, req.Header.Get(HeaderContentType)) assert.Equal(t, JSONMime, req.Header.Get(HeaderAccept)) req, err = JSONRequest("GET", "%2", nil) assert.Error(t, err) assert.Nil(t, req) } func TestHasBody(t *testing.T) { req, _ := http.NewRequest("GET", "", nil) assert.False(t, HasBody(req)) req.ContentLength = 123 assert.True(t, HasBody(req)) } func TestMethod(t *testing.T) { testcase := []struct { method string canHaveBody bool allowsBody bool isSafe bool }{ {"put", true, true, false}, {"post", true, true, false}, {"patch", true, true, false}, {"delete", true, true, false}, {"get", false, true, true}, {"options", false, true, false}, {"head", false, false, true}, {"invalid", false, true, false}, {"", false, true, false}, } for _, tc := range testcase { t.Run(tc.method, func(t *testing.T) { assert.Equal(t, tc.canHaveBody, CanHaveBody(tc.method), "CanHaveBody") req := http.Request{Method: tc.method} assert.Equal(t, tc.allowsBody, AllowsBody(&req), "AllowsBody") assert.Equal(t, tc.isSafe, IsSafe(&req), "IsSafe") }) } } func TestReadSingle(t *testing.T) { values := url.Values(make(map[string][]string)) values.Add("something", "the thing") assert.Equal(t, "the thing", ReadSingleValue(Values(values), "something")) assert.Empty(t, ReadSingleValue(Values(values), "notthere")) } func TestReadCollection(t *testing.T) { values := url.Values(make(map[string][]string)) values.Add("something", "value1,value2") assert.Equal(t, []string{"value1", "value2"}, ReadCollectionValue(Values(values), "something", "csv")) assert.Empty(t, ReadCollectionValue(Values(values), "notthere", "")) } runtime-0.21.0/security/000077500000000000000000000000001413666762700151465ustar00rootroot00000000000000runtime-0.21.0/security/apikey_auth_test.go000066400000000000000000000137431413666762700210470ustar00rootroot00000000000000// 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" "testing" "github.com/go-openapi/errors" "github.com/stretchr/testify/assert" ) var tokenAuth = TokenAuthentication(func(token string) (interface{}, error) { if token == "token123" { return "admin", nil } return nil, errors.Unauthenticated("token") }) var tokenAuthCtx = TokenAuthenticationCtx(func(ctx context.Context, token string) (context.Context, interface{}, error) { if token == "token123" { return context.WithValue(ctx, extra, extraWisdom), "admin", nil } return context.WithValue(ctx, reason, expReason), nil, errors.Unauthenticated("token") }) func TestInvalidApiKeyAuthInitialization(t *testing.T) { assert.Panics(t, func() { APIKeyAuth("api_key", "qery", tokenAuth) }) } func TestValidApiKeyAuth(t *testing.T) { ta := APIKeyAuth("api_key", "query", tokenAuth) ta2 := APIKeyAuth("X-API-KEY", "header", tokenAuth) req1, _ := http.NewRequest("GET", "/blah?api_key=token123", nil) ok, usr, err := ta.Authenticate(req1) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) req2, _ := http.NewRequest("GET", "/blah", nil) req2.Header.Set("X-API-KEY", "token123") ok, usr, err = ta2.Authenticate(req2) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) } func TestInvalidApiKeyAuth(t *testing.T) { ta := APIKeyAuth("api_key", "query", tokenAuth) ta2 := APIKeyAuth("X-API-KEY", "header", tokenAuth) req1, _ := http.NewRequest("GET", "/blah?api_key=token124", nil) ok, usr, err := ta.Authenticate(req1) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) req2, _ := http.NewRequest("GET", "/blah", nil) req2.Header.Set("X-API-KEY", "token124") ok, usr, err = ta2.Authenticate(req2) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) } func TestMissingApiKeyAuth(t *testing.T) { ta := APIKeyAuth("api_key", "query", tokenAuth) ta2 := APIKeyAuth("X-API-KEY", "header", tokenAuth) req1, _ := http.NewRequest("GET", "/blah", nil) req1.Header.Set("X-API-KEY", "token123") ok, usr, err := ta.Authenticate(req1) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) req2, _ := http.NewRequest("GET", "/blah?api_key=token123", nil) ok, usr, err = ta2.Authenticate(req2) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) } func TestInvalidApiKeyAuthInitializationCtx(t *testing.T) { assert.Panics(t, func() { APIKeyAuthCtx("api_key", "qery", tokenAuthCtx) }) } func TestValidApiKeyAuthCtx(t *testing.T) { ta := APIKeyAuthCtx("api_key", "query", tokenAuthCtx) ta2 := APIKeyAuthCtx("X-API-KEY", "header", tokenAuthCtx) req1, _ := http.NewRequest("GET", "/blah?api_key=token123", nil) req1 = req1.WithContext(context.WithValue(req1.Context(), original, wisdom)) ok, usr, err := ta.Authenticate(req1) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) assert.Equal(t, wisdom, req1.Context().Value(original)) assert.Equal(t, extraWisdom, req1.Context().Value(extra)) assert.Nil(t, req1.Context().Value(reason)) req2, _ := http.NewRequest("GET", "/blah", nil) req2 = req2.WithContext(context.WithValue(req2.Context(), original, wisdom)) req2.Header.Set("X-API-KEY", "token123") ok, usr, err = ta2.Authenticate(req2) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) assert.Equal(t, wisdom, req2.Context().Value(original)) assert.Equal(t, extraWisdom, req2.Context().Value(extra)) assert.Nil(t, req2.Context().Value(reason)) } func TestInvalidApiKeyAuthCtx(t *testing.T) { ta := APIKeyAuthCtx("api_key", "query", tokenAuthCtx) ta2 := APIKeyAuthCtx("X-API-KEY", "header", tokenAuthCtx) req1, _ := http.NewRequest("GET", "/blah?api_key=token124", nil) req1 = req1.WithContext(context.WithValue(req1.Context(), original, wisdom)) ok, usr, err := ta.Authenticate(req1) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) assert.Equal(t, wisdom, req1.Context().Value(original)) assert.Equal(t, expReason, req1.Context().Value(reason)) assert.Nil(t, req1.Context().Value(extra)) req2, _ := http.NewRequest("GET", "/blah", nil) req2 = req2.WithContext(context.WithValue(req2.Context(), original, wisdom)) req2.Header.Set("X-API-KEY", "token124") ok, usr, err = ta2.Authenticate(req2) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) assert.Equal(t, wisdom, req2.Context().Value(original)) assert.Equal(t, expReason, req2.Context().Value(reason)) assert.Nil(t, req2.Context().Value(extra)) } func TestMissingApiKeyAuthCtx(t *testing.T) { ta := APIKeyAuthCtx("api_key", "query", tokenAuthCtx) ta2 := APIKeyAuthCtx("X-API-KEY", "header", tokenAuthCtx) req1, _ := http.NewRequest("GET", "/blah", nil) req1 = req1.WithContext(context.WithValue(req1.Context(), original, wisdom)) req1.Header.Set("X-API-KEY", "token123") ok, usr, err := ta.Authenticate(req1) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) assert.Equal(t, wisdom, req1.Context().Value(original)) assert.Nil(t, req1.Context().Value(reason)) assert.Nil(t, req1.Context().Value(extra)) req2, _ := http.NewRequest("GET", "/blah?api_key=token123", nil) req2 = req2.WithContext(context.WithValue(req2.Context(), original, wisdom)) ok, usr, err = ta2.Authenticate(req2) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) assert.Equal(t, wisdom, req2.Context().Value(original)) assert.Nil(t, req2.Context().Value(reason)) assert.Nil(t, req2.Context().Value(extra)) } runtime-0.21.0/security/authenticator.go000066400000000000000000000214261413666762700203540ustar00rootroot00000000000000// 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("Authorization") 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("Authorization") 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 }) } runtime-0.21.0/security/authorizer.go000066400000000000000000000016441413666762700176760ustar00rootroot00000000000000// 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 }) } runtime-0.21.0/security/authorizer_test.go000066400000000000000000000014511413666762700207310ustar00rootroot00000000000000// 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 ( "testing" "github.com/stretchr/testify/assert" ) func TestAuthorized(t *testing.T) { authorizer := Authorized() err := authorizer.Authorize(nil, nil) assert.NoError(t, err) } runtime-0.21.0/security/basic_auth_test.go000066400000000000000000000106431413666762700206420ustar00rootroot00000000000000// 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" "testing" "github.com/go-openapi/errors" "github.com/stretchr/testify/assert" ) var basicAuthHandler = UserPassAuthentication(func(user, pass string) (interface{}, error) { if user == "admin" && pass == "123456" { return "admin", nil } return "", errors.Unauthenticated("basic") }) func TestValidBasicAuth(t *testing.T) { ba := BasicAuth(basicAuthHandler) req, _ := http.NewRequest("GET", "/blah", nil) req.SetBasicAuth("admin", "123456") ok, usr, err := ba.Authenticate(req) assert.NoError(t, err) assert.True(t, ok) assert.Equal(t, "admin", usr) } func TestInvalidBasicAuth(t *testing.T) { ba := BasicAuth(basicAuthHandler) req, _ := http.NewRequest("GET", "/blah", nil) req.SetBasicAuth("admin", "admin") ok, usr, err := ba.Authenticate(req) assert.Error(t, err) assert.True(t, ok) assert.Equal(t, "", usr) assert.NotEmpty(t, FailedBasicAuth(req)) assert.Equal(t, DefaultRealmName, FailedBasicAuth(req)) } func TestMissingbasicAuth(t *testing.T) { ba := BasicAuth(basicAuthHandler) req, _ := http.NewRequest("GET", "/blah", nil) ok, usr, err := ba.Authenticate(req) assert.NoError(t, err) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NotEmpty(t, FailedBasicAuth(req)) assert.Equal(t, DefaultRealmName, FailedBasicAuth(req)) } func TestNoRequestBasicAuth(t *testing.T) { ba := BasicAuth(basicAuthHandler) ok, usr, err := ba.Authenticate("token") assert.NoError(t, err) assert.False(t, ok) assert.Nil(t, usr) } type secTestKey uint8 const ( original secTestKey = iota extra reason ) const ( wisdom = "The man who is swimming against the stream knows the strength of it." extraWisdom = "Our greatest glory is not in never falling, but in rising every time we fall." expReason = "I like the dreams of the future better than the history of the past." ) var basicAuthHandlerCtx = UserPassAuthenticationCtx(func(ctx context.Context, user, pass string) (context.Context, interface{}, error) { if user == "admin" && pass == "123456" { return context.WithValue(ctx, extra, extraWisdom), "admin", nil } return context.WithValue(ctx, reason, expReason), "", errors.Unauthenticated("basic") }) func TestValidBasicAuthCtx(t *testing.T) { ba := BasicAuthCtx(basicAuthHandlerCtx) req, _ := http.NewRequest("GET", "/blah", nil) req = req.WithContext(context.WithValue(req.Context(), original, wisdom)) req.SetBasicAuth("admin", "123456") ok, usr, err := ba.Authenticate(req) assert.NoError(t, err) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.Equal(t, wisdom, req.Context().Value(original)) assert.Equal(t, extraWisdom, req.Context().Value(extra)) assert.Nil(t, req.Context().Value(reason)) } func TestInvalidBasicAuthCtx(t *testing.T) { ba := BasicAuthCtx(basicAuthHandlerCtx) req, _ := http.NewRequest("GET", "/blah", nil) req = req.WithContext(context.WithValue(req.Context(), original, wisdom)) req.SetBasicAuth("admin", "admin") ok, usr, err := ba.Authenticate(req) assert.Error(t, err) assert.True(t, ok) assert.Equal(t, "", usr) assert.Equal(t, wisdom, req.Context().Value(original)) assert.Nil(t, req.Context().Value(extra)) assert.Equal(t, expReason, req.Context().Value(reason)) } func TestMissingbasicAuthCtx(t *testing.T) { ba := BasicAuthCtx(basicAuthHandlerCtx) req, _ := http.NewRequest("GET", "/blah", nil) req = req.WithContext(context.WithValue(req.Context(), original, wisdom)) ok, usr, err := ba.Authenticate(req) assert.NoError(t, err) assert.False(t, ok) assert.Equal(t, nil, usr) assert.Equal(t, wisdom, req.Context().Value(original)) assert.Nil(t, req.Context().Value(extra)) assert.Nil(t, req.Context().Value(reason)) } func TestNoRequestBasicAuthCtx(t *testing.T) { ba := BasicAuthCtx(basicAuthHandlerCtx) ok, usr, err := ba.Authenticate("token") assert.NoError(t, err) assert.False(t, ok) assert.Nil(t, usr) } runtime-0.21.0/security/bearer_auth_test.go000066400000000000000000000271421413666762700210230ustar00rootroot00000000000000package security import ( "bytes" "context" "mime/multipart" "net/http" "net/url" "strings" "testing" "github.com/go-openapi/errors" "github.com/stretchr/testify/assert" ) var bearerAuth = ScopedTokenAuthentication(func(token string, requiredScopes []string) (interface{}, error) { if token == "token123" { return "admin", nil } return nil, errors.Unauthenticated("bearer") }) func TestValidBearerAuth(t *testing.T) { ba := BearerAuth("owners_auth", bearerAuth) req1, _ := http.NewRequest("GET", "/blah?access_token=token123", nil) ok, usr, err := ba.Authenticate(&ScopedAuthRequest{Request: req1}) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) assert.Equal(t, OAuth2SchemeName(req1), "owners_auth") req2, _ := http.NewRequest("GET", "/blah", nil) req2.Header.Set("Authorization", "Bearer token123") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req2}) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) assert.Equal(t, OAuth2SchemeName(req2), "owners_auth") body := url.Values(map[string][]string{}) body.Set("access_token", "token123") req3, _ := http.NewRequest("POST", "/blah", strings.NewReader(body.Encode())) req3.Header.Set("Content-Type", "application/x-www-form-urlencoded") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req3}) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) assert.Equal(t, OAuth2SchemeName(req3), "owners_auth") mpbody := bytes.NewBuffer(nil) writer := multipart.NewWriter(mpbody) _ = writer.WriteField("access_token", "token123") writer.Close() req4, _ := http.NewRequest("POST", "/blah", mpbody) req4.Header.Set("Content-Type", writer.FormDataContentType()) ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req4}) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) assert.Equal(t, OAuth2SchemeName(req4), "owners_auth") } func TestInvalidBearerAuth(t *testing.T) { ba := BearerAuth("owners_auth", bearerAuth) req1, _ := http.NewRequest("GET", "/blah?access_token=token124", nil) ok, usr, err := ba.Authenticate(&ScopedAuthRequest{Request: req1}) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) req2, _ := http.NewRequest("GET", "/blah", nil) req2.Header.Set("Authorization", "Bearer token124") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req2}) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) body := url.Values(map[string][]string{}) body.Set("access_token", "token124") req3, _ := http.NewRequest("POST", "/blah", strings.NewReader(body.Encode())) req3.Header.Set("Content-Type", "application/x-www-form-urlencoded") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req3}) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) mpbody := bytes.NewBuffer(nil) writer := multipart.NewWriter(mpbody) _ = writer.WriteField("access_token", "token124") writer.Close() req4, _ := http.NewRequest("POST", "/blah", mpbody) req4.Header.Set("Content-Type", writer.FormDataContentType()) ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req4}) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) } func TestMissingBearerAuth(t *testing.T) { ba := BearerAuth("owners_auth", bearerAuth) req1, _ := http.NewRequest("GET", "/blah?access_toke=token123", nil) ok, usr, err := ba.Authenticate(&ScopedAuthRequest{Request: req1}) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) req2, _ := http.NewRequest("GET", "/blah", nil) req2.Header.Set("Authorization", "Beare token123") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req2}) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) body := url.Values(map[string][]string{}) body.Set("access_toke", "token123") req3, _ := http.NewRequest("POST", "/blah", strings.NewReader(body.Encode())) req3.Header.Set("Content-Type", "application/x-www-form-urlencoded") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req3}) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) mpbody := bytes.NewBuffer(nil) writer := multipart.NewWriter(mpbody) _ = writer.WriteField("access_toke", "token123") writer.Close() req4, _ := http.NewRequest("POST", "/blah", mpbody) req4.Header.Set("Content-Type", writer.FormDataContentType()) ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req4}) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) } var bearerAuthCtx = ScopedTokenAuthenticationCtx(func(ctx context.Context, token string, requiredScopes []string) (context.Context, interface{}, error) { if token == "token123" { return context.WithValue(ctx, extra, extraWisdom), "admin", nil } return context.WithValue(ctx, reason, expReason), nil, errors.Unauthenticated("bearer") }) func TestValidBearerAuthCtx(t *testing.T) { ba := BearerAuthCtx("owners_auth", bearerAuthCtx) req1, _ := http.NewRequest("GET", "/blah?access_token=token123", nil) req1 = req1.WithContext(context.WithValue(req1.Context(), original, wisdom)) ok, usr, err := ba.Authenticate(&ScopedAuthRequest{Request: req1}) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) assert.Equal(t, wisdom, req1.Context().Value(original)) assert.Equal(t, extraWisdom, req1.Context().Value(extra)) assert.Nil(t, req1.Context().Value(reason)) assert.Equal(t, OAuth2SchemeName(req1), "owners_auth") req2, _ := http.NewRequest("GET", "/blah", nil) req2 = req2.WithContext(context.WithValue(req2.Context(), original, wisdom)) req2.Header.Set("Authorization", "Bearer token123") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req2}) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) assert.Equal(t, wisdom, req2.Context().Value(original)) assert.Equal(t, extraWisdom, req2.Context().Value(extra)) assert.Nil(t, req2.Context().Value(reason)) assert.Equal(t, OAuth2SchemeName(req2), "owners_auth") body := url.Values(map[string][]string{}) body.Set("access_token", "token123") req3, _ := http.NewRequest("POST", "/blah", strings.NewReader(body.Encode())) req3 = req3.WithContext(context.WithValue(req3.Context(), original, wisdom)) req3.Header.Set("Content-Type", "application/x-www-form-urlencoded") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req3}) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) assert.Equal(t, wisdom, req3.Context().Value(original)) assert.Equal(t, extraWisdom, req3.Context().Value(extra)) assert.Nil(t, req3.Context().Value(reason)) assert.Equal(t, OAuth2SchemeName(req3), "owners_auth") mpbody := bytes.NewBuffer(nil) writer := multipart.NewWriter(mpbody) _ = writer.WriteField("access_token", "token123") writer.Close() req4, _ := http.NewRequest("POST", "/blah", mpbody) req4 = req4.WithContext(context.WithValue(req4.Context(), original, wisdom)) req4.Header.Set("Content-Type", writer.FormDataContentType()) ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req4}) assert.True(t, ok) assert.Equal(t, "admin", usr) assert.NoError(t, err) assert.Equal(t, wisdom, req4.Context().Value(original)) assert.Equal(t, extraWisdom, req4.Context().Value(extra)) assert.Nil(t, req4.Context().Value(reason)) assert.Equal(t, OAuth2SchemeName(req4), "owners_auth") } func TestInvalidBearerAuthCtx(t *testing.T) { ba := BearerAuthCtx("owners_auth", bearerAuthCtx) req1, _ := http.NewRequest("GET", "/blah?access_token=token124", nil) req1 = req1.WithContext(context.WithValue(req1.Context(), original, wisdom)) ok, usr, err := ba.Authenticate(&ScopedAuthRequest{Request: req1}) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) assert.Equal(t, wisdom, req1.Context().Value(original)) assert.Equal(t, expReason, req1.Context().Value(reason)) assert.Nil(t, req1.Context().Value(extra)) req2, _ := http.NewRequest("GET", "/blah", nil) req2 = req2.WithContext(context.WithValue(req2.Context(), original, wisdom)) req2.Header.Set("Authorization", "Bearer token124") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req2}) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) assert.Equal(t, wisdom, req2.Context().Value(original)) assert.Equal(t, expReason, req2.Context().Value(reason)) assert.Nil(t, req2.Context().Value(extra)) body := url.Values(map[string][]string{}) body.Set("access_token", "token124") req3, _ := http.NewRequest("POST", "/blah", strings.NewReader(body.Encode())) req3 = req3.WithContext(context.WithValue(req3.Context(), original, wisdom)) req3.Header.Set("Content-Type", "application/x-www-form-urlencoded") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req3}) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) assert.Equal(t, wisdom, req3.Context().Value(original)) assert.Equal(t, expReason, req3.Context().Value(reason)) assert.Nil(t, req3.Context().Value(extra)) mpbody := bytes.NewBuffer(nil) writer := multipart.NewWriter(mpbody) _ = writer.WriteField("access_token", "token124") writer.Close() req4, _ := http.NewRequest("POST", "/blah", mpbody) req4 = req4.WithContext(context.WithValue(req4.Context(), original, wisdom)) req4.Header.Set("Content-Type", writer.FormDataContentType()) ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req4}) assert.True(t, ok) assert.Equal(t, nil, usr) assert.Error(t, err) assert.Equal(t, wisdom, req4.Context().Value(original)) assert.Equal(t, expReason, req4.Context().Value(reason)) assert.Nil(t, req4.Context().Value(extra)) } func TestMissingBearerAuthCtx(t *testing.T) { ba := BearerAuthCtx("owners_auth", bearerAuthCtx) req1, _ := http.NewRequest("GET", "/blah?access_toke=token123", nil) req1 = req1.WithContext(context.WithValue(req1.Context(), original, wisdom)) ok, usr, err := ba.Authenticate(&ScopedAuthRequest{Request: req1}) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) assert.Equal(t, wisdom, req1.Context().Value(original)) assert.Nil(t, req1.Context().Value(reason)) assert.Nil(t, req1.Context().Value(extra)) req2, _ := http.NewRequest("GET", "/blah", nil) req2.Header.Set("Authorization", "Beare token123") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req2}) req2 = req2.WithContext(context.WithValue(req2.Context(), original, wisdom)) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) assert.Equal(t, wisdom, req2.Context().Value(original)) assert.Nil(t, req2.Context().Value(reason)) assert.Nil(t, req2.Context().Value(extra)) body := url.Values(map[string][]string{}) body.Set("access_toke", "token123") req3, _ := http.NewRequest("POST", "/blah", strings.NewReader(body.Encode())) req3 = req3.WithContext(context.WithValue(req3.Context(), original, wisdom)) req3.Header.Set("Content-Type", "application/x-www-form-urlencoded") ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req3}) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) assert.Equal(t, wisdom, req3.Context().Value(original)) assert.Nil(t, req3.Context().Value(reason)) assert.Nil(t, req3.Context().Value(extra)) mpbody := bytes.NewBuffer(nil) writer := multipart.NewWriter(mpbody) _ = writer.WriteField("access_toke", "token123") writer.Close() req4, _ := http.NewRequest("POST", "/blah", mpbody) req4 = req4.WithContext(context.WithValue(req4.Context(), original, wisdom)) req4.Header.Set("Content-Type", writer.FormDataContentType()) ok, usr, err = ba.Authenticate(&ScopedAuthRequest{Request: req4}) assert.False(t, ok) assert.Equal(t, nil, usr) assert.NoError(t, err) assert.Equal(t, wisdom, req4.Context().Value(original)) assert.Nil(t, req4.Context().Value(reason)) assert.Nil(t, req4.Context().Value(extra)) } runtime-0.21.0/statuses.go000066400000000000000000000050171413666762700155040ustar00rootroot00000000000000// 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", } runtime-0.21.0/text.go000066400000000000000000000055261413666762700146220ustar00rootroot00000000000000// 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 { data = "" 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 }) } runtime-0.21.0/text_test.go000066400000000000000000000117171413666762700156600ustar00rootroot00000000000000// 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" "errors" "fmt" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) var consProdText = `The quick brown fox jumped over the lazy dog.` func TestTextConsumer(t *testing.T) { cons := TextConsumer() // can consume as a string var str string err1 := cons.Consume(bytes.NewBuffer([]byte(consProdText)), &str) assert.NoError(t, err1) assert.Equal(t, consProdText, str) var tu textUnmarshalDummy // can consume as a TextUnmarshaler err3 := cons.Consume(bytes.NewBuffer([]byte(consProdText)), &tu) assert.NoError(t, err3) assert.Equal(t, consProdText, tu.str) // text unmarshal objects can return an error as well, this will be propagated assert.NoError(t, cons.Consume(bytes.NewBuffer(nil), &tu)) // when readers can't be read, those errors will be propogated as well assert.Error(t, cons.Consume(new(nopReader), &tu)) // readers can also not be nil assert.Error(t, cons.Consume(nil, &tu)) // can't consume nil ptr's or unsupported types assert.Error(t, cons.Consume(bytes.NewBuffer([]byte(consProdText)), nil)) assert.Error(t, cons.Consume(bytes.NewBuffer([]byte(consProdText)), 42)) assert.Error(t, cons.Consume(bytes.NewBuffer([]byte(consProdText)), &struct{}{})) } type textUnmarshalDummy struct { str string } func (t *textUnmarshalDummy) UnmarshalText(b []byte) error { if len(b) == 0 { return errors.New("no text given") } t.str = string(b) return nil } type nopReader struct{} func (n *nopReader) Read(p []byte) (int, error) { return 0, errors.New("nop") } func TestTextProducer(t *testing.T) { prod := TextProducer() rw := httptest.NewRecorder() err := prod.Produce(rw, consProdText) assert.NoError(t, err) assert.Equal(t, consProdText, rw.Body.String()) rw2 := httptest.NewRecorder() err2 := prod.Produce(rw2, &consProdText) assert.NoError(t, err2) assert.Equal(t, consProdText, rw2.Body.String()) // should always work with type aliases // as an alias is sometimes given by generated go-swagger code type alias string aliasProdText := alias(consProdText) rw3 := httptest.NewRecorder() err3 := prod.Produce(rw3, aliasProdText) assert.NoError(t, err3) assert.Equal(t, consProdText, rw3.Body.String()) rw4 := httptest.NewRecorder() err4 := prod.Produce(rw4, &aliasProdText) assert.NoError(t, err4) assert.Equal(t, consProdText, rw4.Body.String()) const answer = "42" // Should always work with objects implementing Stringer interface rw5 := httptest.NewRecorder() err5 := prod.Produce(rw5, &stringerDummy{answer}) assert.NoError(t, err5) assert.Equal(t, answer, rw5.Body.String()) // Should always work with objects implementing TextMarshaler interface rw6 := httptest.NewRecorder() err6 := prod.Produce(rw6, &textMarshalDummy{answer}) assert.NoError(t, err6) assert.Equal(t, answer, rw6.Body.String()) rw10 := httptest.NewRecorder() err10 := prod.Produce(rw10, errors.New(answer)) assert.NoError(t, err10) assert.Equal(t, answer, rw10.Body.String()) rw11 := httptest.NewRecorder() err11 := prod.Produce(rw11, Error{Message: answer}) assert.NoError(t, err11) assert.Equal(t, fmt.Sprintf(`{"message":%q}`, answer), rw11.Body.String()) rw12 := httptest.NewRecorder() err12 := prod.Produce(rw12, &Error{Message: answer}) assert.NoError(t, err12) assert.Equal(t, fmt.Sprintf(`{"message":%q}`, answer), rw12.Body.String()) rw13 := httptest.NewRecorder() err13 := prod.Produce(rw13, []string{answer}) assert.NoError(t, err13) assert.Equal(t, fmt.Sprintf(`[%q]`, answer), rw13.Body.String()) // should not work with anything that's not (indirectly) a string rw7 := httptest.NewRecorder() err7 := prod.Produce(rw7, 42) assert.Error(t, err7) // nil values should also be safely caught with an error rw8 := httptest.NewRecorder() err8 := prod.Produce(rw8, nil) assert.Error(t, err8) // writer can not be nil assert.Error(t, prod.Produce(nil, &textMarshalDummy{answer})) // should not work for a textMarshaler that returns an error during marshalling rw9 := httptest.NewRecorder() err9 := prod.Produce(rw9, new(textMarshalDummy)) assert.Error(t, err9) } type Error struct { Message string `json:"message"` } type stringerDummy struct { str string } func (t *stringerDummy) String() string { return t.str } type textMarshalDummy struct { str string } func (t *textMarshalDummy) MarshalText() ([]byte, error) { if t.str == "" { return nil, errors.New("no text set") } return []byte(t.str), nil } runtime-0.21.0/values.go000066400000000000000000000007571413666762700151360ustar00rootroot00000000000000package 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 } runtime-0.21.0/values_test.go000066400000000000000000000011421413666762700161620ustar00rootroot00000000000000package runtime import ( "testing" "github.com/stretchr/testify/require" ) func TestGetOK(t *testing.T) { m := make(map[string][]string) m["key1"] = []string{"value1"} m["key2"] = []string{} values := Values(m) v, hasKey, hasValue := values.GetOK("key1") require.Equal(t, []string{"value1"}, v) require.True(t, hasKey) require.True(t, hasValue) v, hasKey, hasValue = values.GetOK("key2") require.Equal(t, []string{}, v) require.True(t, hasKey) require.False(t, hasValue) v, hasKey, hasValue = values.GetOK("key3") require.Nil(t, v) require.False(t, hasKey) require.False(t, hasValue) } runtime-0.21.0/xml.go000066400000000000000000000020471413666762700144310ustar00rootroot00000000000000// 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) }) } runtime-0.21.0/xml_test.go000066400000000000000000000027001413666762700154640ustar00rootroot00000000000000// 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/xml" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) var consProdXML = `Somebody1` func TestXMLConsumer(t *testing.T) { cons := XMLConsumer() var data struct { XMLName xml.Name `xml:"person"` Name string `xml:"name"` ID int `xml:"id"` } err := cons.Consume(bytes.NewBuffer([]byte(consProdXML)), &data) assert.NoError(t, err) assert.Equal(t, "Somebody", data.Name) assert.Equal(t, 1, data.ID) } func TestXMLProducer(t *testing.T) { prod := XMLProducer() data := struct { XMLName xml.Name `xml:"person"` Name string `xml:"name"` ID int `xml:"id"` }{Name: "Somebody", ID: 1} rw := httptest.NewRecorder() err := prod.Produce(rw, data) assert.NoError(t, err) assert.Equal(t, consProdXML, rw.Body.String()) } runtime-0.21.0/yamlpc/000077500000000000000000000000001413666762700145645ustar00rootroot00000000000000runtime-0.21.0/yamlpc/yaml.go000066400000000000000000000021631413666762700160570ustar00rootroot00000000000000// 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 yamlpc import ( "io" "github.com/go-openapi/runtime" "gopkg.in/yaml.v2" ) // YAMLConsumer creates a consumer for yaml data func YAMLConsumer() runtime.Consumer { return runtime.ConsumerFunc(func(r io.Reader, v interface{}) error { dec := yaml.NewDecoder(r) return dec.Decode(v) }) } // YAMLProducer creates a producer for yaml data func YAMLProducer() runtime.Producer { return runtime.ProducerFunc(func(w io.Writer, v interface{}) error { enc := yaml.NewEncoder(w) defer enc.Close() return enc.Encode(v) }) } runtime-0.21.0/yamlpc/yaml_test.go000066400000000000000000000034311413666762700171150ustar00rootroot00000000000000// 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 yamlpc import ( "bytes" "errors" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) var consProdYAML = "name: Somebody\nid: 1\n" func TestYAMLConsumer(t *testing.T) { cons := YAMLConsumer() var data struct { Name string ID int } err := cons.Consume(bytes.NewBuffer([]byte(consProdYAML)), &data) assert.NoError(t, err) assert.Equal(t, "Somebody", data.Name) assert.Equal(t, 1, data.ID) } func TestYAMLProducer(t *testing.T) { prod := YAMLProducer() data := struct { Name string `yaml:"name"` ID int `yaml:"id"` }{Name: "Somebody", ID: 1} rw := httptest.NewRecorder() err := prod.Produce(rw, data) assert.NoError(t, err) assert.Equal(t, consProdYAML, rw.Body.String()) } type failReaderWriter struct { } func (f *failReaderWriter) Read(p []byte) (n int, err error) { return 0, errors.New("expected") } func (f *failReaderWriter) Write(p []byte) (n int, err error) { return 0, errors.New("expected") } func TestFailYAMLWriter(t *testing.T) { prod := YAMLProducer() assert.Error(t, prod.Produce(&failReaderWriter{}, nil)) } func TestFailYAMLReader(t *testing.T) { cons := YAMLConsumer() assert.Error(t, cons.Consume(&failReaderWriter{}, nil)) }