[
  {
    "path": ".github/workflows/tests.yaml",
    "content": "name: Tests\n\non:\n  push:\n    branches: ['*']\n    tags: ['v*']\n  pull_request:\n    branches: ['**']\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    env:\n      GOPATH: ${{ github.workspace }}\n      GOBIN: ${{ github.workspace }}/bin\n    defaults:\n      run:\n        working-directory: ${{ env.GOPATH }}/src/github.com/${{ github.repository }}\n    strategy:\n      matrix:\n        go: [\"stable\", \"oldstable\"]\n        include:\n        - go: \"stable\"\n          latest: true\n          COVERAGE: \"yes\"\n          LINT: \"yes\"\n        - go: \"oldstable\"\n          LINT: \"yes\"\n\n    steps:\n    - name: Setup Go\n      uses: actions/setup-go@v5\n      with:\n        go-version: ${{ matrix.go }}\n\n    - name: Checkout code\n      uses: actions/checkout@v2\n      with:\n        path: ${{ env.GOPATH }}/src/github.com/${{ github.repository }}\n\n    - name: Load cache\n      uses: actions/cache@v1\n      with:\n        path: ~/.glide/cache\n        key: ${{ runner.os }}-go-${{ hashFiles('**/glide.lock') }}\n        restore-keys: |\n          ${{ runner.os }}-go-\n\n    - name: Install CI\n      run: make install_ci\n\n    - name: Test CI\n      env:\n        NO_TEST: ${{ matrix.NO_TEST }}\n      run: test -n \"$NO_TEST\" || make test_ci\n\n    - name: Cover CI\n      env:\n        COVERAGE: ${{ matrix.COVERAGE }}\n      run: test -z \"$COVERAGE\" || make cover_ci\n\n    - name: Lint CI\n      env:\n        LINT: ${{ matrix.LINT }}\n      run: test -z \"$LINT\" || make install_lint lint\n\n    - name: Crossdock CI\n      run: make crossdock_logs_ci\n"
  },
  {
    "path": ".gitignore",
    "content": "build\nGodeps/_workspace\nvendor/\nthrift-gen-release/\n\n# Lint output\nlint.log\n\n# Cover profiles\n*.out\n\n# Editor and OS detritus\n*~\n*.swp\n.DS_Store\n.idea\ntchannel-go.iml\n.vscode\n.bin/\n.idea/\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "Changelog\n=========\n\n## [1.34.6] - 2025-01-07\n### Fixed\n\n * Fix compile issue on FreeBSD 14 (#925)\n\n## [1.34.5] - 2024-10-07\n### Changed\n\n * Add component tag to tracing spans (#923)\n\n## [1.34.4] - 2024-06-26\n### Fixed\n\n * fix getSysConn to work with TLS (#918)\n\n### Changed\n\n * Switch to aliases for Go versions in CI (#919)\n\n## [1.34.3] - 2024-04-23\n### Fixed\n\n * Fix a DoS vulnerability of the vendored apache-thrift library (#915, #916)\n\n## [1.34.2] - 2024-02-16\n### Added\n\n * Expose `inbound.cancels.{requested,honored}` metrics (#912)\n\n## [1.34.1] - 2023-12-11\n### Fixed\n\n * Fix unknown error type in span tag rpc.tchannel.system_error_code (#907)\n\n## [1.34.0] - 2023-10-17\n\n### Added\n\n* Emit the error code and type to the tracing span for inbound calls (#903)\n\n### Changed\n\n * Update go.mod and github test to go 1.21 (#899)\n\n## [1.33.0] - 2023-03-20\n\n### Added\n\n * Optionally send cancelled frames when context is canceled (#890)\n\n### Changed\n\n * Update dependencies as per recommendations (#894)\n * Test against Go 1.19 and 1.20 (#892)\n\n## [1.32.1] - 2022-08-23\n### Fixed\n\n * Release unsent frames when flushing fragments (#887)\n\n## [1.32.0] - 2022-07-12\n### Changed\n * Add TLS option in testutils.NewServerChannel (#882)\n\n## [1.31.0] - 2022-05-04\n### Changed\n * thrift-gen going to use vendored apache-thrift code. Currently vendored apache-thrift pinned to b2a4d4ae21c789b689dd162deb819665567f481c.\n\n## [1.22.3] - 2022-03-28\n### Changed\n * Fix memory leak due to unreturned frames in the relayer.\n * Test against Go 1.17 and 1.18 in CI.\n * Migrate to go mod.\n\n## [1.22.2] - 2021-11-10\n### Changed\n * Fixes related to `SkipHandlerMethods` request handling.\n\n## [1.22.1] - 2021-10-25\n### Changed\n\n * Fixes related to `SkipHandlerMethods` request handling.\n\n## [1.22.0] - 2021-08-13\n### Added\n * Add `SkipHandlerMethods` option as an allow-list of metohds that are handled by the override handler.\n\n### Changed\n * Allow method registration if handler implements Register.\n * Internal changes related to relaying.\n\n## [1.21.2] - 2021-05-19\n### Changed\n\n * Internal changes related to relaying.\n\n## [1.21.1] - 2021-03-17\n### Changed\n * Change log level for connection create/close from info level to debug level to reduce noisy logs.\n\n## [1.21.0] - 2020-12-13\n### Changed\n * Internal changes related to relaying.\n\n## [1.20.1] - 2020-09-24\n### Fixed\n * Set ConnContext in the channel instead of the connection to avoid serialization\n   errors since ConnectionOptions is sometimes embedded in configurations (#806)\n\n## [1.20.0] - 2020-09-23\n### Added\n * Support per-connection base context propagation for inbound/outbound connections (#801)\n\n### Changed\n * Internal API changes related to relaying.\n\n## [1.19.1] - 2020-08-03\n### Fixed\n * Move OS-specific logic into OS-specific files to avoid compile issues on\n   non-Unix platforms.\n\n## [1.19.0] - 2020-05-21\n### Fixed\n * Internal API changes related to relaying.\n\n## [1.18.0] - 2020-03-30\n### Added\n * Introspection now tracks last activity for reads and writes separately (#770)\n * Add options to allow overriding SendBufferSize per process name prefix (#772)\n\n## [1.17.0] - 2020-02-18\n### Added\n * Internal API changes related to relaying.\n\n## [1.16.0] - 2019-10-14\n### Added\n * Support custom Dialer for outbound connections (#759)\n\n### Fixed\n * thrift: Handle TStruct serialization failures gracefully (#744)\n\n## [1.15.0] - 2019-08-26\n### Added\n * introspection: Introspect any channel by ID. (#756)\n\n### Fixed\n * Ensure Introspection endpoints are always available. (#755)\n * Fix testutils.WithTestServer incorrectly using RelayHost when creating the server. (#750)\n\n## [1.14.0] - 2019-05-20\n### Added\n * Expose `CallOptions` caller name for transparent proxying (#741)\n\n## [1.13.0] - 2019-04-04\n### Added\n * Add `MaxCloseTime` which sets a timeout for graceful connection close. (#724)\n\n### Changed\n * Optimize Thrift string field serialization by eliminating `[]byte(string)` allocation. (#729)\n\n### Fixed\n * Return an error if transport header keys/values exceed the  maximum allowed string length. (#728)\n\n## [1.12.0] - 2018-11-13\n### Added\n * Add a channel, `ClosedCh`, to wait for a channel to close. (#718)\n * Add a Code of Conduct. (#711)\n\n### Changed\n * Tweak error message when sending a large error to  mention that we're out of space. (#716)\n * Idle sweeper now skips connections that have pending calls. (#712)\n\n## [1.11.0] - 2018-06-25\n### Added\n * thrift: Support health check type in Health endpoint. (#696)\n\n## [1.10.0] - 2018-04-02\n### Added\n * Support blackholing requests to trigger client timeout without holding\n   on to resources. (#681)\n * introspection: Include channel state in output. (#692)\n * introspection: Add inactive connections to output. (#686)\n\n### Fixed\n * Inherit deadlines from parent context if available, and timeout is\n   unspecified.\n * Ensure outbound tracing headers take precedence over application\n   headers. (#683)\n\n## [1.9.0] - 2018-01-31\n### Added\n * stats: Add tally reporter to emit tagged metrics. (#676)\n * Add optional idle timeout, after which connections will be\n   closed. (#681)\n\n## [1.8.1] - 2017-11-21\n### Fixed\n * Always log addresses as strings. (#669)\n\n## [1.8.0] - 2017-11-06\n### Added\n * Add opt-in active connection health checks. (#318)\n\n### Changed\n * Improve error logging on `thrift.Server` errors. (#663)\n * Reduce memory usage for idle connections. (#658)\n * Unpin and reduce dependencies in `glide.yaml` by using `testImports`. (#649)\n\n### Fixed\n * Don't close connections on ping errors.(#655)\n * Avoid holding on to closed connections' memory in peers. (#644)\n\n## [1.7.0] - 2017-08-04\n### Added\n* Add `WithoutHeaders` to remove TChannel keys from a context.\n\n### Changed\n* Cancel the context on incoming calls if the client connection is closed.\n\n## [1.6.0] - 2017-06-02\n### Added\n* Add `OnPeerStatusChanged` channel option to receive a notification each time\n  the number of available connections changes for any given peer.\n\n### Changed\n* Locks Apache Thrift to version 0.9.3, 0.10.0 to maintain backward-compatibility.\n* Set DiffServ (QoS) bit on outbound connections.\n\n### Fixed\n* Improve resilience of the frame parser.\n\n## [1.5.0] - 2017-03-21\n### Added\n* Add `PeerList.Len` to expose the number of peers in the peer list.\n* Add `PeerList.GetNew` to only return previously unselected peers.\n\n## [1.4.0] - 2017-03-01\n### Added\n* Add version information to the channel's LocalPeerInfo.\n* Add peers package for peer management utilities such as\n  consistent peer selection.\n\n### Fixed\n* Fix SetScoreStrategy not rescoring existing peers. (#583).\n\n## [1.3.0] - 2017-02-01\n### Added\n* Support Thrift namespaces for thrift-gen.\n* Exposes the channel's RootPeerList with `channel.RootPeers()`.\n\n## [1.2.3] - 2017-01-19\n### Changed\n* Improve error messages when an argument reader is closed without\n  reading the EOF. (#567)\n\n### Fixed\n* thrift: Fix an issue where we return `nil` if we expected a Thrift exception\n  but none was found (e.g., exception is from the future). (#566)\n* Fix ListenIP selecting docker interfaces over physical networks. (#565)\n* Fix for error when a Thrift payload has completed decoding and attempts\n  to close the argument reader without waiting until EOF.  (#564)\n* thrift-gen: Fix \"namespace go\" being ignored even though the Apache thrift\n  generated code was respecting it. (#559)\n\n## [1.2.2] - 2016-12-21\n### Added\n* Add a unique channel ID for introspection (#548)\n* Expose local peer information on {Inbound,Outbound}Call (#537)\n* Add remote peer info to connection logger and introspection (#514)\n\n### Fixed\n* Don't drop existing headers on a context when using Wrap(ctx) (#547)\n* Setting response headers is not goroutine safe, allow using a child context\n  for parallel sub-requests (#549).\n* Fix context cancellation not cancelling Dial attempts (#541)\n* Only select active connections for calls (#521)\n* Treat hostPorts ending in \":0\" in the init headers as ephemeral (#513)\n\n## [1.2.1] - 2016-09-29\n### Fixed\n* Fix data race on headers when making concurrent calls using the same context. (#505)\n\n## [1.2.0] - 2016-09-15\n### Added\n* Adds support for routing keys (the TChannel rk transport header).\n\n## [1.1.0] - 2016-08-25\n### Added\n* Integrate OpenTracing for distributed tracing and context propagation.\n  As long as a Zipkin-style tracing is configured, TChannel frames still\n  send tracing information, and `CurrentSpan(ctx)` works as before.\n  All tracer configuration must be handled through OpenTracing.\n  (#426)\n\n### Changed\n* Improve error messages when using the json package and the host:port\n  fails to connect. (#475)\n* mockhyperbahn now using inbuilt TChannel relaying to implement in-process\n  forwarding. (#472)\n* Drop go1.4 support and add support for go1.7.\n* Pass thrift.Context to the thrift.Server's response callback (#465)\n\n## [1.0.9] - 2016-07-20\n### Added\n* Expose meta endpoints on the \"tchannel\" service name. (#459)\n* Add Go version and tchannel-go library version to introspection. (#457)\n* Expose the number of connections on a channel. (#451)\n\n### Changed\n* Better handling of peers where dialed host:port doesn't match the remote\n  connection's reported host:port. (#452)\n\n## [1.0.8] - 2016-07-15\n### Fixed\n* Remove dependency on \"testing\" from \"tchannel-go\" introduced in v1.0.7.\n\n## [1.0.7] - 2016-07-15\n\n### Added\n* Add CallOptions() to IncomingCall which can be used as the call option\n  when making outbound calls to proxy all transport headers.\n* Add tracing information to all error frames generated by the library.\n* Add GetHandlers for getting all registered methods on a subchannel.\n* Expose the peer information for outbound calls.\n* Support a separate connection timeout from the context timeout, useful for\n  streaming calls where the stream timeout may be much longer than the\n  connection timeout.\n\n### Fixed\n* Fix peer score not being calculated when adding a new outbound connections\n\n## [1.0.6] - 2016-06-16\n### Fixed\n* Fix trace span encoding fields in the wrong order\n\n## [1.0.5] - 2016-04-04\n### Changed\n* Use `context.Context` storage for headers so `thrift.Context` and\n  `tchannel.ContextWithHeaders` can be passed to functions that use\n  `context.Context`, and have them retain headers.\n* `thrift.Server` allows a custom factory to be used for `thrift.Context`\n  creation based on the underlying `context.Context` and headers map.\n* Store goroutine stack traces on channel creation that can be accessed\n  via introspection.\n\n## [1.0.4] - 2016-03-09\n### Added\n* #228: Add registered methods to the introspection output.\n* Add ability to set a global handler for a SubChannel.\n\n### Fixed\n* Improve handling of network failures during pending calls. Previously, calls\n  would timeout, but now they fail as soon as the network failure is detected.\n* Remove ephemeral peers with closed outbound connections.\n* #233: Ensure errors returned from Thrift handlers have a non-nil value.\n\n# 1.0.3 (2016-02-15)\n\n### Added\n* Introspection now includes information about all channels created\n  in the current process.\n\n### Changed\n* Improved performance when writing Thrift structs\n* Make closing message exchanges less disruptive, changes a panic due to\n  closing a channel twice to an error log.\n\n## [1.0.2] - 2016-01-29\n### Changed\n* Extend the `ContextBuilder` API to support setting the transport-level\n  routing delegate header.\n* Assorted logging and test improvements.\n\n### Fixed\n* Set a timeout when making new outbound connections to avoid hanging.\n* Fix for #196: Make the initial Hyperbahn advertise more tolerant of transient\n  timeouts.\n\n## [1.0.1] - 2016-01-19\n### Added\n* Peers can now be removed using PeerList.Remove.\n* Add ErrorHandlerFunc to create raw handlers that return errors.\n* Retries try to avoid previously selected hosts, rather than just the\n  host:port.\n* Create an ArgReader interface (which is an alias for io.ReadCloser) for\n  symmetry with ArgWriter.\n* Add ArgReadable and ArgWritable interfaces for the common methods between\n  calls and responses.\n* Expose Thrift binary encoding methods (thrift.ReadStruct, thrift.WriteStruct,\n  thrift.ReadHeaders, thrift.WriteHeaders) so callers can easily send Thrift\n  payloads over the streaming interface.\n\n### Fixed\n* Bug fix for #181: Shuffle peers on PeerList.Add to avoid biases in peer\n  selection.\n\n## 1.0.0 - 2016-01-11\n### Added\n* First stable release.\n* Support making calls with JSON, Thrift or raw payloads.\n* Services use thrift-gen, and implement handlers with a `func(ctx, arg) (res,\n  error)` signature.\n* Support retries.\n* Peer selection (peer heap, prefer incoming strategy, for use with Hyperbahn).\n* Graceful channel shutdown.\n* TCollector trace reporter with sampling support.\n* Metrics collection with StatsD.\n* Thrift support, including includes.\n\n[//]: # (Version Links)\n[1.34.6]: https://github.com/uber/tchannel-go/compare/v1.34.5...v1.34.6\n[1.34.5]: https://github.com/uber/tchannel-go/compare/v1.34.4...v1.34.5\n[1.34.4]: https://github.com/uber/tchannel-go/compare/v1.34.3...v1.34.4\n[1.34.3]: https://github.com/uber/tchannel-go/compare/v1.34.2...v1.34.3\n[1.34.2]: https://github.com/uber/tchannel-go/compare/v1.34.1...v1.34.2\n[1.34.1]: https://github.com/uber/tchannel-go/compare/v1.34.0...v1.34.1\n[1.34.0]: https://github.com/uber/tchannel-go/compare/v1.33.0...v1.34.0\n[1.33.0]: https://github.com/uber/tchannel-go/compare/v1.32.1...v1.33.0\n[1.32.1]: https://github.com/uber/tchannel-go/compare/v1.32.0...v1.32.1\n[1.32.0]: https://github.com/uber/tchannel-go/compare/v1.31.0...v1.32.0\n[1.31.0]: https://github.com/uber/tchannel-go/compare/v1.22.3...v1.31.0\n[1.22.3]: https://github.com/uber/tchannel-go/compare/v1.22.2...v1.22.3\n[1.22.1]: https://github.com/uber/tchannel-go/compare/v1.22.0...v1.22.1\n[1.22.0]: https://github.com/uber/tchannel-go/compare/v1.21.2...v1.22.0\n[1.21.2]: https://github.com/uber/tchannel-go/compare/v1.21.1...v1.21.2\n[1.21.1]: https://github.com/uber/tchannel-go/compare/v1.21.0...v1.21.1\n[1.21.0]: https://github.com/uber/tchannel-go/compare/v1.20.1...v1.21.0\n[1.20.1]: https://github.com/uber/tchannel-go/compare/v1.20.0...v1.20.1\n[1.20.0]: https://github.com/uber/tchannel-go/compare/v1.19.1...v1.20.0\n[1.18.0]: https://github.com/uber/tchannel-go/compare/v1.17.0...v1.18.0\n[1.17.0]: https://github.com/uber/tchannel-go/compare/v1.16.0...v1.17.0\n[1.16.0]: https://github.com/uber/tchannel-go/compare/v1.15.0...v1.16.0\n[1.15.0]: https://github.com/uber/tchannel-go/compare/v1.14.0...v1.15.0\n[1.14.0]: https://github.com/uber/tchannel-go/compare/v1.13.0...v1.14.0\n[1.13.0]: https://github.com/uber/tchannel-go/compare/v1.12.0...v1.13.0\n[1.12.0]: https://github.com/uber/tchannel-go/compare/v1.11.0...v1.12.0\n[1.11.0]: https://github.com/uber/tchannel-go/compare/v1.10.0...v1.11.0\n[1.10.0]: https://github.com/uber/tchannel-go/compare/v1.9.0...v1.10.0\n[1.9.0]: https://github.com/uber/tchannel-go/compare/v1.8.1...v1.9.0\n[1.8.1]: https://github.com/uber/tchannel-go/compare/v1.8.0...v1.8.1\n[1.8.0]: https://github.com/uber/tchannel-go/compare/v1.7.0...v1.8.0\n[1.7.0]: https://github.com/uber/tchannel-go/compare/v1.6.0...v1.7.0\n[1.6.0]: https://github.com/uber/tchannel-go/compare/v1.5.0...v1.6.0\n[1.5.0]: https://github.com/uber/tchannel-go/compare/v1.4.0...v1.5.0\n[1.4.0]: https://github.com/uber/tchannel-go/compare/v1.3.0...v1.4.0\n[1.3.0]: https://github.com/uber/tchannel-go/compare/v1.2.3...v1.3.0\n[1.2.3]: https://github.com/uber/tchannel-go/compare/v1.2.2...v1.2.3\n[1.2.2]: https://github.com/uber/tchannel-go/compare/v1.2.1...v1.2.2\n[1.2.1]: https://github.com/uber/tchannel-go/compare/v1.2.0...v1.2.1\n[1.2.0]: https://github.com/uber/tchannel-go/compare/v1.1.0...v1.2.0\n[1.1.0]: https://github.com/uber/tchannel-go/compare/v1.0.9...v1.1.0\n[1.0.9]: https://github.com/uber/tchannel-go/compare/v1.0.8...v1.0.9\n[1.0.8]: https://github.com/uber/tchannel-go/compare/v1.0.7...v1.0.8\n[1.0.7]: https://github.com/uber/tchannel-go/compare/v1.0.6...v1.0.7\n[1.0.6]: https://github.com/uber/tchannel-go/compare/v1.0.5...v1.0.6\n[1.0.5]: https://github.com/uber/tchannel-go/compare/v1.0.4...v1.0.5\n[1.0.4]: https://github.com/uber/tchannel-go/compare/v1.0.2...v1.0.4\n[1.0.2]: https://github.com/uber/tchannel-go/compare/v1.0.1...v1.0.2\n[1.0.1]: https://github.com/uber/tchannel-go/compare/v1.0.0...v1.0.1\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age,\nbody size, disability, ethnicity, gender identity and expression, level of\nexperience, nationality, personal appearance, race, religion, or sexual\nidentity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an\nappointed representative at an online or offline event. Representation of a\nproject may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at oss-conduct@uber.com. The project\nteam will review and investigate all complaints, and will respond in a way\nthat it deems appropriate to the circumstances. The project team is obligated\nto maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 1.4, available at\n[http://contributor-covenant.org/version/1/4][version].\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Contributing\n============\n\nWe'd love your help making tchannel-go great!\n\n## Getting Started\n\nTChannel uses [glide](https://github.com/Masterminds/glide) to manage\ndependencies.\nTo get started:\n\n```bash\ngo get github.com/uber/tchannel-go\nmake install_glide\nmake  # tests should pass\n```\n\n## Making A Change\n\n*Before making any significant changes, please [open an\nissue](https://github.com/uber/tchannel-go/issues).* Discussing your proposed\nchanges ahead of time will make the contribution process smooth for everyone.\n\nOnce we've discussed your changes and you've got your code ready, make sure\nthat tests are passing (`make test` or `make cover`) and open your PR! Your\npull request is most likely to be accepted if it:\n\n* Includes tests for new functionality.\n* Follows the guidelines in [Effective\n  Go](https://golang.org/doc/effective_go.html) and the [Go team's common code\n  review comments](https://github.com/golang/go/wiki/CodeReviewComments).\n* Has a [good commit\n  message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).\n\n## Cutting a Release\n\n* Send a pull request against dev including:\n  * update CHANGELOG.md (`scripts/changelog_halp.sh`)\n  * update version.go\n* Send a pull request for dev into master\n* `git tag -m v0.0.0 -a v0.0.0`\n* `git push origin --tags`\n* Copy CHANGELOG.md fragment into release notes on\n  https://github.com/uber/tchannel-go/releases \n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright (c) 2015 Uber Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "PATH := $(GOPATH)/bin:$(PATH)\nEXAMPLES=./examples/bench/server ./examples/bench/client ./examples/ping ./examples/thrift ./examples/hyperbahn/echo-server\nALL_PKGS := $(shell go list ./... | grep -v 'thirdparty')\nPROD_PKGS := . ./http ./hyperbahn ./json ./peers ./pprof ./raw ./relay ./stats ./thrift $(EXAMPLES)\nTEST_ARG ?= -race -v -timeout 10m\nCOV_PKG ?= ./\nBUILD := ./build\nTHRIFT_GEN_RELEASE := ./thrift-gen-release\nTHRIFT_GEN_RELEASE_LINUX := $(THRIFT_GEN_RELEASE)/linux-x86_64\nTHRIFT_GEN_RELEASE_DARWIN := $(THRIFT_GEN_RELEASE)/darwin-x86_64\n\nPLATFORM := $(shell uname -s | tr '[:upper:]' '[:lower:]')\nARCH := $(shell uname -m)\n\nBIN := $(shell pwd)/.bin\n\n# Cross language test args\nTEST_HOST=127.0.0.1\nTEST_PORT=0\n\n-include crossdock/rules.mk\n\nall: lint test examples\n\n$(BIN)/thrift:\n\tmkdir -p $(BIN)\n\tscripts/install-thrift.sh $(BIN)\n\npackages_test:\n\tgo list -json ./... | jq -r '. | select ((.TestGoFiles | length) > 0)  | .ImportPath'\n\nsetup:\n\tmkdir -p $(BUILD)\n\tmkdir -p $(BUILD)/examples\n\tmkdir -p $(THRIFT_GEN_RELEASE_LINUX)\n\tmkdir -p $(THRIFT_GEN_RELEASE_DARWIN)\n\ninstall:\n\tgo mod vendor\n\ninstall_lint:\n\t@echo \"Installing golint, since we expect to lint\"\n\tgo install golang.org/x/lint/golint@latest\n\ninstall_ci: $(BIN)/thrift install\nifdef CROSSDOCK\n\t$(MAKE) install_docker_ci\nendif\n\nhelp:\n\t@egrep \"^# target:\" [Mm]akefile | sort -\n\nclean:\n\techo Cleaning build artifacts...\n\tgo clean\n\trm -rf $(BUILD) $(THRIFT_GEN_RELEASE)\n\techo\n\nfmt format:\n\techo Formatting Packages...\n\tgo fmt $(ALL_PKGS)\n\techo\n\ntest_ci:\nifdef CROSSDOCK\n\t$(MAKE) crossdock_ci\nelse\n\t$(MAKE) test\nendif\n\ntest: clean setup check_no_test_deps $(BIN)/thrift\n\t$(MAKE) test_vanilla\n\t$(MAKE) test_relay_frame_leaks\n\n# test_vanilla runs all unit tests without checking for frame leaks\ntest_vanilla:\n\t@echo Testing packages:\n\tPATH=$(BIN):$$PATH DISABLE_FRAME_POOLING_CHECKS=1 go test -parallel=4 $(TEST_ARG) $(ALL_PKGS)\n\t@echo Running frame pool tests\n\tPATH=$(BIN):$$PATH go test -run TestFramesReleased -stressTest $(TEST_ARG)\n\n# test_relay_frame_leaks runs unit tests in relay_test.go with frame leak checks enabled\ntest_relay_frame_leaks:\n\t@echo Testing relay frame leaks\n\tPATH=$(BIN):$$PATH go test -parallel=4 $(TEST_ARG) relay_test.go\n\ncheck_no_test_deps:\n\t! go list -json $(PROD_PKGS) | jq -r '.Deps | select ((. | length) > 0) | .[]' | grep -e test -e mock | grep -v '^internal/testlog'\n\nbenchmark: clean setup $(BIN)/thrift\n\techo Running benchmarks:\n\tPATH=$(BIN)::$$PATH go test $(ALL_PKGS) -bench=. -cpu=1 -benchmem -run NONE\n\ncover_profile: clean setup $(BIN)/thrift\n\t@echo Testing packages:\n\tmkdir -p $(BUILD)\n\tPATH=$(BIN)::$$PATH DISABLE_FRAME_POOLING_CHECKS=1 go test $(COV_PKG) $(TEST_ARG) -coverprofile=$(BUILD)/coverage.out\n\ncover: cover_profile\n\tgo tool cover -html=$(BUILD)/coverage.out\n\ncover_ci:\n\t@echo \"Uploading coverage\"\n\t$(MAKE) cover_profile\n\tcurl -s https://codecov.io/bash > $(BUILD)/codecov.bash\n\tbash $(BUILD)/codecov.bash -f $(BUILD)/coverage.out\n\n\nFILTER := grep -v -e '_string.go' -e '/gen-go/' -e '/mocks/' -e 'vendor/' -e 'thirdparty'\nlint: install\n\t@echo \"Running golint\"\n\t-golint $(ALL_PKGS) | $(FILTER) | tee lint.log\n\t@echo \"Running go vet\"\n\t-go vet $(ALL_PKGS) 2>&1 | $(FILTER) | fgrep -v -e \"possible formatting directiv\" -e \"exit status\" | tee -a lint.log\n\t@echo \"Verifying files are gofmt'd\"\n\t-gofmt -l . | $(FILTER) | tee -a lint.log\n\t@echo \"Checking for unresolved FIXMEs\"\n\t-git grep -i -n fixme | $(FILTER) | grep -v -e Makefile | tee -a lint.log\n\t@[ ! -s lint.log ]\n\nthrift_example: thrift_gen\n\tgo build -o $(BUILD)/examples/thrift       ./examples/thrift/main.go\n\ntest_server:\n\t./build/examples/test_server --host ${TEST_HOST} --port ${TEST_PORT}\n\nexamples: clean setup thrift_example\n\techo Building examples...\n\tmkdir -p $(BUILD)/examples/ping $(BUILD)/examples/bench\n\tgo build -o $(BUILD)/examples/ping/pong    ./examples/ping/main.go\n\tgo build -o $(BUILD)/examples/hyperbahn/echo-server    ./examples/hyperbahn/echo-server/main.go\n\tgo build -o $(BUILD)/examples/bench/server ./examples/bench/server\n\tgo build -o $(BUILD)/examples/bench/client ./examples/bench/client\n\tgo build -o $(BUILD)/examples/bench/runner ./examples/bench/runner.go\n\tgo build -o $(BUILD)/examples/test_server ./examples/test_server\n\nthrift_gen: $(BIN)/thrift\n\tgo build -o $(BUILD)/thrift-gen ./thrift/thrift-gen\n\tPATH=$(BIN):$$PATH $(BUILD)/thrift-gen --generateThrift --inputFile thrift/test.thrift --outputDir thrift/gen-go/\n\tPATH=$(BIN):$$PATH $(BUILD)/thrift-gen --generateThrift --inputFile examples/keyvalue/keyvalue.thrift --outputDir examples/keyvalue/gen-go\n\tPATH=$(BIN):$$PATH $(BUILD)/thrift-gen --generateThrift --inputFile examples/thrift/example.thrift --outputDir examples/thrift/gen-go\n\tPATH=$(BIN):$$PATH $(BUILD)/thrift-gen --generateThrift --inputFile hyperbahn/hyperbahn.thrift --outputDir hyperbahn/gen-go\n\nrelease_thrift_gen: clean setup\n\tGOOS=linux GOARCH=amd64 go build -o $(THRIFT_GEN_RELEASE_LINUX)/thrift-gen ./thrift/thrift-gen\n\tGOOS=darwin GOARCH=amd64 go build -o $(THRIFT_GEN_RELEASE_DARWIN)/thrift-gen ./thrift/thrift-gen\n\ttar -czf thrift-gen-release.tar.gz $(THRIFT_GEN_RELEASE)\n\tmv thrift-gen-release.tar.gz $(THRIFT_GEN_RELEASE)/\n\n.PHONY: all help clean fmt format install install_ci install_lint release_thrift_gen packages_test check_no_test_deps test test_ci lint\n.SILENT: all help clean fmt format test lint\n"
  },
  {
    "path": "README.md",
    "content": "# TChannel [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]\n\n[TChannel][tchan-spec] is a multiplexing and framing protocol for RPC calls.\ntchannel-go is a Go implementation of the protocol, including client libraries\nfor [Hyperbahn][hyperbahn].\n\nIf you'd like to start by writing a small Thrift and TChannel service, check\nout [this guide](guide/Thrift_Hyperbahn.md). For a less opinionated setup, see\nthe [contribution guidelines](CONTRIBUTING.md).\n\n## Overview\n\nTChannel is a network protocol that supports:\n\n * A request/response model,\n * Multiplexing multiple requests across the same TCP socket,\n * Out-of-order responses,\n * Streaming requests and responses,\n * Checksummed frames,\n * Transport of arbitrary payloads,\n * Easy implementation in many languages, and\n * Redis-like performance.\n\nThis protocol is intended to run on datacenter networks for inter-process\ncommunication.\n\n## Protocol\n\nTChannel frames have a fixed-length header and 3 variable-length fields. The\nunderlying protocol does not assign meaning to these fields, but the included\nclient/server implementation uses the first field to represent a unique\nendpoint or function name in an RPC model.  The next two fields can be used for\narbitrary data. Some suggested way to use the 3 fields are:\n\n* URI path + HTTP method and headers as JSON + body, or\n* Function name + headers + thrift/protobuf.\n\nNote, however, that the only encoding supported by TChannel is UTF-8.  If you\nwant JSON, you'll need to stringify and parse outside of TChannel.\n\nThis design supports efficient routing and forwarding: routers need to parse\nthe first or second field, but can forward the third field without parsing.\n\nThere is no notion of client and server in this system. Every TChannel instance\nis capable of making and receiving requests, and thus requires a unique port on\nwhich to listen. This requirement may change in the future.\n\nSee the [protocol specification][tchan-proto-spec] for more details.\n\n## Examples\n\n - [ping](examples/ping): A simple ping/pong example using raw TChannel.\n - [thrift](examples/thrift): A Thrift server/client example.\n - [keyvalue](examples/keyvalue): A keyvalue Thrift service with separate server and client binaries.\n\n<hr>\nThis project is released under the [MIT License](LICENSE.md).\n\n[doc-img]: https://godoc.org/github.com/uber/tchannel-go?status.svg\n[doc]: https://godoc.org/github.com/uber/tchannel-go\n[ci-img]: https://github.com/uber/tchannel-go/actions/workflows/tests.yaml/badge.svg?branch=master\n[ci]: https://github.com/uber/tchannel-go/actions/workflows/tests.yaml\n[cov-img]: https://coveralls.io/repos/uber/tchannel-go/badge.svg?branch=master&service=github\n[cov]: https://coveralls.io/github/uber/tchannel-go?branch=master\n[tchan-spec]: http://tchannel.readthedocs.org/en/latest/\n[tchan-proto-spec]: http://tchannel.readthedocs.org/en/latest/protocol/\n[hyperbahn]: https://github.com/uber/hyperbahn\n"
  },
  {
    "path": "RELEASE.md",
    "content": "Release process\n===============\n\nThis document outlines how to create a release of tchannel-go\n\n1.  Set up some environment variables for use later.\n\n    ```\n    # This is the version being released.\n    $ VERSION=1.8.0\n    ```\n\n2.  Make sure you have the latest dev and create a branch off it.\n\n    ```\n    $ git checkout dev\n    $ git pull\n    $ git checkout -b release\n    ```\n\n3.  Update the `CHANGELOG.md` and `version.go` files.\n\n    ```\n    $ go run ./scripts/vbumper/main.go --version $VERSION\n    ```\n\n4.  Clean up the `CHANGELOG.md` to only mention noteworthy changes for users.\n\n5.  Commit changes and create a PR against `dev` to prepare for release.\n\n6.  Once the release PR has been accepted, run the following to release.\n\n    ```\n    $ git checkout master\n    $ git pull\n    $ git merge dev\n    $ git tag -a \"v$VERSION\" -m \"v$VERSION\"\n    $ git push origin master v$VERSION\n    ```\n\n7.  Go to <https://github.com/uber/tchannel-go/tags> and edit the release notes.\n    Copy changelog entries for this release and set the name to `v$VERSION`.\n\n8.  Switch back to development.\n\n    ```\n    $ git checkout dev\n    $ git merge master\n    $ go run ./scripts/vbumper/main.go --version ${VERSION}-dev --skip-changelog\n    $ git commit -am \"Back to development\"\n    $ git push\n    ```\n"
  },
  {
    "path": "all_channels.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\n// channelMap is used to ensure that applications don't create multiple channels with\n// the same service name in a single process.\nvar channelMap = struct {\n\tsync.Mutex\n\texisting map[string][]*Channel\n}{\n\texisting: make(map[string][]*Channel),\n}\n\nfunc registerNewChannel(ch *Channel) {\n\tserviceName := ch.ServiceName()\n\tch.createdStack = string(getStacks(false /* all */))\n\tch.log.WithFields(\n\t\tLogField{\"channelPtr\", fmt.Sprintf(\"%p\", ch)},\n\t\tLogField{\"createdStack\", ch.createdStack},\n\t).Info(\"Created new channel.\")\n\n\tchannelMap.Lock()\n\tdefer channelMap.Unlock()\n\n\texisting := channelMap.existing[serviceName]\n\tchannelMap.existing[serviceName] = append(existing, ch)\n}\n\nfunc removeClosedChannel(ch *Channel) {\n\tchannelMap.Lock()\n\tdefer channelMap.Unlock()\n\n\tchannels := channelMap.existing[ch.ServiceName()]\n\tfor i, v := range channels {\n\t\tif v != ch {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Replace current index with the last element, and truncate channels.\n\t\tchannels[i] = channels[len(channels)-1]\n\t\tchannels = channels[:len(channels)-1]\n\t\tbreak\n\t}\n\n\tchannelMap.existing[ch.ServiceName()] = channels\n}\n\nfunc findChannelByID(id uint32) (*Channel, bool) {\n\tchannelMap.Lock()\n\tdefer channelMap.Unlock()\n\n\tfor _, channels := range channelMap.existing {\n\t\tfor _, ch := range channels {\n\t\t\tif ch.chID == id {\n\t\t\t\treturn ch, true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, false\n}\n"
  },
  {
    "path": "all_channels_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestAllChannelsRegistered(t *testing.T) {\n\tintrospectOpts := &IntrospectionOptions{IncludeOtherChannels: true}\n\n\tch1_1, err := NewChannel(\"ch1\", nil)\n\trequire.NoError(t, err, \"Channel create failed\")\n\tch1_2, err := NewChannel(\"ch1\", nil)\n\trequire.NoError(t, err, \"Channel create failed\")\n\tch2_1, err := NewChannel(\"ch2\", nil)\n\trequire.NoError(t, err, \"Channel create failed\")\n\n\tstate := ch1_1.IntrospectState(introspectOpts)\n\tassert.Equal(t, 1, len(state.OtherChannels[\"ch1\"]))\n\tassert.Equal(t, 1, len(state.OtherChannels[\"ch2\"]))\n\n\tch1_2.Close()\n\n\tstate = ch1_1.IntrospectState(introspectOpts)\n\tassert.Equal(t, 0, len(state.OtherChannels[\"ch1\"]))\n\tassert.Equal(t, 1, len(state.OtherChannels[\"ch2\"]))\n\n\tch2_2, err := NewChannel(\"ch2\", nil)\n\n\tstate = ch1_1.IntrospectState(introspectOpts)\n\trequire.NoError(t, err, \"Channel create failed\")\n\tassert.Equal(t, 0, len(state.OtherChannels[\"ch1\"]))\n\tassert.Equal(t, 2, len(state.OtherChannels[\"ch2\"]))\n\n\tch1_1.Close()\n\tch2_1.Close()\n\tch2_2.Close()\n\n\tstate = ch1_1.IntrospectState(introspectOpts)\n\tassert.Equal(t, 0, len(state.OtherChannels[\"ch1\"]))\n\tassert.Equal(t, 0, len(state.OtherChannels[\"ch2\"]))\n}\n"
  },
  {
    "path": "arguments.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"bufio\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"io/ioutil\"\n\n\t\"github.com/uber/tchannel-go/internal/argreader\"\n)\n\n// ArgReader is the interface for the arg2 and arg3 streams on an\n// OutboundCallResponse and an InboundCall\ntype ArgReader io.ReadCloser\n\n// ArgWriter is the interface for the arg2 and arg3 streams on an OutboundCall\n// and an InboundCallResponse\ntype ArgWriter interface {\n\tio.WriteCloser\n\n\t// Flush flushes the currently written bytes without waiting for the frame\n\t// to be filled.\n\tFlush() error\n}\n\n// ArgWritable is an interface for providing arg2 and arg3 writer streams;\n// implemented by reqResWriter e.g. OutboundCall and InboundCallResponse\ntype ArgWritable interface {\n\tArg2Writer() (ArgWriter, error)\n\tArg3Writer() (ArgWriter, error)\n}\n\n// ArgReadable is an interface for providing arg2 and arg3 reader streams;\n// implemented by reqResReader e.g. InboundCall and OutboundCallResponse.\ntype ArgReadable interface {\n\tArg2Reader() (ArgReader, error)\n\tArg3Reader() (ArgReader, error)\n}\n\n// ArgReadHelper providers a simpler interface to reading arguments.\ntype ArgReadHelper struct {\n\treader ArgReader\n\terr    error\n}\n\n// NewArgReader wraps the result of calling ArgXReader to provide a simpler\n// interface for reading arguments.\nfunc NewArgReader(reader ArgReader, err error) ArgReadHelper {\n\treturn ArgReadHelper{reader, err}\n}\n\nfunc (r ArgReadHelper) read(f func() error) error {\n\tif r.err != nil {\n\t\treturn r.err\n\t}\n\tif err := f(); err != nil {\n\t\treturn err\n\t}\n\tif err := argreader.EnsureEmpty(r.reader, \"read arg\"); err != nil {\n\t\treturn err\n\t}\n\treturn r.reader.Close()\n}\n\n// Read reads from the reader into the byte slice.\nfunc (r ArgReadHelper) Read(bs *[]byte) error {\n\treturn r.read(func() error {\n\t\tvar err error\n\t\t*bs, err = ioutil.ReadAll(r.reader)\n\t\treturn err\n\t})\n}\n\n// ReadJSON deserializes JSON from the underlying reader into data.\nfunc (r ArgReadHelper) ReadJSON(data interface{}) error {\n\treturn r.read(func() error {\n\t\t// TChannel allows for 0 length values (not valid JSON), so we use a bufio.Reader\n\t\t// to check whether data is of 0 length.\n\t\treader := bufio.NewReader(r.reader)\n\t\tif _, err := reader.Peek(1); err == io.EOF {\n\t\t\t// If the data is 0 length, then we don't try to read anything.\n\t\t\treturn nil\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\td := json.NewDecoder(reader)\n\t\treturn d.Decode(data)\n\t})\n}\n\n// ArgWriteHelper providers a simpler interface to writing arguments.\ntype ArgWriteHelper struct {\n\twriter io.WriteCloser\n\terr    error\n}\n\n// NewArgWriter wraps the result of calling ArgXWriter to provider a simpler\n// interface for writing arguments.\nfunc NewArgWriter(writer io.WriteCloser, err error) ArgWriteHelper {\n\treturn ArgWriteHelper{writer, err}\n}\n\nfunc (w ArgWriteHelper) write(f func() error) error {\n\tif w.err != nil {\n\t\treturn w.err\n\t}\n\n\tif err := f(); err != nil {\n\t\treturn err\n\t}\n\n\treturn w.writer.Close()\n}\n\n// Write writes the given bytes to the underlying writer.\nfunc (w ArgWriteHelper) Write(bs []byte) error {\n\treturn w.write(func() error {\n\t\t_, err := w.writer.Write(bs)\n\t\treturn err\n\t})\n}\n\n// WriteJSON writes the given object as JSON.\nfunc (w ArgWriteHelper) WriteJSON(data interface{}) error {\n\treturn w.write(func() error {\n\t\te := json.NewEncoder(w.writer)\n\t\treturn e.Encode(data)\n\t})\n}\n"
  },
  {
    "path": "arguments_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype bufferWithClose struct {\n\t*bytes.Buffer\n\tclosed bool\n}\n\nvar _ io.WriteCloser = &bufferWithClose{}\nvar _ io.ReadCloser = &bufferWithClose{}\n\nfunc newWriter() *bufferWithClose {\n\treturn &bufferWithClose{bytes.NewBuffer(nil), false}\n}\n\nfunc newReader(bs []byte) *bufferWithClose {\n\treturn &bufferWithClose{bytes.NewBuffer(bs), false}\n}\n\nfunc (w *bufferWithClose) Close() error {\n\tw.closed = true\n\treturn nil\n}\n\ntype testObject struct {\n\tName  string `json:\"name\"`\n\tValue int    `json:\"value\"`\n}\n\nfunc TestJSONInputOutput(t *testing.T) {\n\tobj := testObject{Name: \"Foo\", Value: 20756}\n\n\twriter := newWriter()\n\trequire.Nil(t, NewArgWriter(writer, nil).WriteJSON(obj))\n\tassert.True(t, writer.closed)\n\tassert.Equal(t, \"{\\\"name\\\":\\\"Foo\\\",\\\"value\\\":20756}\\n\", writer.String())\n\n\treader := newReader(writer.Bytes())\n\toutObj := testObject{}\n\trequire.Nil(t, NewArgReader(reader, nil).ReadJSON(&outObj))\n\n\tassert.True(t, reader.closed)\n\tassert.Equal(t, \"Foo\", outObj.Name)\n\tassert.Equal(t, 20756, outObj.Value)\n}\n\nfunc TestReadNotEmpty(t *testing.T) {\n\t// Note: The contents need to be larger than the default buffer size of bufio.NewReader.\n\tr := bytes.NewReader([]byte(\"{}\" + strings.Repeat(\"{}\\n\", 10000)))\n\n\tvar data map[string]interface{}\n\treader := NewArgReader(ioutil.NopCloser(r), nil)\n\trequire.Error(t, reader.ReadJSON(&data), \"Read should fail due to extra bytes\")\n}\n\nfunc BenchmarkArgReaderWriter(b *testing.B) {\n\tobj := testObject{Name: \"Foo\", Value: 20756}\n\toutObj := testObject{}\n\n\tfor i := 0; i < b.N; i++ {\n\t\twriter := newWriter()\n\t\tNewArgWriter(writer, nil).WriteJSON(obj)\n\t\treader := newReader(writer.Bytes())\n\t\tNewArgReader(reader, nil).ReadJSON(&outObj)\n\t}\n\n\tb.StopTimer()\n\tassert.Equal(b, obj, outObj)\n}\n"
  },
  {
    "path": "benchmark/benchclient/main.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// benchclient is used to make requests to a specific server.\npackage main\n\nimport (\n\t\"bufio\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/benchmark\"\n)\n\nvar (\n\tserviceName = flag.String(\"service\", \"bench-server\", \"The benchmark server's service name\")\n\ttimeout     = flag.Duration(\"timeout\", time.Second, \"Timeout for each request\")\n\trequestSize = flag.Int(\"request-size\", 10000, \"The number of bytes of each request\")\n\tnoLibrary   = flag.Bool(\"no-library\", false, \"Whether to use the template based library instead of TChannel's client library\")\n\tnumClients  = flag.Int(\"num-clients\", 1, \"Number of concurrent clients to run in process\")\n\tnoDurations = flag.Bool(\"no-durations\", false, \"Disable printing of latencies to stdout\")\n)\n\nfunc main() {\n\tflag.Parse()\n\n\topts := []benchmark.Option{\n\t\tbenchmark.WithServiceName(*serviceName),\n\t\tbenchmark.WithTimeout(*timeout),\n\t\tbenchmark.WithRequestSize(*requestSize),\n\t\tbenchmark.WithNumClients(*numClients),\n\t}\n\tif *noLibrary {\n\t\topts = append(opts, benchmark.WithNoLibrary())\n\t}\n\n\tclient := benchmark.NewClient(flag.Args(), opts...)\n\tfmt.Println(\"bench-client started\")\n\n\trdr := bufio.NewScanner(os.Stdin)\n\tfor rdr.Scan() {\n\t\tline := rdr.Text()\n\t\tparts := strings.Split(line, \" \")\n\t\tvar n int\n\t\tvar err error\n\t\tif len(parts) >= 2 {\n\t\t\tn, err = strconv.Atoi(parts[1])\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatalf(\"unrecognized number %q: %v\", parts[1], err)\n\t\t\t}\n\t\t}\n\n\t\tswitch cmd := parts[0]; cmd {\n\t\tcase \"warmup\":\n\t\t\tif err := client.Warmup(); err != nil {\n\t\t\t\tlog.Fatalf(\"warmup failed: %v\", err)\n\t\t\t}\n\t\t\tfmt.Println(\"success\")\n\t\t\tcontinue\n\t\tcase \"rcall\":\n\t\t\tmakeCalls(n, client.RawCall)\n\t\tcase \"tcall\":\n\t\t\tmakeCalls(n, client.ThriftCall)\n\t\tcase \"quit\":\n\t\t\treturn\n\t\tdefault:\n\t\t\tlog.Fatalf(\"unrecognized command: %v\", line)\n\t\t}\n\t}\n\n\tif err := rdr.Err(); err != nil {\n\t\tlog.Fatalf(\"Reader failed: %v\", err)\n\t}\n}\n\nfunc makeCalls(n int, f func(n int) ([]time.Duration, error)) {\n\tdurations, err := f(n)\n\tif err != nil {\n\t\tlog.Fatalf(\"Call failed: %v\", err)\n\t}\n\tif !*noDurations {\n\t\tfor i, d := range durations {\n\t\t\tif i > 0 {\n\t\t\t\tfmt.Printf(\" \")\n\t\t\t}\n\t\t\tfmt.Printf(\"%v\", d)\n\t\t}\n\t}\n\tfmt.Println()\n}\n"
  },
  {
    "path": "benchmark/benchserver/main.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// benchserver is used to receive requests for benchmarks.\npackage main\n\nimport (\n\t\"bufio\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/uber/tchannel-go/benchmark\"\n)\n\nvar (\n\tserviceName    = flag.String(\"service\", \"bench-server\", \"The benchmark server's service name\")\n\tadvertiseHosts = flag.String(\"advertise-hosts\", \"\", \"Comma-separated list of hosts to advertise to\")\n)\n\nfunc main() {\n\tflag.Parse()\n\n\tvar adHosts []string\n\tif len(*advertiseHosts) > 0 {\n\t\tadHosts = strings.Split(*advertiseHosts, \",\")\n\t}\n\n\tserver := benchmark.NewServer(\n\t\tbenchmark.WithServiceName(*serviceName),\n\t\tbenchmark.WithAdvertiseHosts(adHosts),\n\t)\n\n\tfmt.Println(server.HostPort())\n\n\trdr := bufio.NewReader(os.Stdin)\n\tfor {\n\t\tline, err := rdr.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Fatalf(\"stdin read failed: %v\", err)\n\t\t}\n\n\t\tline = strings.TrimSuffix(line, \"\\n\")\n\t\tswitch line {\n\t\tcase \"count-raw\":\n\t\t\tfmt.Println(server.RawCalls())\n\t\tcase \"count-thrift\":\n\t\t\tfmt.Println(server.ThriftCalls())\n\t\tcase \"quit\":\n\t\t\treturn\n\t\tdefault:\n\t\t\tlog.Fatalf(\"unrecognized command: %v\", line)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "benchmark/build_manager.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"os/exec\"\n\t\"sync\"\n)\n\ntype buildManager struct {\n\tsync.RWMutex\n\tbuilds map[string]*build\n}\n\ntype build struct {\n\tonce     sync.Once\n\tmainFile string\n\n\tbinaryFile string\n\tbuildErr   error\n}\n\nfunc newBuildManager() *buildManager {\n\treturn &buildManager{\n\t\tbuilds: make(map[string]*build),\n\t}\n}\n\nfunc (m *buildManager) GoBinary(mainFile string) (string, error) {\n\tm.Lock()\n\tbld, ok := m.builds[mainFile]\n\tif !ok {\n\t\tbld = &build{mainFile: mainFile}\n\t\tm.builds[mainFile] = bld\n\t}\n\tm.Unlock()\n\n\tbld.once.Do(bld.Build)\n\treturn bld.binaryFile, bld.buildErr\n}\n\nfunc (b *build) Build() {\n\ttempFile, err := ioutil.TempFile(\"\", \"bench\")\n\tif err != nil {\n\t\tpanic(\"Failed to create temp file: \" + err.Error())\n\t}\n\ttempFile.Close()\n\n\tbuildCmd := exec.Command(\"go\", \"build\", \"-o\", tempFile.Name(), b.mainFile)\n\tbuildCmd.Stdout = os.Stdout\n\tbuildCmd.Stderr = os.Stderr\n\tif err := buildCmd.Run(); err != nil {\n\t\tb.buildErr = err\n\t\treturn\n\t}\n\n\tb.binaryFile = tempFile.Name()\n}\n"
  },
  {
    "path": "benchmark/client_server_bench_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"log\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc BenchmarkServer(b *testing.B) {\n\tserver := NewServer()\n\n\tclient := NewClient([]string{server.HostPort()},\n\t\tWithExternalProcess(),\n\t\tWithNoLibrary(),\n\t\tWithNumClients(10),\n\t\tWithNoDurations(),\n\t\tWithTimeout(10*time.Second),\n\t)\n\n\tassert.NoError(b, client.Warmup(), \"Warmup failed\")\n\n\tb.ResetTimer()\n\tstarted := time.Now()\n\t_, err := client.RawCall(b.N)\n\ttotal := time.Since(started)\n\tassert.NoError(b, err, \"client.RawCall failed\")\n\n\tif n := server.RawCalls(); b.N > n {\n\t\tb.Errorf(\"Server received %v calls, expected at least %v calls\", n, b.N)\n\t}\n\tlog.Printf(\"Calls: %v Duration: %v RPS: %.0f\", b.N, total, float64(b.N)/total.Seconds())\n}\n\nfunc BenchmarkClient(b *testing.B) {\n\tservers := make([]Server, 3)\n\tserverHosts := make([]string, len(servers))\n\tfor i := range servers {\n\t\tservers[i] = NewServer(\n\t\t\tWithExternalProcess(),\n\t\t\tWithNoLibrary(),\n\t\t)\n\t\tserverHosts[i] = servers[i].HostPort()\n\t}\n\n\t// To saturate a single process, we need to have multiple clients.\n\tclient := NewClient(serverHosts,\n\t\tWithNoChecking(),\n\t\tWithNumClients(10),\n\t)\n\trequire.NoError(b, client.Warmup(), \"Warmup failed\")\n\n\tb.ResetTimer()\n\tstarted := time.Now()\n\tif _, err := client.RawCall(b.N); err != nil {\n\t\tb.Fatalf(\"Call failed: %v\", err)\n\t}\n\ttotal := time.Since(started)\n\tlog.Printf(\"Calls: %v Duration: %v RPS: %.0f\", b.N, total, float64(b.N)/total.Seconds())\n}\n"
  },
  {
    "path": "benchmark/external_client.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// externalClient represents a benchmark client running out-of-process.\ntype externalClient struct {\n\t*externalCmd\n\topts *options\n}\n\nfunc newExternalClient(hosts []string, opts *options) Client {\n\tbenchArgs := []string{\n\t\t\"--service\", opts.svcName,\n\t\t\"--timeout\", opts.timeout.String(),\n\t\t\"--request-size\", strconv.Itoa(opts.reqSize),\n\t\t\"--num-clients\", strconv.Itoa(opts.numClients),\n\t}\n\tif opts.noDurations {\n\t\tbenchArgs = append(benchArgs, \"--no-durations\")\n\t}\n\tif opts.noLibrary {\n\t\tbenchArgs = append(benchArgs, \"--no-library\")\n\t}\n\tbenchArgs = append(benchArgs, hosts...)\n\n\tcmd, initial := newExternalCmd(\"benchclient/main.go\", benchArgs)\n\tif !strings.Contains(initial, \"started\") {\n\t\tpanic(\"bench-client did not start, got: \" + initial)\n\t}\n\n\treturn &externalClient{cmd, opts}\n}\n\nfunc (c *externalClient) Warmup() error {\n\tout, err := c.writeAndRead(\"warmup\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif out != \"success\" {\n\t\treturn fmt.Errorf(\"warmup failed: %v\", out)\n\t}\n\treturn nil\n}\n\nfunc (c *externalClient) callAndParse(cmd string) ([]time.Duration, error) {\n\tout, err := c.writeAndRead(cmd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif out == \"\" {\n\t\treturn nil, nil\n\t}\n\n\tdurationStrs := strings.Split(out, \" \")\n\tdurations := make([]time.Duration, len(durationStrs))\n\tfor i, s := range durationStrs {\n\t\td, err := time.ParseDuration(s)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"calls failed: %v\", out)\n\t\t}\n\n\t\tdurations[i] = d\n\t}\n\n\treturn durations, nil\n}\n\nfunc (c *externalClient) RawCall(n int) ([]time.Duration, error) {\n\treturn c.callAndParse(fmt.Sprintf(\"rcall %v\", n))\n}\n\nfunc (c *externalClient) ThriftCall(n int) ([]time.Duration, error) {\n\treturn c.callAndParse(fmt.Sprintf(\"tcall %v\", n))\n}\n"
  },
  {
    "path": "benchmark/external_common.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n)\n\nvar _bm = newBuildManager()\n\n// externalCmd handles communication with an external benchmark client.\ntype externalCmd struct {\n\tcmd        *exec.Cmd\n\tstdoutOrig io.ReadCloser\n\tstdout     *bufio.Scanner\n\tstdin      io.WriteCloser\n}\n\nfunc newExternalCmd(mainFile string, benchArgs []string) (*externalCmd, string) {\n\tbin, err := _bm.GoBinary(BenchmarkDir + mainFile)\n\tif err != nil {\n\t\tpanic(\"failed to compile \" + mainFile + \": \" + err.Error())\n\t}\n\n\tcmd := exec.Command(bin, benchArgs...)\n\tcmd.Stderr = os.Stderr\n\tstdout, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\tpanic(\"failed to create stdout: \" + err.Error())\n\t}\n\n\tstdin, err := cmd.StdinPipe()\n\tif err != nil {\n\t\tpanic(\"failed to create stdin: \" + err.Error())\n\t}\n\n\tif err := cmd.Start(); err != nil {\n\t\tpanic(\"failed to start external process: \" + err.Error())\n\t}\n\n\tstdoutScanner := bufio.NewScanner(stdout)\n\tif !stdoutScanner.Scan() {\n\t\tpanic(\"failed to check if external process started: \" + err.Error())\n\t}\n\n\tout := stdoutScanner.Text()\n\treturn &externalCmd{\n\t\tcmd:        cmd,\n\t\tstdin:      stdin,\n\t\tstdout:     stdoutScanner,\n\t\tstdoutOrig: stdout,\n\t}, out\n}\n\nfunc (c *externalCmd) writeAndRead(cmd string) (string, error) {\n\tif _, err := io.WriteString(c.stdin, cmd+\"\\n\"); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif c.stdout.Scan() {\n\t\treturn c.stdout.Text(), nil\n\t}\n\n\treturn \"\", c.stdout.Err()\n}\n\nfunc (c *externalCmd) Close() {\n\tc.stdin.Close()\n\tc.stdoutOrig.Close()\n\tc.cmd.Process.Kill()\n}\n"
  },
  {
    "path": "benchmark/external_server.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// externalServer represents a benchmark server running out-of-process.\ntype externalServer struct {\n\t*externalCmd\n\thostPort string\n\topts     *options\n}\n\nfunc newExternalServer(opts *options) Server {\n\tbenchArgs := []string{\n\t\t\"--service\", opts.svcName,\n\t}\n\tif len(opts.advertiseHosts) > 0 {\n\t\tbenchArgs = append(benchArgs,\n\t\t\t\"--advertise-hosts\", strings.Join(opts.advertiseHosts, \",\"))\n\t}\n\n\tcmd, hostPortStr := newExternalCmd(\"benchserver/main.go\", benchArgs)\n\tif _, _, err := net.SplitHostPort(hostPortStr); err != nil {\n\t\tpanic(\"bench-server did not print host:port on startup: \" + err.Error())\n\t}\n\n\treturn &externalServer{cmd, hostPortStr, opts}\n}\n\nfunc (s *externalServer) HostPort() string {\n\treturn s.hostPort\n}\n\nfunc (s *externalServer) RawCalls() int {\n\treturn s.writeAndReadInt(\"count-raw\")\n}\n\nfunc (s *externalServer) ThriftCalls() int {\n\treturn s.writeAndReadInt(\"count-thrift\")\n}\n\nfunc (s *externalServer) writeAndReadInt(cmd string) int {\n\tv, err := s.writeAndRead(cmd)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tvInt, err := strconv.Atoi(v)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn vInt\n}\n"
  },
  {
    "path": "benchmark/frame_templates.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n)\n\nconst (\n\t_idOffset    = 4             /* size (2) + type (1) + reserved (1) */\n\t_idOffsetEnd = _idOffset + 4 /* length */\n)\n\ntype frames struct {\n\toutgoing [][]byte\n\tincoming [][]byte\n}\n\nfunc (f frames) duplicate() frames {\n\treturn frames{\n\t\toutgoing: deepCopyByteSlice(f.outgoing),\n\t\tincoming: deepCopyByteSlice(f.incoming),\n\t}\n}\n\nfunc deepCopyByteSlice(bs [][]byte) [][]byte {\n\tnewBs := make([][]byte, len(bs))\n\tfor i, b := range bs {\n\t\tnewBs[i] = make([]byte, len(b))\n\t\tcopy(newBs[i], b)\n\t}\n\treturn newBs\n}\n\nfunc (f frames) writeInitReq(w io.Writer) error {\n\t_, err := w.Write(f.outgoing[0])\n\treturn err\n}\n\nfunc (f frames) writeInitRes(w io.Writer) error {\n\t_, err := w.Write(f.incoming[0])\n\treturn err\n}\n\nfunc (f frames) writeCallReq(id uint32, w io.Writer) (int, error) {\n\tframes := f.outgoing[1:]\n\treturn f.writeMulti(id, w, frames)\n}\n\nfunc (f frames) writeCallRes(id uint32, w io.Writer) (int, error) {\n\tframes := f.incoming[1:]\n\treturn f.writeMulti(id, w, frames)\n}\n\nfunc (f frames) writeMulti(id uint32, w io.Writer, frames [][]byte) (int, error) {\n\twritten := 0\n\tfor _, f := range frames {\n\t\tbinary.BigEndian.PutUint32(f[_idOffset:_idOffsetEnd], id)\n\t\tif _, err := w.Write(f); err != nil {\n\t\t\treturn written, err\n\t\t}\n\t\twritten++\n\t}\n\n\treturn written, nil\n}\n\nfunc getRawCallFrames(timeout time.Duration, svcName string, reqSize int) frames {\n\tvar fs frames\n\tmodifier := func(fromClient bool, f *tchannel.Frame) *tchannel.Frame {\n\t\tbuf := &bytes.Buffer{}\n\t\tif err := f.WriteOut(buf); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tif fromClient {\n\t\t\tfs.outgoing = append(fs.outgoing, buf.Bytes())\n\t\t} else {\n\t\t\tfs.incoming = append(fs.incoming, buf.Bytes())\n\t\t}\n\n\t\treturn f\n\t}\n\n\twithNewServerClient(svcName, func(server, client *tchannel.Channel) {\n\t\ttestutils.RegisterEcho(server, nil)\n\n\t\trelay, err := NewTCPFrameRelay([]string{server.PeerInfo().HostPort}, modifier)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdefer relay.Close()\n\n\t\targs := &raw.Args{\n\t\t\tArg2: getRequestBytes(reqSize),\n\t\t\tArg3: getRequestBytes(reqSize),\n\t\t}\n\n\t\tctx, cancel := tchannel.NewContext(timeout)\n\t\tdefer cancel()\n\n\t\tif _, _, _, err := raw.Call(ctx, client, relay.HostPort(), svcName, \"echo\", args.Arg2, args.Arg3); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t})\n\n\treturn fs\n}\n\nfunc withNewServerClient(svcName string, f func(server, client *tchannel.Channel)) {\n\topts := testutils.NewOpts().SetServiceName(svcName)\n\tserver, err := testutils.NewServerChannel(opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer server.Close()\n\n\tclient, err := testutils.NewClientChannel(opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer client.Close()\n\n\tf(server, client)\n}\n"
  },
  {
    "path": "benchmark/interfaces.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport \"time\"\n\n// BenchmarkDir should be set to the benchmark source directory.\nvar BenchmarkDir = \"./\"\n\n// Client is a benchmark client that can be used to call a benchmark server.\ntype Client interface {\n\t// Warmup will create connections to all host:ports the client was created with.\n\tWarmup() error\n\n\t// RawCall makes an echo call using raw.\n\tRawCall(n int) ([]time.Duration, error)\n\n\t// ThriftCall makes an echo call using thrift.\n\tThriftCall(n int) ([]time.Duration, error)\n\n\t// Close closes the benchmark client.\n\tClose()\n}\n\n// inProcClient represents a client that is running in the same process.\n// It adds methods to reduce allocations.\ntype inProcClient interface {\n\tClient\n\n\t// RawCallBuffer will make n raw calls and store the latencies in the specified buffer.\n\tRawCallBuffer(latencies []time.Duration) error\n\n\t// ThriftCallBuffer will make n thrift calls and store the latencies in the specified buffer.\n\tThriftCallBuffer(latencies []time.Duration) error\n}\n\n// Server is a benchmark server that can receive requests.\ntype Server interface {\n\t// HostPort returns the HostPort that the server is listening on.\n\tHostPort() string\n\n\t// Close closes the benchmark server.\n\tClose()\n\n\t// RawCalls returns the number of raw calls the server has received.\n\tRawCalls() int\n\n\t// ThriftCalls returns the number of Thrift calls the server has received.\n\tThriftCalls() int\n}\n\n// Relay represents a relay for benchmarking.\ntype Relay interface {\n\t// HostPort is the host:port that the relay is listening on.\n\tHostPort() string\n\n\t// Close clsoes the relay.\n\tClose()\n}\n"
  },
  {
    "path": "benchmark/internal_client.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/thrift\"\n\tgen \"github.com/uber/tchannel-go/thrift/gen-go/test\"\n)\n\n// internalClient represents a benchmark client.\ntype internalClient struct {\n\tch          *tchannel.Channel\n\tsc          *tchannel.SubChannel\n\ttClient     gen.TChanSecondService\n\targStr      string\n\targBytes    []byte\n\tcheckResult bool\n\topts        *options\n}\n\n// NewClient returns a new Client that can make calls to a benchmark server.\nfunc NewClient(hosts []string, optFns ...Option) Client {\n\topts := getOptions(optFns)\n\tif opts.external {\n\t\treturn newExternalClient(hosts, opts)\n\t}\n\tif opts.numClients > 1 {\n\t\treturn newInternalMultiClient(hosts, opts)\n\t}\n\treturn newClient(hosts, opts)\n}\n\nfunc newClient(hosts []string, opts *options) inProcClient {\n\tif opts.external || opts.numClients > 1 {\n\t\tpanic(\"newClient got options that should be handled by NewClient\")\n\t}\n\n\tif opts.noLibrary {\n\t\treturn newInternalTCPClient(hosts, opts)\n\t}\n\treturn newInternalClient(hosts, opts)\n}\n\nfunc newInternalClient(hosts []string, opts *options) inProcClient {\n\tch, err := tchannel.NewChannel(opts.svcName, &tchannel.ChannelOptions{\n\t\tLogger: tchannel.NewLevelLogger(tchannel.NewLogger(os.Stderr), tchannel.LogLevelWarn),\n\t})\n\tif err != nil {\n\t\tpanic(\"failed to create channel: \" + err.Error())\n\t}\n\tfor _, host := range hosts {\n\t\tch.Peers().Add(host)\n\t}\n\tthriftClient := thrift.NewClient(ch, opts.svcName, nil)\n\tclient := gen.NewTChanSecondServiceClient(thriftClient)\n\n\treturn &internalClient{\n\t\tch:       ch,\n\t\tsc:       ch.GetSubChannel(opts.svcName),\n\t\ttClient:  client,\n\t\targBytes: getRequestBytes(opts.reqSize),\n\t\targStr:   getRequestString(opts.reqSize),\n\t\topts:     opts,\n\t}\n}\n\nfunc (c *internalClient) Warmup() error {\n\tfor _, peer := range c.ch.Peers().Copy() {\n\t\tctx, cancel := tchannel.NewContext(c.opts.timeout)\n\t\t_, err := peer.GetConnection(ctx)\n\t\tcancel()\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c *internalClient) makeCalls(latencies []time.Duration, f func() (time.Duration, error)) error {\n\tfor i := range latencies {\n\t\tvar err error\n\t\tlatencies[i], err = f()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *internalClient) RawCallBuffer(latencies []time.Duration) error {\n\treturn c.makeCalls(latencies, func() (time.Duration, error) {\n\t\tctx, cancel := tchannel.NewContext(c.opts.timeout)\n\t\tdefer cancel()\n\n\t\tstarted := time.Now()\n\t\trArg2, rArg3, _, err := raw.CallSC(ctx, c.sc, \"echo\", c.argBytes, c.argBytes)\n\t\tduration := time.Since(started)\n\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif c.checkResult {\n\t\t\tif !bytes.Equal(rArg2, c.argBytes) || !bytes.Equal(rArg3, c.argBytes) {\n\t\t\t\tfmt.Println(\"Arg2\", rArg2, \"Expect\", c.argBytes)\n\t\t\t\tfmt.Println(\"Arg3\", rArg3, \"Expect\", c.argBytes)\n\t\t\t\tpanic(\"echo call returned wrong results\")\n\t\t\t}\n\t\t}\n\t\treturn duration, nil\n\t})\n}\n\nfunc (c *internalClient) RawCall(n int) ([]time.Duration, error) {\n\tlatencies := make([]time.Duration, n)\n\treturn latencies, c.RawCallBuffer(latencies)\n}\n\nfunc (c *internalClient) ThriftCallBuffer(latencies []time.Duration) error {\n\treturn c.makeCalls(latencies, func() (time.Duration, error) {\n\t\tctx, cancel := thrift.NewContext(c.opts.timeout)\n\t\tdefer cancel()\n\n\t\tstarted := time.Now()\n\t\tres, err := c.tClient.Echo(ctx, c.argStr)\n\t\tduration := time.Since(started)\n\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif c.checkResult {\n\t\t\tif res != c.argStr {\n\t\t\t\tpanic(\"thrift Echo returned wrong result\")\n\t\t\t}\n\t\t}\n\t\treturn duration, nil\n\t})\n}\n\nfunc (c *internalClient) ThriftCall(n int) ([]time.Duration, error) {\n\tlatencies := make([]time.Duration, n)\n\treturn latencies, c.ThriftCallBuffer(latencies)\n}\n\nfunc (c *internalClient) Close() {\n\tc.ch.Close()\n}\n"
  },
  {
    "path": "benchmark/internal_multi_client.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/testutils\"\n)\n\ntype internalMultiClient struct {\n\tclients []inProcClient\n}\n\nfunc newInternalMultiClient(hosts []string, opts *options) Client {\n\tclients := make([]inProcClient, opts.numClients)\n\topts.numClients = 1\n\n\tfor i := range clients {\n\t\tclients[i] = newClient(hosts, opts)\n\t}\n\n\treturn &internalMultiClient{clients: clients}\n}\n\nfunc (c *internalMultiClient) Warmup() error {\n\tfor _, c := range c.clients {\n\t\tif err := c.Warmup(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *internalMultiClient) Close() {\n\tfor _, client := range c.clients {\n\t\tclient.Close()\n\t}\n}\n\nfunc (c *internalMultiClient) RawCall(n int) ([]time.Duration, error) {\n\treturn c.makeCalls(n, func(c inProcClient) callFunc {\n\t\treturn c.RawCallBuffer\n\t})\n}\n\nfunc (c *internalMultiClient) ThriftCall(n int) ([]time.Duration, error) {\n\treturn c.makeCalls(n, func(c inProcClient) callFunc {\n\t\treturn c.ThriftCallBuffer\n\t})\n}\n\ntype callFunc func([]time.Duration) error\n\ntype clientToCallFunc func(c inProcClient) callFunc\n\nfunc (c *internalMultiClient) makeCalls(n int, f clientToCallFunc) ([]time.Duration, error) {\n\tbuckets := testutils.Buckets(n, len(c.clients))\n\terrCs := make([]chan error, len(c.clients))\n\n\tvar start int\n\tlatencies := make([]time.Duration, n)\n\tfor i := range c.clients {\n\t\tcalls := buckets[i]\n\n\t\tend := start + calls\n\t\terrCs[i] = c.callUsingClient(latencies[start:end], f(c.clients[i]))\n\t\tstart = end\n\t}\n\n\tfor _, errC := range errCs {\n\t\tif err := <-errC; err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn latencies, nil\n}\n\nfunc (c *internalMultiClient) callUsingClient(latencies []time.Duration, f callFunc) chan error {\n\terrC := make(chan error, 1)\n\tif len(latencies) == 0 {\n\t\terrC <- nil\n\t\treturn errC\n\t}\n\n\tgo func() {\n\t\terrC <- f(latencies)\n\t}()\n\treturn errC\n}\n"
  },
  {
    "path": "benchmark/internal_server.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/hyperbahn\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/thrift\"\n\tgen \"github.com/uber/tchannel-go/thrift/gen-go/test\"\n\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\n// internalServer represents a benchmark server.\ntype internalServer struct {\n\tch          *tchannel.Channel\n\thc          *hyperbahn.Client\n\topts        *options\n\trawCalls    atomic.Int64\n\tthriftCalls atomic.Int64\n}\n\n// NewServer returns a new Server that can recieve Thrift calls or raw calls.\nfunc NewServer(optFns ...Option) Server {\n\topts := getOptions(optFns)\n\tif opts.external {\n\t\treturn newExternalServer(opts)\n\t}\n\n\tch, err := tchannel.NewChannel(opts.svcName, &tchannel.ChannelOptions{\n\t\tLogger: tchannel.NewLevelLogger(tchannel.NewLogger(os.Stderr), tchannel.LogLevelWarn),\n\t})\n\tif err != nil {\n\t\tpanic(\"failed to create channel: \" + err.Error())\n\t}\n\tif err := ch.ListenAndServe(\"127.0.0.1:0\"); err != nil {\n\t\tpanic(\"failed to listen on port 0: \" + err.Error())\n\t}\n\n\ts := &internalServer{\n\t\tch:   ch,\n\t\topts: opts,\n\t}\n\n\ttServer := thrift.NewServer(ch)\n\ttServer.Register(gen.NewTChanSecondServiceServer(handler{calls: &s.thriftCalls}))\n\tch.Register(raw.Wrap(rawHandler{calls: &s.rawCalls}), \"echo\")\n\n\tif len(opts.advertiseHosts) > 0 {\n\t\tif err := s.Advertise(opts.advertiseHosts); err != nil {\n\t\t\tpanic(\"failed to advertise: \" + err.Error())\n\t\t}\n\t}\n\n\treturn s\n}\n\n// HostPort returns the host:port that the server is listening on.\nfunc (s *internalServer) HostPort() string {\n\treturn s.ch.PeerInfo().HostPort\n}\n\n// Advertise advertises with Hyperbahn.\nfunc (s *internalServer) Advertise(hyperbahnHosts []string) error {\n\tvar err error\n\tconfig := hyperbahn.Configuration{InitialNodes: hyperbahnHosts}\n\ts.hc, err = hyperbahn.NewClient(s.ch, config, nil)\n\tif err != nil {\n\t\tpanic(\"failed to setup Hyperbahn client: \" + err.Error())\n\t}\n\treturn s.hc.Advertise()\n}\n\nfunc (s *internalServer) Close() {\n\ts.ch.Close()\n\tif s.hc != nil {\n\t\ts.hc.Close()\n\t}\n}\n\nfunc (s *internalServer) RawCalls() int {\n\treturn int(s.rawCalls.Load())\n}\n\nfunc (s *internalServer) ThriftCalls() int {\n\treturn int(s.thriftCalls.Load())\n}\n\ntype rawHandler struct {\n\tcalls *atomic.Int64\n}\n\nfunc (rawHandler) OnError(ctx context.Context, err error) {\n\tfmt.Println(\"benchmark.Server error:\", err)\n}\n\nfunc (h rawHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\th.calls.Inc()\n\treturn &raw.Res{\n\t\tArg2: args.Arg2,\n\t\tArg3: args.Arg3,\n\t}, nil\n}\n\ntype handler struct {\n\tcalls *atomic.Int64\n}\n\nfunc (h handler) Echo(ctx thrift.Context, arg1 string) (string, error) {\n\th.calls.Inc()\n\treturn arg1, nil\n}\n"
  },
  {
    "path": "benchmark/internal_tcp_client.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n)\n\n// internalTCPClient represents a TCP client that makes\n// TChannel calls using raw TCP packets.\ntype internalTCPClient struct {\n\thost        string\n\tlastID      uint32\n\tresponseIDs chan uint32\n\tconn        net.Conn\n\tframes      frames\n\topts        *options\n}\n\nfunc newInternalTCPClient(hosts []string, opts *options) inProcClient {\n\treturn &internalTCPClient{\n\t\thost:        hosts[rand.Intn(len(hosts))],\n\t\tresponseIDs: make(chan uint32, 1000),\n\t\tframes:      getRawCallFrames(opts.timeout, opts.svcName, opts.reqSize),\n\t\tlastID:      1,\n\t\topts:        opts,\n\t}\n}\n\nfunc (c *internalTCPClient) Warmup() error {\n\tconn, err := net.Dial(\"tcp\", c.host)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.conn = conn\n\tgo c.readConn()\n\n\tif err := c.frames.writeInitReq(conn); err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn nil\n}\n\nfunc (c *internalTCPClient) readConn() {\n\tdefer close(c.responseIDs)\n\n\twantFirstID := true\n\tf := tchannel.NewFrame(tchannel.MaxFrameSize)\n\tfor {\n\t\terr := f.ReadIn(c.conn)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif wantFirstID {\n\t\t\tif f.Header.ID != 1 {\n\t\t\t\tpanic(fmt.Errorf(\"Expected first response ID to be 1, got %v\", f.Header.ID))\n\t\t\t}\n\t\t\twantFirstID = false\n\t\t\tcontinue\n\t\t}\n\n\t\tc.responseIDs <- f.Header.ID\n\t}\n}\n\ntype call struct {\n\tid        uint32\n\tstarted   time.Time\n\tnumFrames int\n}\n\nfunc (c *internalTCPClient) makeCalls(latencies []time.Duration, f func() (call, error)) error {\n\tn := len(latencies)\n\tcalls := make(map[uint32]*call, n)\n\n\tfor i := 0; i < n; i++ {\n\t\tc, err := f()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tcalls[c.id] = &c\n\t}\n\n\ttimer := time.NewTimer(c.opts.timeout)\n\n\t// Use the original underlying slice for latencies.\n\tdurations := latencies[:0]\n\tfor {\n\t\tif len(calls) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\ttimer.Reset(c.opts.timeout)\n\t\tselect {\n\t\tcase id, ok := <-c.responseIDs:\n\t\t\tif !ok {\n\t\t\t\tpanic(\"expecting more calls, but connection is closed\")\n\t\t\t}\n\t\t\tcall, ok := calls[id]\n\t\t\tif !ok {\n\t\t\t\tpanic(fmt.Errorf(\"received unexpected response frame: %v\", id))\n\t\t\t}\n\n\t\t\tcall.numFrames--\n\t\t\tif call.numFrames != 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdurations = append(durations, time.Since(call.started))\n\t\t\tdelete(calls, id)\n\t\tcase <-timer.C:\n\t\t\treturn tchannel.ErrTimeout\n\t\t}\n\t}\n}\n\nfunc (c *internalTCPClient) RawCallBuffer(latencies []time.Duration) error {\n\treturn c.makeCalls(latencies, func() (call, error) {\n\t\tc.lastID++\n\n\t\tstarted := time.Now()\n\t\tnumFrames, err := c.frames.writeCallReq(c.lastID, c.conn)\n\t\tif err != nil {\n\t\t\treturn call{}, err\n\t\t}\n\n\t\treturn call{c.lastID, started, numFrames}, nil\n\t})\n}\n\nfunc (c *internalTCPClient) RawCall(n int) ([]time.Duration, error) {\n\tlatencies := make([]time.Duration, n)\n\treturn latencies, c.RawCallBuffer(latencies)\n}\n\nfunc (c *internalTCPClient) ThriftCallBuffer(latencies []time.Duration) error {\n\tpanic(\"not yet implemented\")\n}\n\nfunc (c *internalTCPClient) ThriftCall(n int) ([]time.Duration, error) {\n\tpanic(\"not yet implemented\")\n}\n\nfunc (c *internalTCPClient) Close() {\n\tc.conn.Close()\n}\n"
  },
  {
    "path": "benchmark/internal_tcp_server.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"log\"\n\t\"net\"\n\n\t\"github.com/uber/tchannel-go\"\n\n\t\"go.uber.org/atomic\"\n)\n\n// internalTCPServer represents a TCP server responds to TChannel\n// calls using raw TCP packets.\ntype internalTCPServer struct {\n\tframes   frames\n\tln       net.Listener\n\topts     *options\n\trawCalls atomic.Int64\n}\n\nfunc newInternalTCPServer(opts *options) Server {\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\ts := &internalTCPServer{\n\t\tln:     ln,\n\t\tframes: getRawCallFrames(opts.timeout, opts.svcName, opts.reqSize),\n\t\topts:   opts,\n\t}\n\tgo s.acceptLoop()\n\treturn s\n}\n\nfunc (s *internalTCPServer) acceptLoop() {\n\tfor {\n\t\tconn, err := s.ln.Accept()\n\t\tif err, ok := err.(net.Error); ok && err.Temporary() {\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tgo s.handleConn(conn)\n\t}\n}\n\nfunc (s *internalTCPServer) handleConn(conn net.Conn) {\n\tc := make(chan uint32, 1000)\n\tdefer close(c)\n\tgo s.writeResponses(conn, c)\n\n\tvar lastID uint32\n\n\tf := tchannel.NewFrame(tchannel.MaxFrameSize)\n\tfor {\n\t\tif err := f.ReadIn(conn); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif f.Header.ID > lastID {\n\t\t\tc <- f.Header.ID\n\t\t\tlastID = f.Header.ID\n\t\t}\n\t}\n}\n\nfunc (s *internalTCPServer) writeResponses(conn net.Conn, ids chan uint32) {\n\tframes := s.frames.duplicate()\n\n\tfor id := range ids {\n\t\tif id == 1 {\n\t\t\tif err := frames.writeInitRes(conn); err != nil {\n\t\t\t\tlog.Printf(\"writeInitRes failed: %v\", err)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\ts.rawCalls.Inc()\n\t\tif _, err := frames.writeCallRes(id, conn); err != nil {\n\t\t\tlog.Printf(\"writeCallRes failed: %v\", err)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (s *internalTCPServer) HostPort() string {\n\treturn s.ln.Addr().String()\n}\n\nfunc (s *internalTCPServer) RawCalls() int {\n\treturn int(s.rawCalls.Load())\n}\n\nfunc (s *internalTCPServer) ThriftCalls() int {\n\t// Server does not support Thrift calls currently.\n\treturn 0\n}\n\nfunc (s *internalTCPServer) Close() {\n\ts.ln.Close()\n}\n"
  },
  {
    "path": "benchmark/matrix_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/uber/tchannel-go\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// combinations will call f with every combination of selecting elements\n// from a slice with the specified length.\n// e.g. for 2, the callback would be:\n// f(false, false)\n// f(false, true)\n// f(true, false)\n// f(true, true)\nfunc combinations(length int, f func([]bool)) {\n\tcur := make([]bool, length)\n\ttoGenerate := (1 << uint(length))\n\n\tf(cur)\n\tfor i := 0; i < toGenerate-1; i++ {\n\t\tvar digit int\n\t\tfor digit = length - 1; cur[digit]; digit-- {\n\t\t\tcur[digit] = false\n\t\t}\n\t\tcur[digit] = true\n\t\tf(cur)\n\t}\n}\n\nfunc TestCombinations(t *testing.T) {\n\ttests := []struct {\n\t\tlength int\n\t\twant   [][]bool\n\t}{\n\t\t{\n\t\t\tlength: 1,\n\t\t\twant:   [][]bool{{false}, {true}},\n\t\t},\n\t\t{\n\t\t\tlength: 2,\n\t\t\twant:   [][]bool{{false, false}, {false, true}, {true, false}, {true, true}},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tvar got [][]bool\n\t\trecordCombs := func(comb []bool) {\n\t\t\tcopied := append([]bool(nil), comb...)\n\t\t\tgot = append(got, copied)\n\t\t}\n\t\tcombinations(tt.length, recordCombs)\n\t\tassert.Equal(t, tt.want, got, \"Mismatch for combinations of length %v\", tt.length)\n\t}\n}\n\nfunc selectOptions(options []Option, toSelect []bool) []Option {\n\tvar opts []Option\n\tfor i, v := range toSelect {\n\t\tif v {\n\t\t\topts = append(opts, options[i])\n\t\t}\n\t}\n\treturn opts\n}\n\nfunc combineOpts(base, override []Option) []Option {\n\tresultOpts := append([]Option(nil), base...)\n\treturn append(resultOpts, override...)\n}\n\nfunc runSingleTest(t *testing.T, baseOpts, serverOpts, clientOpts []Option) {\n\tserverOpts = combineOpts(baseOpts, serverOpts)\n\tclientOpts = combineOpts(baseOpts, clientOpts)\n\n\tmsgP := fmt.Sprintf(\"%+v: \", struct {\n\t\tserverOpts options\n\t\tclientOpts options\n\t}{*(getOptions(serverOpts)), *(getOptions(clientOpts))})\n\n\tserver := NewServer(serverOpts...)\n\tdefer server.Close()\n\n\tclient := NewClient([]string{server.HostPort()}, clientOpts...)\n\tdefer client.Close()\n\n\trequire.NoError(t, client.Warmup(), msgP+\"Client warmup failed\")\n\n\tdurations, err := client.RawCall(0)\n\trequire.NoError(t, err, msgP+\"Call(0) failed\")\n\tassert.Equal(t, 0, len(durations), msgP+\"Wrong number of calls\")\n\n\tassert.Equal(t, 0, server.RawCalls(), msgP+\"server.RawCalls mismatch\")\n\tassert.Equal(t, 0, server.ThriftCalls(), msgP+\"server.ThriftCalls mismatch\")\n\n\texpectCalls := 0\n\tfor i := 1; i < 10; i *= 2 {\n\t\tdurations, err = client.RawCall(i)\n\t\trequire.NoError(t, err, msgP+\"Call(%v) failed\", i)\n\t\trequire.Equal(t, i, len(durations), msgP+\"Wrong number of calls\")\n\n\t\texpectCalls += i\n\t\trequire.Equal(t, expectCalls, server.RawCalls(), msgP+\"server.RawCalls mismatch\")\n\t\trequire.Equal(t, 0, server.ThriftCalls(), msgP+\"server.ThriftCalls mismatch\")\n\t}\n}\n\nfunc TestServerClientMatrix(t *testing.T) {\n\ttests := [][]Option{\n\t\t{WithServiceName(\"other\")},\n\t\t{WithRequestSize(tchannel.MaxFrameSize)},\n\t}\n\n\t// These options can be independently applied to the server or the client.\n\tindependentOpts := []Option{\n\t\tWithExternalProcess(),\n\t\tWithNoLibrary(),\n\t}\n\n\t// These options only apply to the client.\n\tclientOnlyOpts := combineOpts(independentOpts, []Option{\n\t\tWithNumClients(5),\n\t})\n\n\tfor _, tt := range tests {\n\t\tcombinations(len(independentOpts), func(serverSelect []bool) {\n\t\t\tcombinations(len(clientOnlyOpts), func(clientSelect []bool) {\n\t\t\t\tserverOpts := selectOptions(independentOpts, serverSelect)\n\t\t\t\tclientOpts := selectOptions(clientOnlyOpts, clientSelect)\n\t\t\t\trunSingleTest(t, tt, serverOpts, clientOpts)\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "benchmark/options.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport \"time\"\n\ntype options struct {\n\texternal  bool\n\tsvcName   string\n\tnoLibrary bool\n\n\t// Following options only make sense for clients.\n\tnoChecking bool\n\ttimeout    time.Duration\n\treqSize    int\n\tnumClients int\n\n\t// noDurations disables printing of durations to stdout.\n\t// This only applies to clients running out-of-process.\n\tnoDurations bool\n\n\t// Following options only make sense for servers.\n\tadvertiseHosts []string\n}\n\n// Option represents a Benchmark option.\ntype Option func(*options)\n\n// WithTimeout sets the timeout to use for each call.\nfunc WithTimeout(timeout time.Duration) Option {\n\treturn func(opts *options) {\n\t\topts.timeout = timeout\n\t}\n}\n\n// WithRequestSize sets the request size for each call.\nfunc WithRequestSize(reqSize int) Option {\n\treturn func(opts *options) {\n\t\topts.reqSize = reqSize\n\t}\n}\n\n// WithServiceName sets the service name of the benchmark server.\nfunc WithServiceName(svcName string) Option {\n\treturn func(opts *options) {\n\t\topts.svcName = svcName\n\t}\n}\n\n// WithExternalProcess creates a separate process to host the server/client.\nfunc WithExternalProcess() Option {\n\treturn func(opts *options) {\n\t\topts.external = true\n\t}\n}\n\n// WithNoLibrary uses the fast TCP-template based approach for generating\n// TChannel frames rather than the TChannel client library.\nfunc WithNoLibrary() Option {\n\treturn func(opts *options) {\n\t\topts.noLibrary = true\n\t}\n}\n\n// WithNoChecking disables result verification on the client side, which\n// may slow down the client (as it compares all request bytes against the\n// response bytes).\nfunc WithNoChecking() Option {\n\treturn func(opts *options) {\n\t\topts.noChecking = true\n\t}\n}\n\n// WithNumClients sets the number of concurrent TChannel clients to use\n// internally under a single benchmark.Client. This is used to generate\n// generate a large amount of traffic, as a single TChannel client will\n// not saturate a CPU since it will spend most of the time blocking and\n// waiting for the remote side to respond.\nfunc WithNumClients(numClients int) Option {\n\treturn func(opts *options) {\n\t\topts.numClients = numClients\n\t}\n}\n\n// WithNoDurations disables printing of latencies to standard out.\nfunc WithNoDurations() Option {\n\treturn func(opts *options) {\n\t\topts.noDurations = true\n\t}\n}\n\n// WithAdvertiseHosts sets the hosts to advertise with on startup.\nfunc WithAdvertiseHosts(hosts []string) Option {\n\treturn func(opts *options) {\n\t\topts.advertiseHosts = hosts\n\t}\n}\n\nfunc getOptions(optFns []Option) *options {\n\topts := &options{\n\t\ttimeout: time.Second,\n\t\tsvcName: \"bench-server\",\n\t}\n\tfor _, opt := range optFns {\n\t\topt(opts)\n\t}\n\treturn opts\n}\n"
  },
  {
    "path": "benchmark/real_relay.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"errors\"\n\t\"os\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/relay\"\n\t\"github.com/uber/tchannel-go/relay/relaytest\"\n\t\"go.uber.org/atomic\"\n)\n\ntype fixedHosts struct {\n\thosts   map[string][]string\n\tappends []relay.KeyVal\n\tpickI   atomic.Int32\n}\n\nfunc (fh *fixedHosts) Get(cf relay.CallFrame, _ *relay.Conn) (string, error) {\n\tpeers := fh.hosts[string(cf.Service())]\n\tif len(peers) == 0 {\n\t\treturn \"\", errors.New(\"no peers\")\n\t}\n\n\tfor _, kv := range fh.appends {\n\t\tcf.Arg2Append(kv.Key, kv.Val)\n\t}\n\n\tpickI := int(fh.pickI.Inc()-1) % len(peers)\n\treturn peers[pickI], nil\n}\n\ntype realRelay struct {\n\tch    *tchannel.Channel\n\thosts *fixedHosts\n}\n\n// NewRealRelay creates a TChannel relay.\nfunc NewRealRelay(services map[string][]string, appends []relay.KeyVal) (Relay, error) {\n\thosts := &fixedHosts{\n\t\thosts:   services,\n\t\tappends: appends,\n\t}\n\tch, err := tchannel.NewChannel(\"relay\", &tchannel.ChannelOptions{\n\t\tRelayHost: relaytest.HostFunc(hosts.Get),\n\t\tLogger:    tchannel.NewLevelLogger(tchannel.NewLogger(os.Stderr), tchannel.LogLevelWarn),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := ch.ListenAndServe(\"127.0.0.1:0\"); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &realRelay{\n\t\tch:    ch,\n\t\thosts: hosts,\n\t}, nil\n}\n\nfunc (r *realRelay) HostPort() string {\n\treturn r.ch.PeerInfo().HostPort\n}\n\nfunc (r *realRelay) Close() {\n\tr.ch.Close()\n}\n"
  },
  {
    "path": "benchmark/req_bytes.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nfunc getRequestBytes(n int) []byte {\n\tbs := make([]byte, n)\n\tfor i := range bs {\n\t\tbs[i] = byte(i)\n\t}\n\treturn bs\n}\n\nfunc getRequestString(n int) string {\n\t// TODO: we should replace this with base64 once we drop go1.4 support.\n\tchars := []byte(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz\")\n\tbs := make([]byte, n)\n\tfor i := range bs {\n\t\tbs[i] = chars[i%len(chars)]\n\t}\n\treturn string(bs)\n}\n"
  },
  {
    "path": "benchmark/tcp_bench_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc echoServer(tb testing.TB) net.Listener {\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\trequire.NoError(tb, err, \"Listen failed\")\n\n\tgo func() {\n\t\tconn, err := ln.Accept()\n\t\trequire.NoError(tb, err, \"Accept failed\")\n\n\t\t// Echo the connection back to itself.\n\t\tio.Copy(conn, conn)\n\t}()\n\n\treturn ln\n}\n\nfunc benchmarkClient(b *testing.B, dst string, reqSize int) {\n\treq := getRequestBytes(reqSize)\n\ttotalExpected := b.N * reqSize\n\n\tconn, err := net.Dial(\"tcp\", dst)\n\trequire.NoError(b, err, \"Failed to connect to destination\")\n\tdefer conn.Close()\n\n\treaderDone := make(chan struct{})\n\tgo func() {\n\t\tdefer close(readerDone)\n\t\tn, err := io.CopyN(ioutil.Discard, conn, int64(totalExpected))\n\t\tassert.NoError(b, err, \"Expected %v response bytes, got %v\", totalExpected, n)\n\t}()\n\n\tb.SetBytes(int64(reqSize))\n\tfor i := 0; i < b.N; i++ {\n\t\t_, err := conn.Write(req)\n\t\trequire.NoError(b, err, \"Write failed\")\n\t}\n\n\t<-readerDone\n}\n\nfunc benchmarkTCPDirect(b *testing.B, reqSize int) {\n\tln := echoServer(b)\n\tbenchmarkClient(b, ln.Addr().String(), reqSize)\n}\n\nfunc BenchmarkTCPDirect100Bytes(b *testing.B) {\n\tbenchmarkTCPDirect(b, 100)\n}\n\nfunc BenchmarkTCPDirect1k(b *testing.B) {\n\tbenchmarkTCPDirect(b, 1024)\n}\n\nfunc BenchmarkTCPDirect4k(b *testing.B) {\n\tbenchmarkTCPDirect(b, 4*1024)\n}\n\nfunc benchmarkTCPRelay(b *testing.B, reqSize int) {\n\tln := echoServer(b)\n\n\trelay, err := NewTCPRawRelay([]string{ln.Addr().String()})\n\trequire.NoError(b, err, \"Relay failed\")\n\tdefer relay.Close()\n\n\tbenchmarkClient(b, relay.HostPort(), reqSize)\n}\n\nfunc BenchmarkTCPRelay100Bytes(b *testing.B) {\n\tbenchmarkTCPRelay(b, 100)\n}\n\nfunc BenchmarkTCPRelay1kBytes(b *testing.B) {\n\tbenchmarkTCPRelay(b, 1024)\n}\n\nfunc BenchmarkTCPRelay4k(b *testing.B) {\n\tbenchmarkTCPRelay(b, 4*1024)\n}\n"
  },
  {
    "path": "benchmark/tcp_frame_relay.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"log\"\n\t\"net\"\n)\nimport \"github.com/uber/tchannel-go\"\n\ntype tcpFrameRelay struct {\n\t*tcpRelay\n\tmodifier func(bool, *tchannel.Frame) *tchannel.Frame\n}\n\n// NewTCPFrameRelay relays frames from one connection to another. It reads\n// and writes frames using the TChannel frame functions.\nfunc NewTCPFrameRelay(dests []string, modifier func(bool, *tchannel.Frame) *tchannel.Frame) (Relay, error) {\n\tvar err error\n\tr := &tcpFrameRelay{modifier: modifier}\n\n\tr.tcpRelay, err = newTCPRelay(dests, r.handleConnFrameRelay)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn r, nil\n}\n\nfunc (r *tcpFrameRelay) handleConnFrameRelay(fromClient bool, src, dst net.Conn) {\n\tpool := tchannel.NewSyncFramePool()\n\tframeCh := make(chan *tchannel.Frame, 100)\n\tdefer close(frameCh)\n\n\tgo func() {\n\t\tfor f := range frameCh {\n\t\t\tif err := f.WriteOut(dst); err != nil {\n\t\t\t\tlog.Printf(\"Failed to write out frame: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpool.Release(f)\n\t\t}\n\t}()\n\n\tfor {\n\t\tf := pool.Get()\n\t\tif err := f.ReadIn(src); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif r.modifier != nil {\n\t\t\tf = r.modifier(fromClient, f)\n\t\t}\n\n\t\tselect {\n\t\tcase frameCh <- f:\n\t\tdefault:\n\t\t\tpanic(\"frame buffer full\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "benchmark/tcp_raw_relay.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmark\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"net\"\n\n\t\"go.uber.org/atomic\"\n)\n\ntype tcpRelay struct {\n\tdestI      atomic.Int32\n\tdests      []string\n\tln         net.Listener\n\thandleConn func(fromClient bool, src, dst net.Conn)\n}\n\nfunc newTCPRelay(dests []string, handleConn func(fromClient bool, src, dst net.Conn)) (*tcpRelay, error) {\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trelay := &tcpRelay{\n\t\tdests:      dests,\n\t\tln:         ln,\n\t\thandleConn: handleConn,\n\t}\n\tgo relay.acceptLoop()\n\treturn relay, nil\n}\n\n// NewTCPRawRelay creates a relay that just pipes data from one connection\n// to another directly.\nfunc NewTCPRawRelay(dests []string) (Relay, error) {\n\treturn newTCPRelay(dests, func(_ bool, src, dst net.Conn) {\n\t\tio.Copy(src, dst)\n\t})\n}\n\nfunc (r *tcpRelay) acceptLoop() {\n\tfor {\n\t\tconn, err := r.ln.Accept()\n\t\tif err, ok := err.(net.Error); ok && err.Temporary() {\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tgo r.handleIncoming(conn)\n\t}\n}\n\nfunc (r *tcpRelay) handleIncoming(src net.Conn) {\n\tdefer src.Close()\n\n\tdst, err := net.Dial(\"tcp\", r.nextDestination())\n\tif err != nil {\n\t\tlog.Printf(\"Connection failed: %v\", err)\n\t\treturn\n\t}\n\tdefer dst.Close()\n\n\tgo r.handleConn(true, src, dst)\n\tr.handleConn(false, dst, src)\n}\n\nfunc (r *tcpRelay) nextDestination() string {\n\ti := int(r.destI.Inc()-1) % len(r.dests)\n\treturn r.dests[i]\n}\n\nfunc (r *tcpRelay) HostPort() string {\n\treturn r.ln.Addr().String()\n}\n\nfunc (r *tcpRelay) Close() {\n\tr.ln.Close()\n}\n"
  },
  {
    "path": "calloptions.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\n// Format is the arg scheme used for a specific call.\ntype Format string\n\n// The list of formats supported by tchannel.\nconst (\n\tHTTP   Format = \"http\"\n\tJSON   Format = \"json\"\n\tRaw    Format = \"raw\"\n\tThrift Format = \"thrift\"\n)\n\nfunc (f Format) String() string {\n\treturn string(f)\n}\n\n// CallOptions are options for a specific call.\ntype CallOptions struct {\n\t// Format is arg scheme used for this call, sent in the \"as\" header.\n\t// This header is only set if the Format is set.\n\tFormat Format\n\n\t// ShardKey determines where this call request belongs, used with ringpop applications.\n\tShardKey string\n\n\t// RequestState stores request state across retry attempts.\n\tRequestState *RequestState\n\n\t// RoutingKey identifies the destined traffic group. Relays may favor the\n\t// routing key over the service name to route the request to a specialized\n\t// traffic group.\n\tRoutingKey string\n\n\t// RoutingDelegate identifies a traffic group capable of routing a request\n\t// to an instance of the intended service.\n\tRoutingDelegate string\n\n\t// CallerName defaults to the channel's service name for an outbound call.\n\t// Optionally override this field to support transparent proxying when inbound\n\t// caller names vary across calls.\n\tCallerName string\n}\n\nvar defaultCallOptions = &CallOptions{}\n\nfunc (c *CallOptions) setHeaders(headers transportHeaders) {\n\theaders[ArgScheme] = Raw.String()\n\tc.overrideHeaders(headers)\n}\n\n// overrideHeaders sets headers if the call options contains non-default values.\nfunc (c *CallOptions) overrideHeaders(headers transportHeaders) {\n\tif c.Format != \"\" {\n\t\theaders[ArgScheme] = c.Format.String()\n\t}\n\tif c.ShardKey != \"\" {\n\t\theaders[ShardKey] = c.ShardKey\n\t}\n\tif c.RoutingKey != \"\" {\n\t\theaders[RoutingKey] = c.RoutingKey\n\t}\n\tif c.RoutingDelegate != \"\" {\n\t\theaders[RoutingDelegate] = c.RoutingDelegate\n\t}\n\tif c.CallerName != \"\" {\n\t\theaders[CallerName] = c.CallerName\n\t}\n}\n\n// setResponseHeaders copies some headers from the incoming call request to the response.\nfunc setResponseHeaders(reqHeaders, respHeaders transportHeaders) {\n\trespHeaders[ArgScheme] = reqHeaders[ArgScheme]\n}\n"
  },
  {
    "path": "calloptions_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSetHeaders(t *testing.T) {\n\ttests := []struct {\n\t\tformat          Format\n\t\troutingDelegate string\n\t\troutingKey      string\n\t\tcallerName      string\n\t\texpectedHeaders transportHeaders\n\t}{\n\t\t{\n\t\t\t// When no format is specified, Raw should be used by default.\n\t\t\tformat:          \"\",\n\t\t\texpectedHeaders: transportHeaders{ArgScheme: Raw.String()},\n\t\t},\n\t\t{\n\t\t\tformat:          Thrift,\n\t\t\texpectedHeaders: transportHeaders{ArgScheme: Thrift.String()},\n\t\t},\n\t\t{\n\t\t\tcallerName: \"foo-caller\",\n\t\t\texpectedHeaders: transportHeaders{\n\t\t\t\tArgScheme:  Raw.String(),\n\t\t\t\tCallerName: \"foo-caller\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tformat:          JSON,\n\t\t\troutingDelegate: \"xpr\",\n\t\t\texpectedHeaders: transportHeaders{\n\t\t\t\tArgScheme:       JSON.String(),\n\t\t\t\tRoutingDelegate: \"xpr\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tformat:     JSON,\n\t\t\troutingKey: \"canary\",\n\t\t\texpectedHeaders: transportHeaders{\n\t\t\t\tArgScheme:  JSON.String(),\n\t\t\t\tRoutingKey: \"canary\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tcallOpts := &CallOptions{\n\t\t\tFormat:          tt.format,\n\t\t\tRoutingDelegate: tt.routingDelegate,\n\t\t\tRoutingKey:      tt.routingKey,\n\t\t\tCallerName:      tt.callerName,\n\t\t}\n\t\theaders := make(transportHeaders)\n\t\tcallOpts.setHeaders(headers)\n\t\tassert.Equal(t, tt.expectedHeaders, headers)\n\t}\n}\n"
  },
  {
    "path": "channel.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/tnet\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\nvar (\n\terrAlreadyListening  = errors.New(\"channel already listening\")\n\terrInvalidStateForOp = errors.New(\"channel is in an invalid state for that method\")\n\terrMaxIdleTimeNotSet = errors.New(\"IdleCheckInterval is set but MaxIdleTime is zero\")\n\n\t// ErrNoServiceName is returned when no service name is provided when\n\t// creating a new channel.\n\tErrNoServiceName = errors.New(\"no service name provided\")\n)\n\nconst ephemeralHostPort = \"0.0.0.0:0\"\n\n// ChannelOptions are used to control parameters on a create a TChannel\ntype ChannelOptions struct {\n\t// Default Connection options\n\tDefaultConnectionOptions ConnectionOptions\n\n\t// The name of the process, for logging and reporting to peers\n\tProcessName string\n\n\t// OnPeerStatusChanged is an optional callback that receives a notification\n\t// whenever the channel establishes a usable connection to a peer, or loses\n\t// a connection to a peer.\n\tOnPeerStatusChanged func(*Peer)\n\n\t// The logger to use for this channel\n\tLogger Logger\n\n\t// The host:port selection implementation to use for relaying. This is an\n\t// unstable API - breaking changes are likely.\n\tRelayHost RelayHost\n\n\t// The list of service names that should be handled locally by this channel.\n\t// This is an unstable API - breaking changes are likely.\n\tRelayLocalHandlers []string\n\n\t// The maximum allowable timeout for relayed calls (longer timeouts are\n\t// clamped to this value). Passing zero uses the default of 2m.\n\t// This is an unstable API - breaking changes are likely.\n\tRelayMaxTimeout time.Duration\n\n\t// If the relay needs to connect while processing a frame, this specifies\n\t// the max connection timeout used.\n\tRelayMaxConnectionTimeout time.Duration\n\n\t// RelayMaxTombs is the maximum number of timed-out calls that the relay\n\t// will keep track of per-connection to avoid spurious logs\n\t// for late-arriving frames.\n\t// This is an unstable API - breaking changes are likely.\n\tRelayMaxTombs uint64\n\n\t// RelayTimerVerification will disable pooling of relay timers, and instead\n\t// verify that timers are not used once they are released.\n\t// This is an unstable API - breaking changes are likely.\n\tRelayTimerVerification bool\n\n\t// The reporter to use for reporting stats for this channel.\n\tStatsReporter StatsReporter\n\n\t// TimeNow is a variable for overriding time.Now in unit tests.\n\t// Note: This is not a stable part of the API and may change.\n\tTimeNow func() time.Time\n\n\t// TimeTicker is a variable for overriding time.Ticker in unit tests.\n\t// Note: This is not a stable part of the API and may change.\n\tTimeTicker func(d time.Duration) *time.Ticker\n\n\t// MaxIdleTime controls how long we allow an idle connection to exist\n\t// before tearing it down. Must be set to non-zero if IdleCheckInterval\n\t// is set.\n\tMaxIdleTime time.Duration\n\n\t// IdleCheckInterval controls how often the channel runs a sweep over\n\t// all active connections to see if they can be dropped. Connections that\n\t// are idle for longer than MaxIdleTime are disconnected. If this is set to\n\t// zero (the default), idle checking is disabled.\n\tIdleCheckInterval time.Duration\n\n\t// Tracer is an OpenTracing Tracer used to manage distributed tracing spans.\n\t// If not set, opentracing.GlobalTracer() is used.\n\tTracer opentracing.Tracer\n\n\t// Handler is an alternate handler for all inbound requests, overriding the\n\t// default handler that delegates to a subchannel.\n\tHandler Handler\n\n\t// SkipHandlerMethods allow users to configure TChannel server such that\n\t// requests with specified methods can be ignored by the above passed-in handler\n\t// and handled natively by TChannel.\n\t// Requests with other methods will be handled by passed-in handler.\n\t// Methods should be in the format of Service::Method.\n\t// This is useful for the gradual migration purpose.\n\tSkipHandlerMethods []string\n\n\t// Dialer is optional factory method which can be used for overriding\n\t// outbound connections for things like TLS handshake\n\tDialer func(ctx context.Context, network, hostPort string) (net.Conn, error)\n\n\t// ConnContext runs when a connection is established, which updates\n\t// the per-connection base context. This context is used as the parent context\n\t// for incoming calls.\n\tConnContext func(ctx context.Context, conn net.Conn) context.Context\n}\n\n// ChannelState is the state of a channel.\ntype ChannelState int\n\nconst (\n\t// ChannelClient is a channel that can be used as a client.\n\tChannelClient ChannelState = iota + 1\n\n\t// ChannelListening is a channel that is listening for new connnections.\n\tChannelListening\n\n\t// ChannelStartClose is a channel that has received a Close request.\n\t// The channel is no longer listening, and all new incoming connections are rejected.\n\tChannelStartClose\n\n\t// ChannelInboundClosed is a channel that has drained all incoming connections, but may\n\t// have outgoing connections. All incoming calls and new outgoing calls are rejected.\n\tChannelInboundClosed\n\n\t// ChannelClosed is a channel that has closed completely.\n\tChannelClosed\n)\n\n//go:generate stringer -type=ChannelState\n\n// A Channel is a bi-directional connection to the peering and routing network.\n// Applications can use a Channel to make service calls to remote peers via\n// BeginCall, or to listen for incoming calls from peers.  Applications that\n// want to receive requests should call one of Serve or ListenAndServe\n// TODO(prashant): Shutdown all subchannels + peers when channel is closed.\ntype Channel struct {\n\tchannelConnectionCommon\n\n\tchID                uint32\n\tcreatedStack        string\n\tcommonStatsTags     map[string]string\n\tconnectionOptions   ConnectionOptions\n\tpeers               *PeerList\n\trelayHost           RelayHost\n\trelayMaxTimeout     time.Duration\n\trelayMaxConnTimeout time.Duration\n\trelayMaxTombs       uint64\n\trelayTimerVerify    bool\n\tinternalHandlers    *handlerMap\n\thandler             Handler\n\tonPeerStatusChanged func(*Peer)\n\tdialer              func(ctx context.Context, hostPort string) (net.Conn, error)\n\tconnContext         func(ctx context.Context, conn net.Conn) context.Context\n\tclosed              chan struct{}\n\n\t// mutable contains all the members of Channel which are mutable.\n\tmutable struct {\n\t\tsync.RWMutex // protects members of the mutable struct.\n\t\tstate        ChannelState\n\t\tpeerInfo     LocalPeerInfo // May be ephemeral if this is a client only channel\n\t\tl            net.Listener  // May be nil if this is a client only channel\n\t\tidleSweep    *idleSweep\n\t\tconns        map[uint32]*Connection\n\t}\n}\n\n// channelConnectionCommon is the list of common objects that both use\n// and can be copied directly from the channel to the connection.\ntype channelConnectionCommon struct {\n\tlog           Logger\n\trelayLocal    map[string]struct{}\n\tstatsReporter StatsReporter\n\ttracer        opentracing.Tracer\n\tsubChannels   *subChannelMap\n\ttimeNow       func() time.Time\n\ttimeTicker    func(time.Duration) *time.Ticker\n}\n\n// _nextChID is used to allocate unique IDs to every channel for debugging purposes.\nvar _nextChID atomic.Uint32\n\n// Tracer returns the OpenTracing Tracer for this channel. If no tracer was provided\n// in the configuration, returns opentracing.GlobalTracer(). Note that this approach\n// allows opentracing.GlobalTracer() to be initialized _after_ the channel is created.\nfunc (ccc channelConnectionCommon) Tracer() opentracing.Tracer {\n\tif ccc.tracer != nil {\n\t\treturn ccc.tracer\n\t}\n\treturn opentracing.GlobalTracer()\n}\n\n// NewChannel creates a new Channel.  The new channel can be used to send outbound requests\n// to peers, but will not listen or handling incoming requests until one of ListenAndServe\n// or Serve is called. The local service name should be passed to serviceName.\nfunc NewChannel(serviceName string, opts *ChannelOptions) (*Channel, error) {\n\tif serviceName == \"\" {\n\t\treturn nil, ErrNoServiceName\n\t}\n\n\tif opts == nil {\n\t\topts = &ChannelOptions{}\n\t}\n\n\tprocessName := opts.ProcessName\n\tif processName == \"\" {\n\t\tprocessName = fmt.Sprintf(\"%s[%d]\", filepath.Base(os.Args[0]), os.Getpid())\n\t}\n\n\tlogger := opts.Logger\n\tif logger == nil {\n\t\tlogger = NullLogger\n\t}\n\n\tstatsReporter := opts.StatsReporter\n\tif statsReporter == nil {\n\t\tstatsReporter = NullStatsReporter\n\t}\n\n\ttimeNow := opts.TimeNow\n\tif timeNow == nil {\n\t\ttimeNow = time.Now\n\t}\n\n\ttimeTicker := opts.TimeTicker\n\tif timeTicker == nil {\n\t\ttimeTicker = time.NewTicker\n\t}\n\n\tchID := _nextChID.Inc()\n\tlogger = logger.WithFields(\n\t\tLogField{\"serviceName\", serviceName},\n\t\tLogField{\"process\", processName},\n\t\tLogField{\"chID\", chID},\n\t)\n\n\tif err := opts.validateIdleCheck(); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Default to dialContext if dialer is not passed in as an option\n\tdialCtx := dialContext\n\tif opts.Dialer != nil {\n\t\tdialCtx = func(ctx context.Context, hostPort string) (net.Conn, error) {\n\t\t\treturn opts.Dialer(ctx, \"tcp\", hostPort)\n\t\t}\n\t}\n\n\tif opts.ConnContext == nil {\n\t\topts.ConnContext = func(ctx context.Context, conn net.Conn) context.Context {\n\t\t\treturn ctx\n\t\t}\n\t}\n\n\tch := &Channel{\n\t\tchannelConnectionCommon: channelConnectionCommon{\n\t\t\tlog:           logger,\n\t\t\trelayLocal:    toStringSet(opts.RelayLocalHandlers),\n\t\t\tstatsReporter: statsReporter,\n\t\t\tsubChannels:   &subChannelMap{},\n\t\t\ttimeNow:       timeNow,\n\t\t\ttimeTicker:    timeTicker,\n\t\t\ttracer:        opts.Tracer,\n\t\t},\n\t\tchID:                chID,\n\t\tconnectionOptions:   opts.DefaultConnectionOptions.withDefaults(),\n\t\trelayHost:           opts.RelayHost,\n\t\trelayMaxTimeout:     validateRelayMaxTimeout(opts.RelayMaxTimeout, logger),\n\t\trelayMaxConnTimeout: opts.RelayMaxConnectionTimeout,\n\t\trelayMaxTombs:       opts.RelayMaxTombs,\n\t\trelayTimerVerify:    opts.RelayTimerVerification,\n\t\tdialer:              dialCtx,\n\t\tconnContext:         opts.ConnContext,\n\t\tclosed:              make(chan struct{}),\n\t}\n\tch.peers = newRootPeerList(ch, opts.OnPeerStatusChanged).newChild()\n\n\tswitch {\n\tcase len(opts.SkipHandlerMethods) > 0 && opts.Handler != nil:\n\t\tsm, err := toServiceMethodSet(opts.SkipHandlerMethods)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tch.handler = userHandlerWithSkip{\n\t\t\tlocalHandler:      channelHandler{ch},\n\t\t\tignoreUserHandler: sm,\n\t\t\tuserHandler:       opts.Handler,\n\t\t}\n\tcase opts.Handler != nil:\n\t\tch.handler = opts.Handler\n\tdefault:\n\t\tch.handler = channelHandler{ch}\n\t}\n\n\tch.mutable.peerInfo = LocalPeerInfo{\n\t\tPeerInfo: PeerInfo{\n\t\t\tProcessName: processName,\n\t\t\tHostPort:    ephemeralHostPort,\n\t\t\tIsEphemeral: true,\n\t\t\tVersion: PeerVersion{\n\t\t\t\tLanguage:        \"go\",\n\t\t\t\tLanguageVersion: strings.TrimPrefix(runtime.Version(), \"go\"),\n\t\t\t\tTChannelVersion: VersionInfo,\n\t\t\t},\n\t\t},\n\t\tServiceName: serviceName,\n\t}\n\tch.mutable.state = ChannelClient\n\tch.mutable.conns = make(map[uint32]*Connection)\n\tch.createCommonStats()\n\tch.internalHandlers = ch.createInternalHandlers()\n\n\tregisterNewChannel(ch)\n\n\tif opts.RelayHost != nil {\n\t\topts.RelayHost.SetChannel(ch)\n\t}\n\n\t// Start the idle connection timer.\n\tch.mutable.idleSweep = startIdleSweep(ch, opts)\n\n\treturn ch, nil\n}\n\n// ConnectionOptions returns the channel's connection options.\nfunc (ch *Channel) ConnectionOptions() *ConnectionOptions {\n\treturn &ch.connectionOptions\n}\n\n// Serve serves incoming requests using the provided listener.\n// The local peer info is set synchronously, but the actual socket listening is done in\n// a separate goroutine.\nfunc (ch *Channel) Serve(l net.Listener) error {\n\tmutable := &ch.mutable\n\tmutable.Lock()\n\tdefer mutable.Unlock()\n\n\tif mutable.l != nil {\n\t\treturn errAlreadyListening\n\t}\n\tmutable.l = tnet.Wrap(l)\n\n\tif mutable.state != ChannelClient {\n\t\treturn errInvalidStateForOp\n\t}\n\tmutable.state = ChannelListening\n\n\tmutable.peerInfo.HostPort = l.Addr().String()\n\tmutable.peerInfo.IsEphemeral = false\n\tch.log = ch.log.WithFields(LogField{\"hostPort\", mutable.peerInfo.HostPort})\n\tch.log.Info(\"Channel is listening.\")\n\tgo ch.serve()\n\treturn nil\n}\n\n// ListenAndServe listens on the given address and serves incoming requests.\n// The port may be 0, in which case the channel will use an OS assigned port\n// This method does not block as the handling of connections is done in a goroutine.\nfunc (ch *Channel) ListenAndServe(hostPort string) error {\n\tmutable := &ch.mutable\n\tmutable.RLock()\n\n\tif mutable.l != nil {\n\t\tmutable.RUnlock()\n\t\treturn errAlreadyListening\n\t}\n\n\tl, err := net.Listen(\"tcp\", hostPort)\n\tif err != nil {\n\t\tmutable.RUnlock()\n\t\treturn err\n\t}\n\n\tmutable.RUnlock()\n\treturn ch.Serve(l)\n}\n\n// Registrar is the base interface for registering handlers on either the base\n// Channel or the SubChannel\ntype Registrar interface {\n\t// ServiceName returns the service name that this Registrar is for.\n\tServiceName() string\n\n\t// Register registers a handler for ServiceName and the given method.\n\tRegister(h Handler, methodName string)\n\n\t// Logger returns the logger for this Registrar.\n\tLogger() Logger\n\n\t// StatsReporter returns the stats reporter for this Registrar\n\tStatsReporter() StatsReporter\n\n\t// StatsTags returns the tags that should be used.\n\tStatsTags() map[string]string\n\n\t// Peers returns the peer list for this Registrar.\n\tPeers() *PeerList\n}\n\n// Register registers a handler for a method.\n//\n// The handler is registered with the service name used when the Channel was\n// created. To register a handler with a different service name, obtain a\n// SubChannel for that service with GetSubChannel, and Register a handler\n// under that. You may also use SetHandler on a SubChannel to set up a\n// catch-all Handler for that service. See the docs for SetHandler for more\n// information.\n//\n// Register panics if the channel was constructed with an alternate root\n// handler that does not support Register.\nfunc (ch *Channel) Register(h Handler, methodName string) {\n\tr, ok := ch.handler.(registrar)\n\tif !ok {\n\t\tpanic(\"can't register handler when channel configured with alternate root handler without Register method\")\n\t}\n\n\tr.Register(h, methodName)\n}\n\n// PeerInfo returns the current peer info for the channel\nfunc (ch *Channel) PeerInfo() LocalPeerInfo {\n\tch.mutable.RLock()\n\tpeerInfo := ch.mutable.peerInfo\n\tch.mutable.RUnlock()\n\n\treturn peerInfo\n}\n\nfunc (ch *Channel) createCommonStats() {\n\tch.commonStatsTags = map[string]string{\n\t\t\"app\":     ch.mutable.peerInfo.ProcessName,\n\t\t\"service\": ch.mutable.peerInfo.ServiceName,\n\t}\n\thost, err := os.Hostname()\n\tif err != nil {\n\t\tch.log.WithFields(ErrField(err)).Info(\"Channel creation failed to get host.\")\n\t\treturn\n\t}\n\tch.commonStatsTags[\"host\"] = host\n\t// TODO(prashant): Allow user to pass extra tags (such as cluster, version).\n}\n\n// GetSubChannel returns a SubChannel for the given service name. If the subchannel does not\n// exist, it is created.\nfunc (ch *Channel) GetSubChannel(serviceName string, opts ...SubChannelOption) *SubChannel {\n\tsub, added := ch.subChannels.getOrAdd(serviceName, ch)\n\tif added {\n\t\tfor _, opt := range opts {\n\t\t\topt(sub)\n\t\t}\n\t}\n\treturn sub\n}\n\n// Peers returns the PeerList for the channel.\nfunc (ch *Channel) Peers() *PeerList {\n\treturn ch.peers\n}\n\n// RootPeers returns the root PeerList for the channel, which is the sole place\n// new Peers are created. All children of the root list (including ch.Peers())\n// automatically re-use peers from the root list and create new peers in the\n// root list.\nfunc (ch *Channel) RootPeers() *RootPeerList {\n\treturn ch.peers.parent\n}\n\n// BeginCall starts a new call to a remote peer, returning an OutboundCall that can\n// be used to write the arguments of the call.\nfunc (ch *Channel) BeginCall(ctx context.Context, hostPort, serviceName, methodName string, callOptions *CallOptions) (*OutboundCall, error) {\n\tp := ch.RootPeers().GetOrAdd(hostPort)\n\treturn p.BeginCall(ctx, serviceName, methodName, callOptions)\n}\n\n// serve runs the listener to accept and manage new incoming connections, blocking\n// until the channel is closed.\nfunc (ch *Channel) serve() {\n\tacceptBackoff := 0 * time.Millisecond\n\n\tfor {\n\t\tnetConn, err := ch.mutable.l.Accept()\n\t\tif err != nil {\n\t\t\t// Backoff from new accepts if this is a temporary error\n\t\t\tif ne, ok := err.(net.Error); ok && ne.Temporary() {\n\t\t\t\tif acceptBackoff == 0 {\n\t\t\t\t\tacceptBackoff = 5 * time.Millisecond\n\t\t\t\t} else {\n\t\t\t\t\tacceptBackoff *= 2\n\t\t\t\t}\n\t\t\t\tif max := 1 * time.Second; acceptBackoff > max {\n\t\t\t\t\tacceptBackoff = max\n\t\t\t\t}\n\t\t\t\tch.log.WithFields(\n\t\t\t\t\tErrField(err),\n\t\t\t\t\tLogField{\"backoff\", acceptBackoff},\n\t\t\t\t).Warn(\"Accept error, will wait and retry.\")\n\t\t\t\ttime.Sleep(acceptBackoff)\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\t// Only log an error if this didn't happen due to a Close.\n\t\t\t\tif ch.State() >= ChannelStartClose {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tch.log.WithFields(ErrField(err)).Fatal(\"Unrecoverable accept error, closing server.\")\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tacceptBackoff = 0\n\n\t\t// Perform the connection handshake in a background goroutine.\n\t\tgo func() {\n\t\t\t// Register the connection in the peer once the channel is set up.\n\t\t\tevents := connectionEvents{\n\t\t\t\tOnActive:           ch.inboundConnectionActive,\n\t\t\t\tOnCloseStateChange: ch.connectionCloseStateChange,\n\t\t\t\tOnExchangeUpdated:  ch.exchangeUpdated,\n\t\t\t}\n\t\t\tif _, err := ch.inboundHandshake(context.Background(), netConn, events); err != nil {\n\t\t\t\tnetConn.Close()\n\t\t\t}\n\t\t}()\n\t}\n}\n\n// Ping sends a ping message to the given hostPort and waits for a response.\nfunc (ch *Channel) Ping(ctx context.Context, hostPort string) error {\n\tpeer := ch.RootPeers().GetOrAdd(hostPort)\n\tconn, err := peer.GetConnection(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn conn.ping(ctx)\n}\n\n// Logger returns the logger for this channel.\nfunc (ch *Channel) Logger() Logger {\n\treturn ch.log\n}\n\n// StatsReporter returns the stats reporter for this channel.\nfunc (ch *Channel) StatsReporter() StatsReporter {\n\treturn ch.statsReporter\n}\n\n// StatsTags returns the common tags that should be used when reporting stats.\n// It returns a new map for each call.\nfunc (ch *Channel) StatsTags() map[string]string {\n\tm := make(map[string]string)\n\tfor k, v := range ch.commonStatsTags {\n\t\tm[k] = v\n\t}\n\treturn m\n}\n\n// ServiceName returns the serviceName that this channel was created for.\nfunc (ch *Channel) ServiceName() string {\n\treturn ch.PeerInfo().ServiceName\n}\n\n// Connect creates a new outbound connection to hostPort.\nfunc (ch *Channel) Connect(ctx context.Context, hostPort string) (*Connection, error) {\n\tswitch state := ch.State(); state {\n\tcase ChannelClient, ChannelListening:\n\t\tbreak\n\tdefault:\n\t\tch.log.Debugf(\"Connect rejecting new connection as state is %v\", state)\n\t\treturn nil, errInvalidStateForOp\n\t}\n\n\t// The context timeout applies to the whole call, but users may want a lower\n\t// connect timeout (e.g. for streams).\n\tif params := getTChannelParams(ctx); params != nil && params.connectTimeout > 0 {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, params.connectTimeout)\n\t\tdefer cancel()\n\t}\n\n\tevents := connectionEvents{\n\t\tOnActive:           ch.outboundConnectionActive,\n\t\tOnCloseStateChange: ch.connectionCloseStateChange,\n\t\tOnExchangeUpdated:  ch.exchangeUpdated,\n\t}\n\n\tif err := ctx.Err(); err != nil {\n\t\treturn nil, GetContextError(err)\n\t}\n\n\ttimeout := getTimeout(ctx)\n\ttcpConn, err := ch.dialer(ctx, hostPort)\n\tif err != nil {\n\t\tif ne, ok := err.(net.Error); ok && ne.Timeout() {\n\t\t\tch.log.WithFields(\n\t\t\t\tLogField{\"remoteHostPort\", hostPort},\n\t\t\t\tLogField{\"timeout\", timeout},\n\t\t\t).Info(\"Outbound net.Dial timed out.\")\n\t\t\terr = ErrTimeout\n\t\t} else if ctx.Err() == context.Canceled {\n\t\t\tch.log.WithFields(\n\t\t\t\tLogField{\"remoteHostPort\", hostPort},\n\t\t\t).Info(\"Outbound net.Dial was cancelled.\")\n\t\t\terr = GetContextError(ErrRequestCancelled)\n\t\t} else {\n\t\t\tch.log.WithFields(\n\t\t\t\tErrField(err),\n\t\t\t\tLogField{\"remoteHostPort\", hostPort},\n\t\t\t).Info(\"Outbound net.Dial failed.\")\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tconn, err := ch.outboundHandshake(ctx, tcpConn, hostPort, events)\n\tif conn != nil {\n\t\t// It's possible that the connection we just created responds with a host:port\n\t\t// that is not what we tried to connect to. E.g., we may have connected to\n\t\t// 127.0.0.1:1234, but the returned host:port may be 10.0.0.1:1234.\n\t\t// In this case, the connection won't be added to 127.0.0.1:1234 peer\n\t\t// and so future calls to that peer may end up creating new connections. To\n\t\t// avoid this issue, and to avoid clients being aware of any TCP relays, we\n\t\t// add the connection to the intended peer.\n\t\tif hostPort != conn.remotePeerInfo.HostPort {\n\t\t\tconn.log.Debugf(\"Outbound connection host:port mismatch, adding to peer %v\", conn.remotePeerInfo.HostPort)\n\t\t\tch.addConnectionToPeer(hostPort, conn, outbound)\n\t\t}\n\t}\n\n\treturn conn, err\n}\n\n// exchangeUpdated updates the peer heap.\nfunc (ch *Channel) exchangeUpdated(c *Connection) {\n\tif c.remotePeerInfo.HostPort == \"\" {\n\t\t// Hostport is unknown until we get init resp.\n\t\treturn\n\t}\n\n\tp, ok := ch.RootPeers().Get(c.remotePeerInfo.HostPort)\n\tif !ok {\n\t\treturn\n\t}\n\n\tch.updatePeer(p)\n}\n\n// updatePeer updates the score of the peer and update it's position in heap as well.\nfunc (ch *Channel) updatePeer(p *Peer) {\n\tch.peers.onPeerChange(p)\n\tch.subChannels.updatePeer(p)\n\tp.callOnUpdateComplete()\n}\n\n// addConnection adds the connection to the channel's list of connection\n// if the channel is in a valid state to accept this connection. It returns\n// whether the connection was added.\nfunc (ch *Channel) addConnection(c *Connection, direction connectionDirection) bool {\n\tch.mutable.Lock()\n\tdefer ch.mutable.Unlock()\n\n\tif c.readState() != connectionActive {\n\t\treturn false\n\t}\n\n\tswitch state := ch.mutable.state; state {\n\tcase ChannelClient, ChannelListening:\n\t\tbreak\n\tdefault:\n\t\treturn false\n\t}\n\n\tch.mutable.conns[c.connID] = c\n\treturn true\n}\n\nfunc (ch *Channel) connectionActive(c *Connection, direction connectionDirection) {\n\tc.log.Debugf(\"New active %v connection for peer %v\", direction, c.remotePeerInfo.HostPort)\n\n\tif added := ch.addConnection(c, direction); !added {\n\t\t// The channel isn't in a valid state to accept this connection, close the connection.\n\t\tc.close(LogField{\"reason\", \"new active connection on closing channel\"})\n\t\treturn\n\t}\n\n\tch.addConnectionToPeer(c.remotePeerInfo.HostPort, c, direction)\n}\n\nfunc (ch *Channel) addConnectionToPeer(hostPort string, c *Connection, direction connectionDirection) {\n\tp := ch.RootPeers().GetOrAdd(hostPort)\n\tif err := p.addConnection(c, direction); err != nil {\n\t\tc.log.WithFields(\n\t\t\tLogField{\"remoteHostPort\", c.remotePeerInfo.HostPort},\n\t\t\tLogField{\"direction\", direction},\n\t\t\tErrField(err),\n\t\t).Warn(\"Failed to add connection to peer.\")\n\t}\n\n\tch.updatePeer(p)\n}\n\nfunc (ch *Channel) inboundConnectionActive(c *Connection) {\n\tch.connectionActive(c, inbound)\n}\n\nfunc (ch *Channel) outboundConnectionActive(c *Connection) {\n\tch.connectionActive(c, outbound)\n}\n\n// removeClosedConn removes a connection if it's closed.\n// Until a connection is fully closed, the channel must keep track of it.\nfunc (ch *Channel) removeClosedConn(c *Connection) {\n\tif c.readState() != connectionClosed {\n\t\treturn\n\t}\n\n\tch.mutable.Lock()\n\tdelete(ch.mutable.conns, c.connID)\n\tch.mutable.Unlock()\n}\n\nfunc (ch *Channel) getMinConnectionState() connectionState {\n\tminState := connectionClosed\n\tfor _, c := range ch.mutable.conns {\n\t\tif s := c.readState(); s < minState {\n\t\t\tminState = s\n\t\t}\n\t}\n\treturn minState\n}\n\n// connectionCloseStateChange is called when a connection's close state changes.\nfunc (ch *Channel) connectionCloseStateChange(c *Connection) {\n\tch.removeClosedConn(c)\n\tif peer, ok := ch.RootPeers().Get(c.remotePeerInfo.HostPort); ok {\n\t\tpeer.connectionCloseStateChange(c)\n\t\tch.updatePeer(peer)\n\t}\n\tif c.outboundHP != \"\" && c.outboundHP != c.remotePeerInfo.HostPort {\n\t\t// Outbound connections may be in multiple peers.\n\t\tif peer, ok := ch.RootPeers().Get(c.outboundHP); ok {\n\t\t\tpeer.connectionCloseStateChange(c)\n\t\t\tch.updatePeer(peer)\n\t\t}\n\t}\n\n\tchState := ch.State()\n\tif chState != ChannelStartClose && chState != ChannelInboundClosed {\n\t\treturn\n\t}\n\n\tch.mutable.RLock()\n\tminState := ch.getMinConnectionState()\n\tch.mutable.RUnlock()\n\n\tvar updateTo ChannelState\n\tif minState >= connectionClosed {\n\t\tupdateTo = ChannelClosed\n\t} else if minState >= connectionInboundClosed && chState == ChannelStartClose {\n\t\tupdateTo = ChannelInboundClosed\n\t}\n\n\tvar updatedToState ChannelState\n\tif updateTo > 0 {\n\t\tch.mutable.Lock()\n\t\t// Recheck the state as it's possible another goroutine changed the state\n\t\t// from what we expected, and so we might make a stale change.\n\t\tif ch.mutable.state == chState {\n\t\t\tch.mutable.state = updateTo\n\t\t\tupdatedToState = updateTo\n\t\t}\n\t\tch.mutable.Unlock()\n\t\tchState = updateTo\n\t}\n\n\tc.log.Debugf(\"ConnectionCloseStateChange channel state = %v connection minState = %v\",\n\t\tchState, minState)\n\n\tif updatedToState == ChannelClosed {\n\t\tch.onClosed()\n\t}\n}\n\nfunc (ch *Channel) onClosed() {\n\tremoveClosedChannel(ch)\n\n\tclose(ch.closed)\n\tch.log.Infof(\"Channel closed.\")\n}\n\n// Closed returns whether this channel has been closed with .Close()\nfunc (ch *Channel) Closed() bool {\n\treturn ch.State() == ChannelClosed\n}\n\n// ClosedChan returns a channel that will close when the Channel has completely\n// closed.\nfunc (ch *Channel) ClosedChan() <-chan struct{} {\n\treturn ch.closed\n}\n\n// State returns the current channel state.\nfunc (ch *Channel) State() ChannelState {\n\tch.mutable.RLock()\n\tstate := ch.mutable.state\n\tch.mutable.RUnlock()\n\n\treturn state\n}\n\n// Close starts a graceful Close for the channel. This does not happen immediately:\n// 1. This call closes the Listener and starts closing connections.\n// 2. When all incoming connections are drained, the connection blocks new outgoing calls.\n// 3. When all connections are drained, the channel's state is updated to Closed.\nfunc (ch *Channel) Close() {\n\tch.Logger().Info(\"Channel.Close called.\")\n\tvar connections []*Connection\n\tvar channelClosed bool\n\n\tfunc() {\n\t\tch.mutable.Lock()\n\t\tdefer ch.mutable.Unlock()\n\n\t\tif ch.mutable.state == ChannelClosed {\n\t\t\tch.Logger().Info(\"Channel already closed, skipping additional Close() calls\")\n\t\t\treturn\n\t\t}\n\n\t\tif ch.mutable.l != nil {\n\t\t\tch.mutable.l.Close()\n\t\t}\n\n\t\t// Stop the idle connections timer.\n\t\tch.mutable.idleSweep.Stop()\n\n\t\tch.mutable.state = ChannelStartClose\n\t\tif len(ch.mutable.conns) == 0 {\n\t\t\tch.mutable.state = ChannelClosed\n\t\t\tchannelClosed = true\n\t\t}\n\t\tfor _, c := range ch.mutable.conns {\n\t\t\tconnections = append(connections, c)\n\t\t}\n\t}()\n\n\tfor _, c := range connections {\n\t\tc.close(LogField{\"reason\", \"channel closing\"})\n\t}\n\n\tif channelClosed {\n\t\tch.onClosed()\n\t}\n}\n\n// RelayHost returns the channel's RelayHost, if any.\nfunc (ch *Channel) RelayHost() RelayHost {\n\treturn ch.relayHost\n}\n\nfunc (o *ChannelOptions) validateIdleCheck() error {\n\tif o.IdleCheckInterval > 0 && o.MaxIdleTime <= 0 {\n\t\treturn errMaxIdleTimeNotSet\n\t}\n\n\treturn nil\n}\n\nfunc toStringSet(ss []string) map[string]struct{} {\n\tset := make(map[string]struct{}, len(ss))\n\tfor _, s := range ss {\n\t\tset[s] = struct{}{}\n\t}\n\treturn set\n}\n\n// take a list of service::method formatted string and make\n// the map[service::method]struct{} set\nfunc toServiceMethodSet(sms []string) (map[string]struct{}, error) {\n\tset := map[string]struct{}{}\n\tfor _, sm := range sms {\n\t\tif len(strings.Split(sm, \"::\")) != 2 {\n\t\t\treturn nil, fmt.Errorf(\"each %q value should be of service::Method format but got %q\", \"SkipHandlerMethods\", sm)\n\t\t}\n\t\tset[sm] = struct{}{}\n\t}\n\treturn set, nil\n}\n"
  },
  {
    "path": "channel_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"io/ioutil\"\n\t\"math\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/mocktracer\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc toMap(fields LogFields) map[string]interface{} {\n\tm := make(map[string]interface{})\n\tfor _, f := range fields {\n\t\tm[f.Key] = f.Value\n\t}\n\treturn m\n}\n\nfunc TestNewChannel(t *testing.T) {\n\tch, err := NewChannel(\"svc\", &ChannelOptions{\n\t\tProcessName: \"pname\",\n\t})\n\trequire.NoError(t, err, \"NewChannel failed\")\n\n\tassert.Equal(t, LocalPeerInfo{\n\t\tServiceName: \"svc\",\n\t\tPeerInfo: PeerInfo{\n\t\t\tProcessName: \"pname\",\n\t\t\tHostPort:    ephemeralHostPort,\n\t\t\tIsEphemeral: true,\n\t\t\tVersion: PeerVersion{\n\t\t\t\tLanguage:        \"go\",\n\t\t\t\tLanguageVersion: strings.TrimPrefix(runtime.Version(), \"go\"),\n\t\t\t\tTChannelVersion: VersionInfo,\n\t\t\t},\n\t\t},\n\t}, ch.PeerInfo(), \"Wrong local peer info\")\n}\n\nfunc TestLoggers(t *testing.T) {\n\tch, err := NewChannel(\"svc\", &ChannelOptions{\n\t\tLogger: NewLogger(ioutil.Discard),\n\t})\n\trequire.NoError(t, err, \"NewChannel failed\")\n\tdefer ch.Close()\n\n\tpeerInfo := ch.PeerInfo()\n\tfields := toMap(ch.Logger().Fields())\n\tassert.Equal(t, peerInfo.ServiceName, fields[\"serviceName\"])\n\n\tsc := ch.GetSubChannel(\"subch\")\n\tfields = toMap(sc.Logger().Fields())\n\tassert.Equal(t, peerInfo.ServiceName, fields[\"serviceName\"])\n\tassert.Equal(t, \"subch\", fields[\"subchannel\"])\n}\n\nfunc TestStats(t *testing.T) {\n\tch, err := NewChannel(\"svc\", &ChannelOptions{\n\t\tLogger: NewLogger(ioutil.Discard),\n\t})\n\trequire.NoError(t, err, \"NewChannel failed\")\n\tdefer ch.Close()\n\n\thostname, err := os.Hostname()\n\trequire.NoError(t, err, \"Hostname failed\")\n\n\tpeerInfo := ch.PeerInfo()\n\ttags := ch.StatsTags()\n\tassert.NotNil(t, ch.StatsReporter(), \"StatsReporter missing\")\n\tassert.Equal(t, peerInfo.ProcessName, tags[\"app\"], \"app tag\")\n\tassert.Equal(t, peerInfo.ServiceName, tags[\"service\"], \"service tag\")\n\tassert.Equal(t, hostname, tags[\"host\"], \"hostname tag\")\n\n\tsc := ch.GetSubChannel(\"subch\")\n\tsubTags := sc.StatsTags()\n\tassert.NotNil(t, sc.StatsReporter(), \"StatsReporter missing\")\n\tfor k, v := range tags {\n\t\tassert.Equal(t, v, subTags[k], \"subchannel missing tag %v\", k)\n\t}\n\tassert.Equal(t, \"subch\", subTags[\"subchannel\"], \"subchannel tag missing\")\n}\n\nfunc TestRelayMaxTTL(t *testing.T) {\n\ttests := []struct {\n\t\tmax      time.Duration\n\t\texpected time.Duration\n\t}{\n\t\t{time.Second, time.Second},\n\t\t{-time.Second, _defaultRelayMaxTimeout},\n\t\t{0, _defaultRelayMaxTimeout},\n\t\t{time.Microsecond, _defaultRelayMaxTimeout},\n\t\t{math.MaxUint32 * time.Millisecond, math.MaxUint32 * time.Millisecond},\n\t\t{(math.MaxUint32 + 1) * time.Millisecond, _defaultRelayMaxTimeout},\n\t}\n\n\tfor _, tt := range tests {\n\t\tch, err := NewChannel(\"svc\", &ChannelOptions{\n\t\t\tRelayMaxTimeout: tt.max,\n\t\t})\n\t\tassert.NoError(t, err, \"Unexpected error when creating channel.\")\n\t\tassert.Equal(t, ch.relayMaxTimeout, tt.expected, \"Unexpected max timeout on channel.\")\n\t}\n}\n\nfunc TestIsolatedSubChannelsDontSharePeers(t *testing.T) {\n\tch, err := NewChannel(\"svc\", &ChannelOptions{\n\t\tLogger: NewLogger(ioutil.Discard),\n\t})\n\trequire.NoError(t, err, \"NewChannel failed\")\n\tdefer ch.Close()\n\n\tsub := ch.GetSubChannel(\"svc-ringpop\")\n\tif ch.peers != sub.peers {\n\t\tt.Log(\"Channel and subchannel don't share the same peer list.\")\n\t\tt.Fail()\n\t}\n\n\tisolatedSub := ch.GetSubChannel(\"svc-shy-ringpop\", Isolated)\n\tif ch.peers == isolatedSub.peers {\n\t\tt.Log(\"Channel and isolated subchannel share the same peer list.\")\n\t\tt.Fail()\n\t}\n\n\t// Nobody knows about the peer.\n\tassert.Nil(t, ch.peers.peersByHostPort[\"127.0.0.1:3000\"])\n\tassert.Nil(t, sub.peers.peersByHostPort[\"127.0.0.1:3000\"])\n\tassert.Nil(t, isolatedSub.peers.peersByHostPort[\"127.0.0.1:3000\"])\n\n\t// Uses of the parent channel should be reflected in the subchannel, but\n\t// not the isolated subchannel.\n\tch.Peers().Add(\"127.0.0.1:3000\")\n\tassert.NotNil(t, ch.peers.peersByHostPort[\"127.0.0.1:3000\"])\n\tassert.NotNil(t, sub.peers.peersByHostPort[\"127.0.0.1:3000\"])\n\tassert.Nil(t, isolatedSub.peers.peersByHostPort[\"127.0.0.1:3000\"])\n}\n\nfunc TestChannelTracerMethod(t *testing.T) {\n\tmockTracer := mocktracer.New()\n\tch, err := NewChannel(\"svc\", &ChannelOptions{\n\t\tTracer: mockTracer,\n\t})\n\trequire.NoError(t, err)\n\tdefer ch.Close()\n\tassert.Equal(t, mockTracer, ch.Tracer(), \"expecting tracer passed at initialization\")\n\n\tch, err = NewChannel(\"svc\", &ChannelOptions{})\n\trequire.NoError(t, err)\n\tdefer ch.Close()\n\tassert.EqualValues(t, opentracing.GlobalTracer(), ch.Tracer(), \"expecting default tracer\")\n\n\t// because ch.Tracer() function is doing dynamic lookup, we can change global tracer\n\torigTracer := opentracing.GlobalTracer()\n\tdefer opentracing.InitGlobalTracer(origTracer)\n\n\topentracing.InitGlobalTracer(mockTracer)\n\tassert.Equal(t, mockTracer, ch.Tracer(), \"expecting tracer set as global tracer\")\n}\n\nfunc TestToServiceMethodSet(t *testing.T) {\n\ttests := []struct {\n\t\tdesc    string\n\t\tsms     []string\n\t\twant    map[string]struct{}\n\t\twantErr string\n\t}{\n\t\t{\n\t\t\tdesc: \"single service, single method\",\n\t\t\tsms:  []string{\"service::Method\"},\n\t\t\twant: map[string]struct{}{\n\t\t\t\t\"service::Method\": struct{}{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"single service, multiple methods\",\n\t\t\tsms:  []string{\"service::Method1\", \"service::Method2\", \"service::Method3\"},\n\t\t\twant: map[string]struct{}{\n\t\t\t\t\"service::Method1\": struct{}{},\n\t\t\t\t\"service::Method2\": struct{}{},\n\t\t\t\t\"service::Method3\": struct{}{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"invalid input\",\n\t\t\tsms:     []string{\"notDelimitedByDoubleColons\"},\n\t\t\twantErr: `each \"SkipHandlerMethods\" value should be of service::Method format but got \"notDelimitedByDoubleColons\"`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tr, err := toServiceMethodSet(tt.sms)\n\t\t\tif tt.wantErr != \"\" {\n\t\t\t\tassert.EqualError(t, err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tassert.Equal(t, tt.want, r)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "channel_utils_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"testing\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/testutils\"\n)\n\n// NewServer creates a new server and returns the channel, service name, and host port.\nfunc NewServer(t testing.TB, opts *testutils.ChannelOpts) (*Channel, string, string) {\n\tch := testutils.NewServer(t, opts)\n\tpeerInfo := ch.PeerInfo()\n\treturn ch, peerInfo.ServiceName, peerInfo.HostPort\n}\n"
  },
  {
    "path": "channelstate_string.go",
    "content": "// generated by stringer -type=ChannelState; DO NOT EDIT\n\npackage tchannel\n\nimport \"fmt\"\n\nconst _ChannelState_name = \"ChannelClientChannelListeningChannelStartCloseChannelInboundClosedChannelClosed\"\n\nvar _ChannelState_index = [...]uint8{0, 13, 29, 46, 66, 79}\n\nfunc (i ChannelState) String() string {\n\ti -= 1\n\tif i < 0 || i+1 >= ChannelState(len(_ChannelState_index)) {\n\t\treturn fmt.Sprintf(\"ChannelState(%d)\", i+1)\n\t}\n\treturn _ChannelState_name[_ChannelState_index[i]:_ChannelState_index[i+1]]\n}\n"
  },
  {
    "path": "checked_frame_pool.go",
    "content": "package tchannel\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"sync\"\n)\n\n// CheckedFramePoolForTest tracks gets and releases of frames, verifies that\n// frames aren't double released, and can be used to check for frame leaks.\n// As such, it is not performant, nor is it even a proper frame pool.\n//\n// It is intended to be used ONLY in tests.\ntype CheckedFramePoolForTest struct {\n\tmu sync.Mutex\n\n\tallocations map[*Frame]string\n\tbadRelease  []string\n}\n\n// NewCheckedFramePoolForTest initializes a new CheckedFramePoolForTest.\nfunc NewCheckedFramePoolForTest() *CheckedFramePoolForTest {\n\treturn &CheckedFramePoolForTest{\n\t\tallocations: make(map[*Frame]string),\n\t}\n}\n\n// Get implements FramePool\nfunc (p *CheckedFramePoolForTest) Get() *Frame {\n\tp.mu.Lock()\n\tdefer p.mu.Unlock()\n\n\tframe := NewFrame(MaxFramePayloadSize)\n\tp.allocations[frame] = recordStack()\n\treturn frame\n}\n\n// Release implements FramePool\nfunc (p *CheckedFramePoolForTest) Release(f *Frame) {\n\t// Make sure the payload is not used after this point by clearing the frame.\n\tzeroOut(f.Payload)\n\tf.Payload = nil\n\tzeroOut(f.buffer)\n\tf.buffer = nil\n\tzeroOut(f.headerBuffer)\n\tf.headerBuffer = nil\n\tf.Header = FrameHeader{}\n\n\tp.mu.Lock()\n\tdefer p.mu.Unlock()\n\n\tif _, ok := p.allocations[f]; !ok {\n\t\tp.badRelease = append(p.badRelease, \"bad Release at \"+recordStack())\n\t\treturn\n\t}\n\n\tdelete(p.allocations, f)\n}\n\n// CheckedFramePoolForTestResult contains info on mismatched gets/releases\ntype CheckedFramePoolForTestResult struct {\n\tBadReleases []string\n\tUnreleased  []string\n}\n\n// HasIssues indicates whether there were any issues with gets/releases\nfunc (r CheckedFramePoolForTestResult) HasIssues() bool {\n\treturn len(r.BadReleases)+len(r.Unreleased) > 0\n}\n\n// CheckEmpty returns the number of unreleased frames in the pool\nfunc (p *CheckedFramePoolForTest) CheckEmpty() CheckedFramePoolForTestResult {\n\tp.mu.Lock()\n\tdefer p.mu.Unlock()\n\n\tvar badCalls []string\n\tfor f, s := range p.allocations {\n\t\tbadCalls = append(badCalls, fmt.Sprintf(\"frame %p: %v not released, get from: %v\", f, f.Header, s))\n\t}\n\n\treturn CheckedFramePoolForTestResult{\n\t\tUnreleased:  badCalls,\n\t\tBadReleases: p.badRelease,\n\t}\n}\n\nfunc recordStack() string {\n\tbuf := make([]byte, 4096)\n\truntime.Stack(buf, false /* all */)\n\treturn string(buf)\n}\n\nfunc zeroOut(bs []byte) {\n\tfor i := range bs {\n\t\tbs[i] = 0\n\t}\n}\n"
  },
  {
    "path": "checked_frame_pool_test.go",
    "content": "package tchannel\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/uber/tchannel-go/testutils/goroutines\"\n)\n\nfunc TestCheckedFramePoolForTest(t *testing.T) {\n\ttests := []struct {\n\t\tmsg                string\n\t\toperations         func(pool *CheckedFramePoolForTest)\n\t\twantHasIssues      bool\n\t\twantBadAllocations int\n\t\twantBadReleases    int\n\t}{\n\t\t{\n\t\t\tmsg: \"no bad releases or leaks\",\n\t\t\toperations: func(pool *CheckedFramePoolForTest) {\n\t\t\t\tfor i := 0; i < 10; i++ {\n\t\t\t\t\tpool.Release(pool.Get())\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"frames are leaked\",\n\t\t\toperations: func(pool *CheckedFramePoolForTest) {\n\t\t\t\tfor i := 0; i < 10; i++ {\n\t\t\t\t\tpool.Release(pool.Get())\n\t\t\t\t}\n\t\t\t\tfor i := 0; i < 10; i++ {\n\t\t\t\t\t_ = pool.Get()\n\t\t\t\t}\n\t\t\t},\n\t\t\twantHasIssues:      true,\n\t\t\twantBadAllocations: 10,\n\t\t},\n\t\t{\n\t\t\tmsg: \"frames are double released\",\n\t\t\toperations: func(pool *CheckedFramePoolForTest) {\n\t\t\t\tfor i := 0; i < 10; i++ {\n\t\t\t\t\tpool.Release(pool.Get())\n\t\t\t\t}\n\t\t\t\tf := pool.Get()\n\t\t\t\tpool.Release(f)\n\t\t\t\tpool.Release(f)\n\t\t\t},\n\t\t\twantHasIssues:   true,\n\t\t\twantBadReleases: 1,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\tpool := NewCheckedFramePoolForTest()\n\n\t\t\ttt.operations(pool)\n\t\t\tresults := pool.CheckEmpty()\n\n\t\t\tassert.Equal(t, tt.wantHasIssues, results.HasIssues(), \"Unexpected HasIssues() state\")\n\t\t\tassert.Equal(t, tt.wantBadAllocations, len(results.Unreleased), \"Unexpected allocs\")\n\t\t\tassert.Equal(t, tt.wantBadReleases, len(results.BadReleases), \"Unexpected bad releases\")\n\t\t})\n\t}\n}\n\nfunc CheckFramePoolIsEmpty(t testing.TB, pool *CheckedFramePoolForTest) {\n\tt.Helper()\n\n\tstacks := goroutines.GetAll()\n\tif result := pool.CheckEmpty(); result.HasIssues() {\n\t\tif len(result.Unreleased) > 0 {\n\t\t\tt.Errorf(\"Frame pool has %v unreleased frames, errors:\\n%v\\nStacks:%v\",\n\t\t\t\tlen(result.Unreleased), strings.Join(result.Unreleased, \"\\n\"), stacks)\n\t\t}\n\t\tif len(result.BadReleases) > 0 {\n\t\t\tt.Errorf(\"Frame pool has %v bad releases, errors:\\n%v\\nStacks:%v\",\n\t\t\t\tlen(result.BadReleases), strings.Join(result.BadReleases, \"\\n\"), stacks)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "checksum.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"hash\"\n\t\"hash/crc32\"\n\t\"sync\"\n)\n\nvar checksumPools [checksumCount]sync.Pool\n\n// A ChecksumType is a checksum algorithm supported by TChannel for checksumming call bodies\ntype ChecksumType byte\n\nconst (\n\t// ChecksumTypeNone indicates no checksum is included in the message\n\tChecksumTypeNone ChecksumType = 0\n\n\t// ChecksumTypeCrc32 indicates the message checksum is calculated using crc32\n\tChecksumTypeCrc32 ChecksumType = 1\n\n\t// ChecksumTypeFarmhash indicates the message checksum is calculated using Farmhash\n\tChecksumTypeFarmhash ChecksumType = 2\n\n\t// ChecksumTypeCrc32C indicates the message checksum is calculated using crc32c\n\tChecksumTypeCrc32C ChecksumType = 3\n\n\tchecksumCount = 4\n)\n\nfunc init() {\n\tcrc32CastagnoliTable := crc32.MakeTable(crc32.Castagnoli)\n\n\tChecksumTypeNone.pool().New = func() interface{} {\n\t\treturn nullChecksum{}\n\t}\n\tChecksumTypeCrc32.pool().New = func() interface{} {\n\t\treturn newHashChecksum(ChecksumTypeCrc32, crc32.NewIEEE())\n\t}\n\tChecksumTypeCrc32C.pool().New = func() interface{} {\n\t\treturn newHashChecksum(ChecksumTypeCrc32C, crc32.New(crc32CastagnoliTable))\n\t}\n\n\t// TODO: Implement farm hash.\n\tChecksumTypeFarmhash.pool().New = func() interface{} {\n\t\treturn nullChecksum{}\n\t}\n}\n\n// ChecksumSize returns the size in bytes of the checksum calculation\nfunc (t ChecksumType) ChecksumSize() int {\n\tswitch t {\n\tcase ChecksumTypeNone:\n\t\treturn 0\n\tcase ChecksumTypeCrc32, ChecksumTypeCrc32C:\n\t\treturn crc32.Size\n\tcase ChecksumTypeFarmhash:\n\t\treturn 4\n\tdefault:\n\t\treturn 0\n\t}\n}\n\n// pool returns the sync.Pool used to pool checksums for this type.\nfunc (t ChecksumType) pool() *sync.Pool {\n\treturn &checksumPools[int(t)]\n}\n\n// New creates a new Checksum of the given type\nfunc (t ChecksumType) New() Checksum {\n\ts := t.pool().Get().(Checksum)\n\ts.Reset()\n\treturn s\n}\n\n// Release puts a Checksum back in the pool.\nfunc (t ChecksumType) Release(checksum Checksum) {\n\tt.pool().Put(checksum)\n}\n\n// A Checksum calculates a running checksum against a bytestream\ntype Checksum interface {\n\t// TypeCode returns the type of this checksum\n\tTypeCode() ChecksumType\n\n\t// Size returns the size of the calculated checksum\n\tSize() int\n\n\t// Add adds bytes to the checksum calculation\n\tAdd(b []byte) []byte\n\n\t// Sum returns the current checksum value\n\tSum() []byte\n\n\t// Release puts a Checksum back in the pool.\n\tRelease()\n\n\t// Reset resets the checksum state to the default 0 value.\n\tReset()\n}\n\n// No checksum\ntype nullChecksum struct{}\n\n// TypeCode returns the type of the checksum\nfunc (c nullChecksum) TypeCode() ChecksumType { return ChecksumTypeNone }\n\n// Size returns the size of the checksum data, in the case the null checksum this is zero\nfunc (c nullChecksum) Size() int { return 0 }\n\n// Add adds a byteslice to the checksum calculation\nfunc (c nullChecksum) Add(b []byte) []byte { return nil }\n\n// Sum returns the current checksum calculation\nfunc (c nullChecksum) Sum() []byte { return nil }\n\n// Release puts a Checksum back in the pool.\nfunc (c nullChecksum) Release() {\n\tc.TypeCode().Release(c)\n}\n\n// Reset resets the checksum state to the default 0 value.\nfunc (c nullChecksum) Reset() {}\n\n// Hash Checksum\ntype hashChecksum struct {\n\tchecksumType ChecksumType\n\thash         hash.Hash\n\tsumCache     []byte\n}\n\nfunc newHashChecksum(t ChecksumType, hash hash.Hash) *hashChecksum {\n\treturn &hashChecksum{\n\t\tchecksumType: t,\n\t\thash:         hash,\n\t\tsumCache:     make([]byte, 0, 4),\n\t}\n}\n\n// TypeCode returns the type of the checksum\nfunc (h *hashChecksum) TypeCode() ChecksumType { return h.checksumType }\n\n// Size returns the size of the checksum data\nfunc (h *hashChecksum) Size() int { return h.hash.Size() }\n\n// Add adds a byte slice to the checksum calculation\nfunc (h *hashChecksum) Add(b []byte) []byte { h.hash.Write(b); return h.Sum() }\n\n// Sum returns the current value of the checksum calculation\nfunc (h *hashChecksum) Sum() []byte { return h.hash.Sum(h.sumCache) }\n\n// Release puts a Checksum back in the pool.\nfunc (h *hashChecksum) Release() { h.TypeCode().Release(h) }\n\n// Reset resets the checksum state to the default 0 value.\nfunc (h *hashChecksum) Reset() { h.hash.Reset() }\n\n// noReleaseChecksum overrides .Release() with a NOOP so that the checksum won't\n// be released by the fragmentingWriter when it is managed externally, e.g. by the\n// relayer\ntype noReleaseChecksum struct {\n\tChecksum\n}\n\nfunc (n *noReleaseChecksum) Release() {}\n"
  },
  {
    "path": "close_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"math/rand\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\t\"github.com/uber/tchannel-go/testutils/goroutines\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\ntype channelState struct {\n\ttestServer *testutils.TestServer\n\tcloseCh    chan struct{}\n\tclosed     bool\n}\n\nfunc makeCall(client *Channel, server *testutils.TestServer) error {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\t_, _, _, err := raw.Call(ctx, client, server.HostPort(), server.ServiceName(), \"test\", nil, nil)\n\treturn err\n}\n\nfunc assertStateChangesTo(t testing.TB, ch *Channel, state ChannelState) {\n\tvar lastState ChannelState\n\trequire.True(t, testutils.WaitFor(time.Second, func() bool {\n\t\tlastState = ch.State()\n\t\treturn lastState == state\n\t}), \"Channel state is %v expected %v\", lastState, state)\n}\n\nfunc TestCloseOnlyListening(t *testing.T) {\n\tch := testutils.NewServer(t, nil)\n\n\t// If there are no connections, then the channel should close immediately.\n\tch.Close()\n\tassert.Equal(t, ChannelClosed, ch.State())\n\tassert.True(t, ch.Closed(), \"Channel should be closed\")\n}\n\nfunc TestCloseNewClient(t *testing.T) {\n\tch := testutils.NewClient(t, nil)\n\n\t// If there are no connections, then the channel should close immediately.\n\tch.Close()\n\tassert.Equal(t, ChannelClosed, ch.State())\n\tassert.True(t, ch.Closed(), \"Channel should be closed\")\n}\n\nfunc ignoreError(h *testHandler) Handler {\n\treturn raw.Wrap(onErrorTestHandler{\n\t\ttestHandler: h,\n\t\tonError:     func(_ context.Context, err error) {},\n\t})\n}\n\nfunc TestCloseAfterTimeout(t *testing.T) {\n\t// Disable log verfication since connections are closed after a timeout\n\t// and the relay might still be reading/writing to the connection.\n\t// TODO: Ideally, we only disable log verification on the relay.\n\topts := testutils.NewOpts().DisableLogVerification()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttestHandler := newTestHandler(t)\n\t\tts.Register(ignoreError(testHandler), \"block\")\n\n\t\tctx, cancel := NewContext(100 * time.Millisecond)\n\t\tdefer cancel()\n\n\t\t// Make a call, wait for it to timeout.\n\t\tclientCh := ts.NewClient(nil)\n\t\t_, _, _, err := raw.Call(ctx, clientCh, ts.HostPort(), ts.ServiceName(), \"block\", nil, nil)\n\t\trequire.Equal(t, ErrTimeout, err, \"Expected call to timeout\")\n\n\t\t// The client channel should also close immediately.\n\t\tclientCh.Close()\n\t\tassertStateChangesTo(t, clientCh, ChannelClosed)\n\t\tassert.True(t, clientCh.Closed(), \"Channel should be closed\")\n\t})\n}\n\nfunc TestRelayCloseTimeout(t *testing.T) {\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().          // this is a relay-specific test.\n\t\tDisableLogVerification() // we're causing errors on purpose.\n\topts.DefaultConnectionOptions.MaxCloseTime = 100 * time.Millisecond\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tgotCall := make(chan struct{})\n\t\tunblock := make(chan struct{})\n\t\tdefer close(unblock)\n\n\t\ttestutils.RegisterEcho(ts.Server(), func() {\n\t\t\tclose(gotCall)\n\t\t\t<-unblock\n\t\t})\n\n\t\tclientCh := ts.NewClient(opts)\n\n\t\t// Start a call in the background, since it will block\n\t\tgo func() {\n\t\t\tctx, cancel := NewContext(10 * time.Second)\n\t\t\tdefer cancel()\n\n\t\t\t_, _, _, err := raw.Call(ctx, clientCh, ts.HostPort(), ts.ServiceName(), \"echo\", nil, nil)\n\t\t\trequire.Error(t, err)\n\t\t\tassert.Equal(t, ErrCodeNetwork, GetSystemErrorCode(err),\n\t\t\t\t\"expect network error from relay closing connection on timeout\")\n\t\t}()\n\n\t\t<-gotCall\n\t\tts.Relay().Close()\n\n\t\t// The relay should close within the timeout.\n\t\t<-ts.Relay().ClosedChan()\n\t})\n}\n\nfunc TestRaceExchangesWithClose(t *testing.T) {\n\tvar wg sync.WaitGroup\n\n\topts := testutils.NewOpts().DisableLogVerification()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tvar (\n\t\t\tserver       = ts.Server()\n\t\t\tgotCall      = make(chan struct{})\n\t\t\tcompleteCall = make(chan struct{})\n\t\t)\n\n\t\ttestutils.RegisterFunc(server, \"dummy\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\treturn &raw.Res{}, nil\n\t\t})\n\n\t\ttestutils.RegisterEcho(server, func() {\n\t\t\tclose(gotCall)\n\t\t\t<-completeCall\n\t\t})\n\n\t\tclient := ts.NewClient(opts)\n\t\tdefer client.Close()\n\n\t\tcallDone := make(chan struct{})\n\t\tgo func() {\n\t\t\t// n.b. Use a longer context here; server shutdown is inherently\n\t\t\t//      nondeterministic, and now that it's blocking on channel and\n\t\t\t//      connection closure, it can take anywhere from 0-2s to fully\n\t\t\t//      close all of its internals.\n\t\t\tctx, cancel := context.WithTimeout(\n\t\t\t\tcontext.Background(),\n\t\t\t\ttestutils.Timeout(5*time.Second),\n\t\t\t)\n\t\t\tdefer cancel()\n\n\t\t\tassert.NoError(\n\t\t\t\tt,\n\t\t\t\ttestutils.CallEchoWithContext(\n\t\t\t\t\tctx,\n\t\t\t\t\tclient,\n\t\t\t\t\tts.HostPort(),\n\t\t\t\t\tserver.ServiceName(),\n\t\t\t\t\t&raw.Args{},\n\t\t\t\t),\n\t\t\t\t\"Echo failed\",\n\t\t\t)\n\t\t\tclose(callDone)\n\t\t}()\n\n\t\t// Wait until the server recieves a call, so it has an active inbound.\n\t\t<-gotCall\n\n\t\t// Start a bunch of clients to trigger races between connecting and close.\n\t\tvar closed atomic.Bool\n\t\tfor i := 0; i < 100; i++ {\n\t\t\twg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\t// We don't use ts.NewClient here to avoid data races.\n\t\t\t\tc := testutils.NewClient(t, opts)\n\t\t\t\tdefer c.Close()\n\n\t\t\t\tif closed.Load() {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tctx, cancel := NewContext(testutils.Timeout(time.Second))\n\t\t\t\tdefer cancel()\n\n\t\t\t\tif err := c.Ping(ctx, ts.HostPort()); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif closed.Load() {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\traw.Call(ctx, c, ts.HostPort(), server.ServiceName(), \"dummy\", nil, nil)\n\t\t\t}()\n\t\t}\n\n\t\t// Now try to close the channel, it should block since there's active exchanges.\n\t\tserver.Close()\n\t\tclosed.Store(true)\n\n\t\t// n.b. As it's shutting down, server state can be in any of the\n\t\t//      outlined states below. It doesn't matter which specific state\n\t\t//      it's in, as long as we're verifying that it's at least in the\n\t\t//      process of shutting down.\n\t\tvar (\n\t\t\ttimeout    = time.After(testutils.Timeout(time.Second))\n\t\t\tvalidState = func() bool {\n\t\t\t\tswitch ts.Server().State() {\n\t\t\t\tcase ChannelStartClose, ChannelInboundClosed, ChannelClosed:\n\t\t\t\t\treturn true\n\t\t\t\tdefault:\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t)\n\n\t\tticker := time.NewTicker(25 * time.Millisecond)\n\t\tdefer ticker.Stop()\n\n\t\tfor !validState() {\n\t\t\tselect {\n\t\t\tcase <-ticker.C:\n\t\t\tcase <-timeout:\n\t\t\t\trequire.FailNow(\n\t\t\t\t\tt,\n\t\t\t\t\t\"server state did not transition as expected: %v\",\n\t\t\t\t\tts.Server().State(),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tclosed.Store(true)\n\t\tclose(completeCall)\n\t\t<-callDone\n\t})\n\n\t// Wait for all calls to complete\n\twg.Wait()\n}\n\n// TestCloseStress ensures that once a Channel is closed, it cannot be reached.\nfunc TestCloseStress(t *testing.T) {\n\tCheckStress(t)\n\n\tconst numHandlers = 5\n\thandler := &swapper{t}\n\tvar lock sync.RWMutex\n\tvar wg sync.WaitGroup\n\tvar channels []*channelState\n\n\t// Start numHandlers servers, and don't close the connections till they are signalled.\n\tfor i := 0; i < numHandlers; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\t\tts.Register(raw.Wrap(handler), \"test\")\n\n\t\t\t\tchState := &channelState{\n\t\t\t\t\ttestServer: ts,\n\t\t\t\t\tcloseCh:    make(chan struct{}),\n\t\t\t\t}\n\n\t\t\t\tlock.Lock()\n\t\t\t\tchannels = append(channels, chState)\n\t\t\t\tlock.Unlock()\n\t\t\t\twg.Done()\n\n\t\t\t\t// Wait for a close signal.\n\t\t\t\t<-chState.closeCh\n\n\t\t\t\t// Lock until the connection is closed.\n\t\t\t\tlock.Lock()\n\t\t\t\tchState.closed = true\n\t\t\t})\n\t\t}()\n\t}\n\n\t// Wait till all the channels have been registered.\n\twg.Wait()\n\n\t// Start goroutines to make calls until the test has ended.\n\ttestEnded := make(chan struct{})\n\tfor i := 0; i < 10; i++ {\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-testEnded:\n\t\t\t\t\treturn\n\t\t\t\tdefault:\n\t\t\t\t\t// Keep making requests till the test ends.\n\t\t\t\t}\n\n\t\t\t\t// Get 2 random channels and make a call from one to the other.\n\t\t\t\tlock.RLock()\n\t\t\t\tchState1 := channels[rand.Intn(len(channels))]\n\t\t\t\tchState2 := channels[rand.Intn(len(channels))]\n\t\t\t\tif chState1 == chState2 {\n\t\t\t\t\tlock.RUnlock()\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Grab a read lock to make sure channels aren't closed while we call.\n\t\t\t\tch1Closed := chState1.closed\n\t\t\t\tch2Closed := chState2.closed\n\t\t\t\terr := makeCall(chState1.testServer.NewClient(nil), chState2.testServer)\n\t\t\t\tlock.RUnlock()\n\t\t\t\tif ch1Closed || ch2Closed {\n\t\t\t\t\tassert.Error(\n\t\t\t\t\t\tt,\n\t\t\t\t\t\terr,\n\t\t\t\t\t\t\"Call from %v (%v) to %v (%v) should fail\",\n\t\t\t\t\t\tchState1.testServer.ServiceName(),\n\t\t\t\t\t\tchState1.testServer.HostPort(),\n\t\t\t\t\t\tchState2.testServer.ServiceName(),\n\t\t\t\t\t\tchState2.testServer.HostPort(),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tassert.NoError(\n\t\t\t\t\t\tt,\n\t\t\t\t\t\terr,\n\t\t\t\t\t\t\"Call from %v (%v) to %v (%v) should not fail\",\n\t\t\t\t\t\tchState1.testServer.ServiceName(),\n\t\t\t\t\t\tchState1.testServer.HostPort(),\n\t\t\t\t\t\tchState2.testServer.ServiceName(),\n\t\t\t\t\t\tchState2.testServer.HostPort(),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Kill connections till all of the connections are dead.\n\tfor i := 0; i < numHandlers; i++ {\n\t\ttime.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond)\n\t\tchannels[i].closeCh <- struct{}{}\n\t}\n}\n\ntype closeSemanticsTest struct {\n\t*testing.T\n\tisolated bool\n}\n\nfunc (t *closeSemanticsTest) makeServer(name string) (*Channel, chan struct{}) {\n\tch := testutils.NewServer(t.T, &testutils.ChannelOpts{ServiceName: name})\n\n\tc := make(chan struct{})\n\ttestutils.RegisterFunc(ch, \"stream\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t<-c\n\t\treturn &raw.Res{}, nil\n\t})\n\ttestutils.RegisterFunc(ch, \"call\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\treturn &raw.Res{}, nil\n\t})\n\treturn ch, c\n}\n\nfunc (t *closeSemanticsTest) withNewClient(f func(ch *Channel)) {\n\tch := testutils.NewClient(t.T, &testutils.ChannelOpts{ServiceName: \"client\"})\n\tf(ch)\n\tch.Close()\n}\n\nfunc (t *closeSemanticsTest) startCall(from *Channel, to *Channel, method string) (*OutboundCall, error) {\n\tctx, _ := NewContext(time.Second)\n\tvar call *OutboundCall\n\tvar err error\n\ttoPeer := to.PeerInfo()\n\tif t.isolated {\n\t\tsc := from.GetSubChannel(toPeer.ServiceName, Isolated)\n\t\tsc.Peers().Add(toPeer.HostPort)\n\t\tcall, err = sc.BeginCall(ctx, method, nil)\n\t} else {\n\t\tcall, err = from.BeginCall(ctx, toPeer.HostPort, toPeer.ServiceName, method, nil)\n\t}\n\treturn call, err\n}\n\nfunc (t *closeSemanticsTest) call(from *Channel, to *Channel) error {\n\tcall, err := t.startCall(from, to, \"call\")\n\tif err == nil {\n\t\t_, _, _, err = raw.WriteArgs(call, nil, nil)\n\t}\n\treturn err\n}\n\nfunc (t *closeSemanticsTest) callStream(from *Channel, to *Channel) <-chan struct{} {\n\tc := make(chan struct{})\n\n\tcall, err := t.startCall(from, to, \"stream\")\n\trequire.NoError(t, err, \"stream call failed to start\")\n\trequire.NoError(t, NewArgWriter(call.Arg2Writer()).Write(nil), \"write arg2\")\n\trequire.NoError(t, NewArgWriter(call.Arg3Writer()).Write(nil), \"write arg3\")\n\n\tgo func() {\n\t\tvar d []byte\n\t\tassert.NoError(t, NewArgReader(call.Response().Arg2Reader()).Read(&d), \"read arg2 from %v to %v\", from.PeerInfo(), to.PeerInfo())\n\t\tassert.NoError(t, NewArgReader(call.Response().Arg3Reader()).Read(&d), \"read arg3\")\n\t\tc <- struct{}{}\n\t}()\n\n\treturn c\n}\n\nfunc (t *closeSemanticsTest) runTest() {\n\ts1, s1C := t.makeServer(\"s1\")\n\ts2, s2C := t.makeServer(\"s2\")\n\n\t// Make a call from s1 -> s2, and s2 -> s1\n\tcall1 := t.callStream(s1, s2)\n\tcall2 := t.callStream(s2, s1)\n\n\t// s1 and s2 are both open, so calls to it should be successful.\n\tt.withNewClient(func(ch *Channel) {\n\t\trequire.NoError(t, t.call(ch, s1), \"failed to call s1\")\n\t\trequire.NoError(t, t.call(ch, s2), \"failed to call s2\")\n\t})\n\trequire.NoError(t, t.call(s1, s2), \"call s1 -> s2 failed\")\n\trequire.NoError(t, t.call(s2, s1), \"call s2 -> s1 failed\")\n\n\t// Close s1, should no longer be able to call it.\n\ts1.Close()\n\tassert.Equal(t, ChannelStartClose, s1.State())\n\n\tt.withNewClient(func(ch *Channel) {\n\t\tassert.Error(t, t.call(ch, s1), \"closed channel should not accept incoming calls\")\n\t\trequire.NoError(t, t.call(ch, s2),\n\t\t\t\"closed channel with pending incoming calls should allow outgoing calls\")\n\t})\n\n\t// Even an existing connection (e.g. from s2) should fail.\n\t// TODO: this will fail until the peer is shared.\n\tif !assert.Equal(t, ErrChannelClosed, t.call(s2, s1),\n\t\t\"closed channel should not accept incoming calls\") {\n\t\tt.Errorf(\"err %v\", t.call(s2, s1))\n\t}\n\n\trequire.Error(t, t.call(s1, s2),\n\t\t\"closed channel with pending incoming calls disallows outgoing calls\")\n\n\t// Once the incoming connection is drained, outgoing calls should fail.\n\ts1C <- struct{}{}\n\t<-call2\n\tassertStateChangesTo(t.T, s1, ChannelInboundClosed)\n\trequire.Error(t, t.call(s1, s2),\n\t\t\"closed channel with no pending incoming calls should not allow outgoing calls\")\n\n\t// Now the channel should be completely closed as there are no pending connections.\n\ts2C <- struct{}{}\n\t<-call1\n\tassertStateChangesTo(t.T, s1, ChannelClosed)\n\n\t// Close s2 so we don't leave any goroutines running.\n\ts2.Close()\n}\n\nfunc TestCloseSemantics(t *testing.T) {\n\t// We defer the check as we want it to run after the SetTimeout clears the timeout.\n\tdefer goroutines.VerifyNoLeaks(t, nil)\n\tdefer testutils.SetTimeout(t, 2*time.Second)()\n\n\tct := &closeSemanticsTest{t, false /* isolated */}\n\tct.runTest()\n}\n\nfunc TestCloseSemanticsIsolated(t *testing.T) {\n\t// We defer the check as we want it to run after the SetTimeout clears the timeout.\n\tdefer goroutines.VerifyNoLeaks(t, nil)\n\tdefer testutils.SetTimeout(t, 2*time.Second)()\n\n\tct := &closeSemanticsTest{t, true /* isolated */}\n\tct.runTest()\n}\n\nfunc TestCloseSingleChannel(t *testing.T) {\n\tch := testutils.NewServer(t, nil)\n\n\tvar connected sync.WaitGroup\n\tvar completed sync.WaitGroup\n\tblockCall := make(chan struct{})\n\n\ttestutils.RegisterFunc(ch, \"echo\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\tconnected.Done()\n\t\t<-blockCall\n\t\treturn &raw.Res{\n\t\t\tArg2: args.Arg2,\n\t\t\tArg3: args.Arg3,\n\t\t}, nil\n\t})\n\n\tfor i := 0; i < 10; i++ {\n\t\tconnected.Add(1)\n\t\tcompleted.Add(1)\n\t\tgo func() {\n\t\t\tctx, cancel := NewContext(time.Second)\n\t\t\tdefer cancel()\n\n\t\t\tpeerInfo := ch.PeerInfo()\n\t\t\t_, _, _, err := raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, \"echo\", nil, nil)\n\t\t\tassert.NoError(t, err, \"Call failed\")\n\t\t\tcompleted.Done()\n\t\t}()\n\t}\n\n\t// Wait for all calls to connect before triggerring the Close (so they do not fail).\n\tconnected.Wait()\n\tch.Close()\n\n\t// Unblock the calls, and wait for all the calls to complete.\n\tclose(blockCall)\n\tcompleted.Wait()\n\n\t// Once all calls are complete, the channel should be closed.\n\tassertStateChangesTo(t, ch, ChannelClosed)\n\tgoroutines.VerifyNoLeaks(t, nil)\n}\n\nfunc TestCloseOneSide(t *testing.T) {\n\tch1 := testutils.NewServer(t, &testutils.ChannelOpts{ServiceName: \"client\"})\n\tch2 := testutils.NewServer(t, &testutils.ChannelOpts{ServiceName: \"server\"})\n\n\tconnected := make(chan struct{})\n\tcompleted := make(chan struct{})\n\tblockCall := make(chan struct{})\n\ttestutils.RegisterFunc(ch2, \"echo\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\tconnected <- struct{}{}\n\t\t<-blockCall\n\t\treturn &raw.Res{\n\t\t\tArg2: args.Arg2,\n\t\t\tArg3: args.Arg3,\n\t\t}, nil\n\t})\n\n\tgo func() {\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\t\tch2Peer := ch2.PeerInfo()\n\t\t_, _, _, err := raw.Call(ctx, ch1, ch2Peer.HostPort, ch2Peer.ServiceName, \"echo\", nil, nil)\n\t\tassert.NoError(t, err, \"Call failed\")\n\t\tcompleted <- struct{}{}\n\t}()\n\n\t// Wait for connected before calling Close.\n\t<-connected\n\tch1.Close()\n\n\t// Now unblock the call and wait for the call to complete.\n\tclose(blockCall)\n\t<-completed\n\n\t// Once the call completes, the channel should be closed.\n\tassertStateChangesTo(t, ch1, ChannelClosed)\n\n\t// We need to close all open TChannels before verifying blocked goroutines.\n\tch2.Close()\n\tgoroutines.VerifyNoLeaks(t, nil)\n}\n\n// TestCloseSendError tests that system errors are not attempted to be sent when\n// a connection is closed, and ensures there's no race conditions such as the error\n// frame being added to the channel just as it is closed.\nfunc TestCloseSendError(t *testing.T) {\n\tvar (\n\t\tclosed  atomic.Uint32\n\t\tcounter atomic.Uint32\n\t)\n\n\topts := testutils.NewOpts().DisableLogVerification()\n\tserverCh := testutils.NewServer(t, opts)\n\ttestutils.RegisterEcho(serverCh, func() {\n\t\tif counter.Inc() > 10 {\n\t\t\t// Close the server in a goroutine to possibly trigger more race conditions.\n\t\t\tgo func() {\n\t\t\t\tclosed.Inc()\n\t\t\t\tserverCh.Close()\n\t\t\t}()\n\t\t}\n\t})\n\n\tclientCh := testutils.NewClient(t, opts)\n\n\t// Create a connection that will be shared.\n\trequire.NoError(t, testutils.Ping(clientCh, serverCh), \"Ping from client to server failed\")\n\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < 100; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\ttime.Sleep(time.Duration(rand.Intn(1000)) * time.Microsecond)\n\t\t\terr := testutils.CallEcho(clientCh, serverCh.PeerInfo().HostPort, serverCh.ServiceName(), nil)\n\t\t\tif err != nil && closed.Load() == 0 {\n\t\t\t\tt.Errorf(\"Call failed: %v\", err)\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t}\n\n\t// Wait for all the goroutines to end\n\twg.Wait()\n\n\tclientCh.Close()\n\tgoroutines.VerifyNoLeaks(t, nil)\n}\n"
  },
  {
    "path": "codecov.yml",
    "content": "coverage:\n  range: 75..100\n  round: down\n  precision: 2\n\n  status:\n    project:\n      default:\n        enabled: yes\n        target: 85%\n        if_not_found: success\n        if_ci_failed: error\nignore:\n- \"*_string.go\"\n\n"
  },
  {
    "path": "conn_leak_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\npackage tchannel_test\n\nimport (\n\t\"io/ioutil\"\n\t\"runtime\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\n// This is a regression test for https://github.com/uber/tchannel-go/issues/643\n// We want to ensure that once a connection is closed, there are no references\n// to the closed connection, and the GC frees the connection.\n// We use `runtime.SetFinalizer` to detect whether the GC has freed the object.\n// However, finalizers cannot be set on objects with circular references,\n// so we cannot set a finalizer on the connection, but instead set a finalizer\n// on a field of the connection which has the same lifetime. The connection\n// logger is unique per connection and does not have circular references\n// so we can use the logger, but need a pointer for `runtime.SetFinalizer`.\n// loggerPtr is a Logger implementation that uses a pointer unlike other\n// TChannel loggers.\ntype loggerPtr struct {\n\tLogger\n}\n\nfunc (l *loggerPtr) WithFields(fields ...LogField) Logger {\n\treturn &loggerPtr{l.Logger.WithFields(fields...)}\n}\n\nfunc TestPeerConnectionLeaks(t *testing.T) {\n\t// Disable log verification since we want to set our own logger.\n\topts := testutils.NewOpts().NoRelay().DisableLogVerification()\n\topts.Logger = &loggerPtr{NullLogger}\n\n\tconnFinalized := make(chan struct{})\n\tsetFinalizer := func(p *Peer) {\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\tconn, err := p.GetConnection(ctx)\n\t\trequire.NoError(t, err, \"Failed to get connection\")\n\n\t\truntime.SetFinalizer(conn.Logger(), func(interface{}) {\n\t\t\tclose(connFinalized)\n\t\t})\n\t}\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ts2Opts := testutils.NewOpts().SetServiceName(\"s2\")\n\t\ts2Opts.Logger = NewLogger(ioutil.Discard)\n\t\ts2 := ts.NewServer(s2Opts)\n\n\t\t// Set a finalizer to detect when the connection from s1 -> s2 is freed.\n\t\tpeer := ts.Server().Peers().GetOrAdd(s2.PeerInfo().HostPort)\n\t\tsetFinalizer(peer)\n\n\t\t// Close s2, so that the connection in s1 to s2 is released.\n\t\ts2.Close()\n\t\tclosed := testutils.WaitFor(3*time.Second, s2.Closed)\n\t\trequire.True(t, closed, \"s2 didn't close\")\n\n\t\t// Trigger the GC which will call the finalizer, and ensure\n\t\t// that the connection logger was finalized.\n\t\tfinalized := testutils.WaitFor(3*time.Second, func() bool {\n\t\t\truntime.GC()\n\t\t\tselect {\n\t\t\tcase <-connFinalized:\n\t\t\t\treturn true\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\t\t})\n\t\trequire.True(t, finalized, \"Connection was not freed\")\n\t})\n}\n"
  },
  {
    "path": "connection.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/tos\"\n\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n\t\"golang.org/x/net/ipv4\"\n\t\"golang.org/x/net/ipv6\"\n)\n\nconst (\n\t// CurrentProtocolVersion is the current version of the TChannel protocol\n\t// supported by this stack\n\tCurrentProtocolVersion = 0x02\n\n\t// DefaultConnectTimeout is the default timeout used by net.Dial, if no timeout\n\t// is specified in the context.\n\tDefaultConnectTimeout = 5 * time.Second\n\n\t// DefaultConnectionBufferSize is the default size for the connection's read\n\t//and write channels.\n\tDefaultConnectionBufferSize = 512\n)\n\n// PeerVersion contains version related information for a specific peer.\n// These values are extracted from the init headers.\ntype PeerVersion struct {\n\tLanguage        string `json:\"language\"`\n\tLanguageVersion string `json:\"languageVersion\"`\n\tTChannelVersion string `json:\"tchannelVersion\"`\n}\n\n// PeerInfo contains information about a TChannel peer\ntype PeerInfo struct {\n\t// The host and port that can be used to contact the peer, as encoded by net.JoinHostPort\n\tHostPort string `json:\"hostPort\"`\n\n\t// The logical process name for the peer, used for only for logging / debugging\n\tProcessName string `json:\"processName\"`\n\n\t// IsEphemeral returns whether the remote host:port is ephemeral (e.g. not listening).\n\tIsEphemeral bool `json:\"isEphemeral\"`\n\n\t// Version returns the version information for the remote peer.\n\tVersion PeerVersion `json:\"version\"`\n}\n\nfunc (p PeerInfo) String() string {\n\treturn fmt.Sprintf(\"%s(%s)\", p.HostPort, p.ProcessName)\n}\n\n// IsEphemeralHostPort returns whether the connection is from an ephemeral host:port.\nfunc (p PeerInfo) IsEphemeralHostPort() bool {\n\treturn p.IsEphemeral\n}\n\n// LocalPeerInfo adds service name to the peer info, only required for the local peer.\ntype LocalPeerInfo struct {\n\tPeerInfo\n\n\t// ServiceName is the service name for the local peer.\n\tServiceName string `json:\"serviceName\"`\n}\n\nfunc (p LocalPeerInfo) String() string {\n\treturn fmt.Sprintf(\"%v: %v\", p.ServiceName, p.PeerInfo)\n}\n\nvar (\n\t// ErrConnectionClosed is returned when a caller performs an method\n\t// on a closed connection\n\tErrConnectionClosed = errors.New(\"connection is closed\")\n\n\t// ErrSendBufferFull is returned when a message cannot be sent to the\n\t// peer because the frame sending buffer has become full.  Typically\n\t// this indicates that the connection is stuck and writes have become\n\t// backed up\n\tErrSendBufferFull = errors.New(\"connection send buffer is full, cannot send frame\")\n\n\t// ErrConnectionNotReady is no longer used.\n\tErrConnectionNotReady = errors.New(\"connection is not yet ready\")\n\n\terrNoSyscallConn = errors.New(\"no syscall.RawConn available\")\n)\n\n// errConnectionInvalidState is returned when the connection is in an unknown state.\ntype errConnectionUnknownState struct {\n\tsite  string\n\tstate connectionState\n}\n\nfunc (e errConnectionUnknownState) Error() string {\n\treturn fmt.Sprintf(\"connection is in unknown state: %v at %v\", e.state, e.site)\n}\n\n// SendBufferSizeOverride is used for overriding per-process send buffer channel size for a\n// connection, using process name prefix matching.\ntype SendBufferSizeOverride struct {\n\tProcessNamePrefix string\n\tSendBufferSize    int\n}\n\n// ConnectionOptions are options that control the behavior of a Connection\ntype ConnectionOptions struct {\n\t// The frame pool, allowing better management of frame buffers. Defaults to using raw heap.\n\tFramePool FramePool\n\n\t// NOTE: This is deprecated and not used for anything.\n\tRecvBufferSize int\n\n\t// The size of send channel buffers. Defaults to 512.\n\tSendBufferSize int\n\n\t// Per-process name prefix override for SendBufferSize\n\t// Note that order matters, if there are multiple matches, the first one is used.\n\tSendBufferSizeOverrides []SendBufferSizeOverride\n\n\t// The type of checksum to use when sending messages.\n\tChecksumType ChecksumType\n\n\t// ToS class name marked on outbound packets.\n\tTosPriority tos.ToS\n\n\t// HealthChecks configures active connection health checking for this channel.\n\t// By default, health checks are not enabled.\n\tHealthChecks HealthCheckOptions\n\n\t// MaxCloseTime controls how long we allow a connection to complete pending\n\t// calls before shutting down. Only used if it is non-zero.\n\tMaxCloseTime time.Duration\n\n\t// PropagateCancel enables cancel messages to cancel contexts.\n\t// By default, cancel messages are ignored.\n\t// This only affects inbounds (servers handling calls).\n\tPropagateCancel bool\n\n\t// SendCancelOnContextCanceled enables sending cancel messages\n\t// when a request context is canceled before receiving a response.\n\t// This only affects outbounds (clients making calls).\n\tSendCancelOnContextCanceled bool\n}\n\n// connectionEvents are the events that can be triggered by a connection.\ntype connectionEvents struct {\n\t// OnActive is called when a connection becomes active.\n\tOnActive func(c *Connection)\n\n\t// OnCloseStateChange is called when a connection that is closing changes state.\n\tOnCloseStateChange func(c *Connection)\n\n\t// OnExchangeUpdated is called when a message exchange added or removed.\n\tOnExchangeUpdated func(c *Connection)\n}\n\n// Connection represents a connection to a remote peer.\ntype Connection struct {\n\tchannelConnectionCommon\n\n\tconnID           uint32\n\tconnDirection    connectionDirection\n\topts             ConnectionOptions\n\tconn             net.Conn\n\tsysConn          syscall.RawConn // may be nil if conn cannot be converted\n\tlocalPeerInfo    LocalPeerInfo\n\tremotePeerInfo   PeerInfo\n\tsendCh           chan *Frame\n\tstopCh           chan struct{}\n\tstate            connectionState\n\tstateMut         sync.RWMutex\n\tinbound          *messageExchangeSet\n\toutbound         *messageExchangeSet\n\tinternalHandlers *handlerMap\n\thandler          Handler\n\tnextMessageID    atomic.Uint32\n\tevents           connectionEvents\n\tcommonStatsTags  map[string]string\n\trelay            *Relayer\n\tbaseContext      context.Context\n\n\t// outboundHP is the host:port we used to create this outbound connection.\n\t// It may not match remotePeerInfo.HostPort, in which case the connection is\n\t// added to peers for both host:ports. For inbound connections, this is empty.\n\toutboundHP string\n\n\t// closeNetworkCalled is used to avoid errors from being logged\n\t// when this side closes a connection.\n\tcloseNetworkCalled atomic.Bool\n\t// stoppedExchanges is atomically set when exchanges are stopped due to error.\n\tstoppedExchanges atomic.Bool\n\t// remotePeerAddress is used as a cache for remote peer address parsed into individual\n\t// components that can be used to set peer tags on OpenTracing Span.\n\tremotePeerAddress peerAddressComponents\n\n\t// healthCheckCtx/Quit are used to stop health checks.\n\thealthCheckCtx     context.Context\n\thealthCheckQuit    context.CancelFunc\n\thealthCheckDone    chan struct{}\n\thealthCheckHistory *healthHistory\n\n\t// lastActivity{Read,Write} is used to track how long the connection has been\n\t// idle for the recieve and send connections respectively. (unix time, nano)\n\tlastActivityRead  atomic.Int64\n\tlastActivityWrite atomic.Int64\n}\n\ntype peerAddressComponents struct {\n\tport     uint16\n\tipv4     uint32\n\tipv6     string\n\thostname string\n}\n\n// _nextConnID is used to allocate unique IDs to every connection for debugging purposes.\nvar _nextConnID atomic.Uint32\n\ntype connectionState int\n\nconst (\n\t// Connection is fully active\n\tconnectionActive connectionState = iota + 1\n\n\t// Connection is starting to close; new incoming requests are rejected, outbound\n\t// requests are allowed to proceed\n\tconnectionStartClose\n\n\t// Connection has finished processing all active inbound, and is\n\t// waiting for outbound requests to complete or timeout\n\tconnectionInboundClosed\n\n\t// Connection is fully closed\n\tconnectionClosed\n)\n\n//go:generate stringer -type=connectionState\n\nfunc getTimeout(ctx context.Context) time.Duration {\n\tdeadline, ok := ctx.Deadline()\n\tif !ok {\n\t\treturn DefaultConnectTimeout\n\t}\n\n\treturn deadline.Sub(time.Now())\n}\n\nfunc (co ConnectionOptions) withDefaults() ConnectionOptions {\n\tif co.ChecksumType == ChecksumTypeNone {\n\t\tco.ChecksumType = ChecksumTypeCrc32\n\t}\n\tif co.FramePool == nil {\n\t\tco.FramePool = DefaultFramePool\n\t}\n\tif co.SendBufferSize <= 0 {\n\t\tco.SendBufferSize = DefaultConnectionBufferSize\n\t}\n\tco.HealthChecks = co.HealthChecks.withDefaults()\n\treturn co\n}\n\nfunc (co ConnectionOptions) getSendBufferSize(processName string) int {\n\tfor _, override := range co.SendBufferSizeOverrides {\n\t\tif strings.HasPrefix(processName, override.ProcessNamePrefix) {\n\t\t\treturn override.SendBufferSize\n\t\t}\n\t}\n\treturn co.SendBufferSize\n}\n\nfunc (ch *Channel) setConnectionTosPriority(tosPriority tos.ToS, c net.Conn) error {\n\ttcpAddr, isTCP := c.RemoteAddr().(*net.TCPAddr)\n\tif !isTCP {\n\t\treturn nil\n\t}\n\n\t// Handle dual stack listeners and set Traffic Class.\n\tvar err error\n\tswitch ip := tcpAddr.IP; {\n\tcase ip.To16() != nil && ip.To4() == nil:\n\t\terr = ipv6.NewConn(c).SetTrafficClass(int(tosPriority))\n\tcase ip.To4() != nil:\n\t\terr = ipv4.NewConn(c).SetTOS(int(tosPriority))\n\t}\n\treturn err\n}\n\nfunc (ch *Channel) newConnection(baseCtx context.Context, conn net.Conn, initialID uint32, outboundHP string, remotePeer PeerInfo, remotePeerAddress peerAddressComponents, events connectionEvents) *Connection {\n\topts := ch.connectionOptions.withDefaults()\n\n\tconnID := _nextConnID.Inc()\n\tconnDirection := inbound\n\tlog := ch.log.WithFields(LogFields{\n\t\t{\"connID\", connID},\n\t\t{\"localAddr\", conn.LocalAddr().String()},\n\t\t{\"remoteAddr\", conn.RemoteAddr().String()},\n\t\t{\"remoteHostPort\", remotePeer.HostPort},\n\t\t{\"remoteIsEphemeral\", remotePeer.IsEphemeral},\n\t\t{\"remoteProcess\", remotePeer.ProcessName},\n\t}...)\n\tif outboundHP != \"\" {\n\t\tconnDirection = outbound\n\t\tlog = log.WithFields(LogField{\"outboundHP\", outboundHP})\n\t}\n\n\tlog = log.WithFields(LogField{\"connectionDirection\", connDirection})\n\tpeerInfo := ch.PeerInfo()\n\ttimeNow := ch.timeNow().UnixNano()\n\n\tc := &Connection{\n\t\tchannelConnectionCommon: ch.channelConnectionCommon,\n\n\t\tconnID:             connID,\n\t\tconn:               conn,\n\t\tsysConn:            getSysConn(conn, log),\n\t\tconnDirection:      connDirection,\n\t\topts:               opts,\n\t\tstate:              connectionActive,\n\t\tsendCh:             make(chan *Frame, opts.getSendBufferSize(remotePeer.ProcessName)),\n\t\tstopCh:             make(chan struct{}),\n\t\tlocalPeerInfo:      peerInfo,\n\t\tremotePeerInfo:     remotePeer,\n\t\tremotePeerAddress:  remotePeerAddress,\n\t\toutboundHP:         outboundHP,\n\t\tinbound:            newMessageExchangeSet(log, messageExchangeSetInbound),\n\t\toutbound:           newMessageExchangeSet(log, messageExchangeSetOutbound),\n\t\tinternalHandlers:   ch.internalHandlers,\n\t\thandler:            ch.handler,\n\t\tevents:             events,\n\t\tcommonStatsTags:    ch.commonStatsTags,\n\t\thealthCheckHistory: newHealthHistory(),\n\t\tlastActivityRead:   *atomic.NewInt64(timeNow),\n\t\tlastActivityWrite:  *atomic.NewInt64(timeNow),\n\t\tbaseContext:        ch.connContext(baseCtx, conn),\n\t}\n\n\tif tosPriority := opts.TosPriority; tosPriority > 0 {\n\t\tif err := ch.setConnectionTosPriority(tosPriority, conn); err != nil {\n\t\t\tlog.WithFields(ErrField(err)).Error(\"Failed to set ToS priority.\")\n\t\t}\n\t}\n\n\tc.nextMessageID.Store(initialID)\n\tc.log = log\n\tc.outbound.onCancel = c.onCancel\n\tc.inbound.onRemoved = c.checkExchanges\n\tc.outbound.onRemoved = c.checkExchanges\n\tc.inbound.onAdded = c.onExchangeAdded\n\tc.outbound.onAdded = c.onExchangeAdded\n\n\tif ch.RelayHost() != nil {\n\t\tc.relay = NewRelayer(ch, c)\n\t}\n\n\t// Connections are activated as soon as they are created.\n\tc.callOnActive()\n\n\tgo c.readFrames(connID)\n\tgo c.writeFrames(connID)\n\treturn c\n}\n\nfunc (c *Connection) onCancel(msgID uint32) {\n\tif !c.opts.SendCancelOnContextCanceled {\n\t\treturn\n\t}\n\n\tcancelMsg := &cancelMessage{\n\t\tid:      msgID,\n\t\tmessage: ErrRequestCancelled.Error(),\n\t}\n\tif err := c.sendMessage(cancelMsg); err != nil {\n\t\tc.connectionError(\"send cancel\", err)\n\t}\n}\n\nfunc (c *Connection) onExchangeAdded() {\n\tc.callOnExchangeChange()\n}\n\n// IsActive returns whether this connection is in an active state.\nfunc (c *Connection) IsActive() bool {\n\treturn c.readState() == connectionActive\n}\n\nfunc (c *Connection) callOnActive() {\n\tlog := c.log\n\tif remoteVersion := c.remotePeerInfo.Version; remoteVersion != (PeerVersion{}) {\n\t\tlog = log.WithFields(LogFields{\n\t\t\t{\"remotePeerLanguage\", remoteVersion.Language},\n\t\t\t{\"remotePeerLanguageVersion\", remoteVersion.LanguageVersion},\n\t\t\t{\"remotePeerTChannelVersion\", remoteVersion.TChannelVersion},\n\t\t}...)\n\t}\n\tlog.Debug(\"Created new active connection.\")\n\n\tif f := c.events.OnActive; f != nil {\n\t\tf(c)\n\t}\n\n\tif c.opts.HealthChecks.enabled() {\n\t\tc.healthCheckCtx, c.healthCheckQuit = context.WithCancel(context.Background())\n\t\tc.healthCheckDone = make(chan struct{})\n\t\tgo c.healthCheck(c.connID)\n\t}\n}\n\nfunc (c *Connection) callOnCloseStateChange() {\n\tif f := c.events.OnCloseStateChange; f != nil {\n\t\tf(c)\n\t}\n}\n\nfunc (c *Connection) callOnExchangeChange() {\n\tif f := c.events.OnExchangeUpdated; f != nil {\n\t\tf(c)\n\t}\n}\n\n// ping sends a ping message and waits for a ping response.\nfunc (c *Connection) ping(ctx context.Context) error {\n\treq := &pingReq{id: c.NextMessageID()}\n\tmex, err := c.outbound.newExchange(ctx, c.outboundCtxCancel, c.opts.FramePool, req.messageType(), req.ID(), 1)\n\tif err != nil {\n\t\treturn c.connectionError(\"create ping exchange\", err)\n\t}\n\tdefer c.outbound.removeExchange(req.ID())\n\n\tif err := c.sendMessage(req); err != nil {\n\t\treturn c.connectionError(\"send ping\", err)\n\t}\n\n\treturn c.recvMessage(ctx, &pingRes{}, mex)\n}\n\n// handlePingRes calls registered ping handlers.\nfunc (c *Connection) handlePingRes(frame *Frame) bool {\n\tif err := c.outbound.forwardPeerFrame(frame); err != nil {\n\t\tc.log.WithFields(LogField{\"response\", frame.Header}).Warn(\"Unexpected ping response.\")\n\t\treturn true\n\t}\n\t// ping req is waiting for this frame, and will release it.\n\treturn false\n}\n\n// handlePingReq responds to the pingReq message with a pingRes.\nfunc (c *Connection) handlePingReq(frame *Frame) {\n\tif state := c.readState(); state != connectionActive {\n\t\tc.protocolError(frame.Header.ID, errConnNotActive{\"ping on incoming\", state})\n\t\treturn\n\t}\n\n\tpingRes := &pingRes{id: frame.Header.ID}\n\tif err := c.sendMessage(pingRes); err != nil {\n\t\tc.connectionError(\"send pong\", err)\n\t}\n}\n\n// sendMessage sends a standalone message (typically a control message)\nfunc (c *Connection) sendMessage(msg message) error {\n\tframe := c.opts.FramePool.Get()\n\tif err := frame.write(msg); err != nil {\n\t\tc.opts.FramePool.Release(frame)\n\t\treturn err\n\t}\n\n\tselect {\n\tcase c.sendCh <- frame:\n\t\treturn nil\n\tdefault:\n\t\treturn ErrSendBufferFull\n\t}\n}\n\n// recvMessage blocks waiting for a standalone response message (typically a\n// control message)\nfunc (c *Connection) recvMessage(ctx context.Context, msg message, mex *messageExchange) error {\n\tframe, err := mex.recvPeerFrameOfType(msg.messageType())\n\tif err != nil {\n\t\tif err, ok := err.(errorMessage); ok {\n\t\t\treturn err.AsSystemError()\n\t\t}\n\t\treturn err\n\t}\n\n\terr = frame.read(msg)\n\tc.opts.FramePool.Release(frame)\n\treturn err\n}\n\n// RemotePeerInfo returns the peer info for the remote peer.\nfunc (c *Connection) RemotePeerInfo() PeerInfo {\n\treturn c.remotePeerInfo\n}\n\n// NextMessageID reserves the next available message id for this connection\nfunc (c *Connection) NextMessageID() uint32 {\n\treturn c.nextMessageID.Inc()\n}\n\n// SendSystemError sends an error frame for the given system error.\nfunc (c *Connection) SendSystemError(id uint32, span Span, err error) (sendErr error) {\n\t// Allocate an error frame to be sent over the connection. A nil is\n\t// returned if the frame was successfully sent, otherwise an error is\n\t// returned, and we must release the error frame back to the pool.\n\tframe := c.opts.FramePool.Get()\n\tdefer func() {\n\t\tif sendErr != nil {\n\t\t\tc.opts.FramePool.Release(frame)\n\t\t}\n\t}()\n\n\tif err := frame.write(&errorMessage{\n\t\tid:      id,\n\t\terrCode: GetSystemErrorCode(err),\n\t\ttracing: span,\n\t\tmessage: GetSystemErrorMessage(err),\n\t}); err != nil {\n\t\t// This shouldn't happen - it means writing the errorMessage is broken.\n\t\tc.log.WithFields(\n\t\t\tLogField{\"remotePeer\", c.remotePeerInfo},\n\t\t\tLogField{\"id\", id},\n\t\t\tErrField(err),\n\t\t).Warn(\"Couldn't create outbound frame.\")\n\t\treturn fmt.Errorf(\"failed to create outbound error frame: %v\", err)\n\t}\n\n\t// When sending errors, we hold the state rlock to ensure that sendCh is not closed\n\t// as we are sending the frame.\n\treturn c.withStateRLock(func() error {\n\t\t// Errors cannot be sent if the connection has been closed.\n\t\tif c.state == connectionClosed {\n\t\t\tc.log.WithFields(\n\t\t\t\tLogField{\"remotePeer\", c.remotePeerInfo},\n\t\t\t\tLogField{\"id\", id},\n\t\t\t).Info(\"Could not send error frame on closed connection.\")\n\t\t\treturn fmt.Errorf(\"failed to send error frame, connection state %v\", c.state)\n\t\t}\n\n\t\tselect {\n\t\tcase c.sendCh <- frame: // Good to go\n\t\t\treturn nil\n\t\tdefault: // If the send buffer is full, log and return an error.\n\t\t}\n\t\tc.log.WithFields(\n\t\t\tLogField{\"remotePeer\", c.remotePeerInfo},\n\t\t\tLogField{\"id\", id},\n\t\t\tErrField(err),\n\t\t).Warn(\"Couldn't send outbound frame.\")\n\t\treturn fmt.Errorf(\"failed to send error frame, buffer full\")\n\t})\n}\n\nfunc (c *Connection) logConnectionError(site string, err error) error {\n\terrCode := ErrCodeNetwork\n\tif err == io.EOF {\n\t\tc.log.Debugf(\"Connection got EOF\")\n\t} else {\n\t\tlogger := c.log.WithFields(\n\t\t\tLogField{\"site\", site},\n\t\t\tErrField(err),\n\t\t)\n\n\t\tif se, ok := err.(SystemError); ok && se.Code() != ErrCodeNetwork {\n\t\t\terrCode = se.Code()\n\t\t\tlogger.Error(\"Connection error.\")\n\t\t} else if ne, ok := err.(net.Error); ok && ne.Timeout() {\n\t\t\tlogger.Warn(\"Connection error due to timeout.\")\n\t\t} else {\n\t\t\tlogger.Info(\"Connection error.\")\n\t\t}\n\t}\n\treturn NewWrappedSystemError(errCode, err)\n}\n\n// connectionError handles a connection level error\nfunc (c *Connection) connectionError(site string, err error) error {\n\tvar closeLogFields LogFields\n\tif err == io.EOF {\n\t\tcloseLogFields = LogFields{{\"reason\", \"network connection EOF\"}}\n\t} else {\n\t\tcloseLogFields = LogFields{\n\t\t\t{\"reason\", \"connection error\"},\n\t\t\tErrField(err),\n\t\t}\n\t}\n\n\tc.stopHealthCheck()\n\terr = c.logConnectionError(site, err)\n\tc.close(closeLogFields...)\n\n\t// On any connection error, notify the exchanges of this error.\n\tif c.stoppedExchanges.CAS(false, true) {\n\t\tc.outbound.stopExchanges(err)\n\t\tc.inbound.stopExchanges(err)\n\t}\n\n\t// checkExchanges will close the connection due to stoppedExchanges.\n\tc.checkExchanges()\n\treturn err\n}\n\nfunc (c *Connection) protocolError(id uint32, err error) error {\n\tc.log.WithFields(ErrField(err)).Warn(\"Protocol error.\")\n\tsysErr := NewWrappedSystemError(ErrCodeProtocol, err)\n\tc.SendSystemError(id, Span{}, sysErr)\n\t// Don't close the connection until the error has been sent.\n\tc.close(\n\t\tLogField{\"reason\", \"protocol error\"},\n\t\tErrField(err),\n\t)\n\n\t// On any connection error, notify the exchanges of this error.\n\tif c.stoppedExchanges.CAS(false, true) {\n\t\tc.outbound.stopExchanges(sysErr)\n\t\tc.inbound.stopExchanges(sysErr)\n\t}\n\treturn sysErr\n}\n\n// withStateLock performs an action with the connection state mutex locked\nfunc (c *Connection) withStateLock(f func() error) error {\n\tc.stateMut.Lock()\n\terr := f()\n\tc.stateMut.Unlock()\n\n\treturn err\n}\n\n// withStateRLock performs an action with the connection state mutex rlocked.\nfunc (c *Connection) withStateRLock(f func() error) error {\n\tc.stateMut.RLock()\n\terr := f()\n\tc.stateMut.RUnlock()\n\n\treturn err\n}\n\nfunc (c *Connection) readState() connectionState {\n\tc.stateMut.RLock()\n\tstate := c.state\n\tc.stateMut.RUnlock()\n\treturn state\n}\n\n// readFrames is the loop that reads frames from the network connection and\n// dispatches to the appropriate handler. Run within its own goroutine to\n// prevent overlapping reads on the socket.  Most handlers simply send the\n// incoming frame to a channel; the init handlers are a notable exception,\n// since we cannot process new frames until the initialization is complete.\nfunc (c *Connection) readFrames(_ uint32) {\n\theaderBuf := make([]byte, FrameHeaderSize)\n\n\thandleErr := func(err error) {\n\t\tif !c.closeNetworkCalled.Load() {\n\t\t\tc.connectionError(\"read frames\", err)\n\t\t} else {\n\t\t\tc.log.Debugf(\"Ignoring error after connection was closed: %v\", err)\n\t\t}\n\t}\n\n\tfor {\n\t\t// Read the header, avoid allocating the frame till we know the size\n\t\t// we need to allocate.\n\t\tif _, err := io.ReadFull(c.conn, headerBuf); err != nil {\n\t\t\thandleErr(err)\n\t\t\treturn\n\t\t}\n\n\t\tframe := c.opts.FramePool.Get()\n\t\tif err := frame.ReadBody(headerBuf, c.conn); err != nil {\n\t\t\thandleErr(err)\n\t\t\tc.opts.FramePool.Release(frame)\n\t\t\treturn\n\t\t}\n\n\t\tc.updateLastActivityRead(frame)\n\n\t\tvar releaseFrame bool\n\t\tif c.relay == nil {\n\t\t\treleaseFrame = c.handleFrameNoRelay(frame)\n\t\t} else {\n\t\t\treleaseFrame = c.handleFrameRelay(frame)\n\t\t}\n\t\tif releaseFrame {\n\t\t\tc.opts.FramePool.Release(frame)\n\t\t}\n\t}\n}\n\nfunc (c *Connection) handleFrameRelay(frame *Frame) bool {\n\tif frame.Header.messageType == messageTypeCancel && !c.opts.PropagateCancel {\n\t\t// If cancel propagation is disabled, don't do anything for this frame.\n\t\tif c.log.Enabled(LogLevelDebug) {\n\t\t\tc.log.Debugf(\"Ignoring cancel in relay for %v\", frame.Header.ID)\n\t\t}\n\t\treturn true\n\t}\n\n\tswitch msgType := frame.Header.messageType; msgType {\n\tcase messageTypeCallReq, messageTypeCallReqContinue, messageTypeCallRes, messageTypeCallResContinue, messageTypeError, messageTypeCancel:\n\t\tshouldRelease, err := c.relay.Relay(frame)\n\t\tif err != nil {\n\t\t\tc.log.WithFields(\n\t\t\t\tErrField(err),\n\t\t\t\tLogField{\"header\", frame.Header},\n\t\t\t\tLogField{\"remotePeer\", c.remotePeerInfo},\n\t\t\t).Error(\"Failed to relay frame.\")\n\t\t}\n\t\treturn shouldRelease\n\tdefault:\n\t\treturn c.handleFrameNoRelay(frame)\n\t}\n}\n\nfunc (c *Connection) handleFrameNoRelay(frame *Frame) bool {\n\treleaseFrame := true\n\n\t// call req and call res messages may not want the frame released immediately.\n\tswitch frame.Header.messageType {\n\tcase messageTypeCallReq:\n\t\treleaseFrame = c.handleCallReq(frame)\n\tcase messageTypeCallReqContinue:\n\t\treleaseFrame = c.handleCallReqContinue(frame)\n\tcase messageTypeCallRes:\n\t\treleaseFrame = c.handleCallRes(frame)\n\tcase messageTypeCallResContinue:\n\t\treleaseFrame = c.handleCallResContinue(frame)\n\tcase messageTypePingReq:\n\t\tc.handlePingReq(frame)\n\tcase messageTypePingRes:\n\t\treleaseFrame = c.handlePingRes(frame)\n\tcase messageTypeError:\n\t\treleaseFrame = c.handleError(frame)\n\tcase messageTypeCancel:\n\t\treleaseFrame = c.handleCancel(frame)\n\tdefault:\n\t\t// TODO(mmihic): Log and close connection with protocol error\n\t\tc.log.WithFields(\n\t\t\tLogField{\"header\", frame.Header},\n\t\t\tLogField{\"remotePeer\", c.remotePeerInfo},\n\t\t).Error(\"Received unexpected frame.\")\n\t}\n\n\treturn releaseFrame\n}\n\n// writeFrames is the main loop that pulls frames from the send channel and\n// writes them to the connection.\nfunc (c *Connection) writeFrames(_ uint32) {\n\tdefer func() {\n\t\t<-c.stopCh\n\t\t// Drain and release any remaining frames in sendCh for best-effort\n\t\t// reduction in leaked frames\n\t\tfor len(c.sendCh) > 0 {\n\t\t\tc.opts.FramePool.Release(<-c.sendCh)\n\t\t}\n\t}()\n\n\tfor {\n\t\tselect {\n\t\tcase f := <-c.sendCh:\n\t\t\tif c.log.Enabled(LogLevelDebug) {\n\t\t\t\tc.log.Debugf(\"Writing frame %s\", f.Header)\n\t\t\t}\n\n\t\t\tc.updateLastActivityWrite(f)\n\t\t\terr := f.WriteOut(c.conn)\n\t\t\tc.opts.FramePool.Release(f)\n\t\t\tif err != nil {\n\t\t\t\tc.connectionError(\"write frames\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\tcase <-c.stopCh:\n\t\t\t// If there are frames in sendCh, we want to drain them.\n\t\t\tif len(c.sendCh) > 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// Close the network once we're no longer writing frames.\n\t\t\tc.closeNetwork()\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// updateLastActivityRead marks when the last message was received on the channel.\n// This is used for monitoring idle connections and timing them out.\nfunc (c *Connection) updateLastActivityRead(frame *Frame) {\n\tif isMessageTypeCall(frame) {\n\t\tc.lastActivityRead.Store(c.timeNow().UnixNano())\n\t}\n}\n\n// updateLastActivityWrite marks when the last message was sent on the channel.\n// This is used for monitoring idle connections and timing them out.\nfunc (c *Connection) updateLastActivityWrite(frame *Frame) {\n\tif isMessageTypeCall(frame) {\n\t\tc.lastActivityWrite.Store(c.timeNow().UnixNano())\n\t}\n}\n\n// hasPendingCalls returns whether there's any pending inbound or outbound calls on this connection.\nfunc (c *Connection) hasPendingCalls() bool {\n\tif c.inbound.count() > 0 || c.outbound.count() > 0 {\n\t\treturn true\n\t}\n\tif !c.relay.canClose() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// checkExchanges is called whenever an exchange is removed, and when Close is called.\nfunc (c *Connection) checkExchanges() {\n\tc.callOnExchangeChange()\n\n\tmoveState := func(fromState, toState connectionState) bool {\n\t\terr := c.withStateLock(func() error {\n\t\t\tif c.state != fromState {\n\t\t\t\treturn errors.New(\"\")\n\t\t\t}\n\t\t\tc.state = toState\n\t\t\treturn nil\n\t\t})\n\t\treturn err == nil\n\t}\n\n\tcurState := c.readState()\n\torigState := curState\n\n\tif curState != connectionClosed && c.stoppedExchanges.Load() {\n\t\tif moveState(curState, connectionClosed) {\n\t\t\tcurState = connectionClosed\n\t\t}\n\t}\n\n\tif curState == connectionStartClose {\n\t\tif !c.relay.canClose() {\n\t\t\treturn\n\t\t}\n\t\tif c.inbound.count() == 0 && moveState(connectionStartClose, connectionInboundClosed) {\n\t\t\tcurState = connectionInboundClosed\n\t\t}\n\t}\n\n\tif curState == connectionInboundClosed {\n\t\t// Safety check -- this should never happen since we already did the check\n\t\t// when transitioning to connectionInboundClosed.\n\t\tif !c.relay.canClose() {\n\t\t\tc.relay.logger.Error(\"Relay can't close even though state is InboundClosed.\")\n\t\t\treturn\n\t\t}\n\n\t\tif c.outbound.count() == 0 && moveState(connectionInboundClosed, connectionClosed) {\n\t\t\tcurState = connectionClosed\n\t\t}\n\t}\n\n\tif curState != origState {\n\t\t// If the connection is closed, we can notify writeFrames to stop which\n\t\t// closes the underlying network connection. We never close sendCh to avoid\n\t\t// races causing panics, see 93ef5c112c8b321367ae52d2bd79396e2e874f31\n\t\tif curState == connectionClosed {\n\t\t\tclose(c.stopCh)\n\t\t}\n\n\t\tc.log.WithFields(\n\t\t\tLogField{\"newState\", curState},\n\t\t).Debug(\"Connection state updated during shutdown.\")\n\t\tc.callOnCloseStateChange()\n\t}\n}\n\nfunc (c *Connection) close(fields ...LogField) error {\n\tc.log.WithFields(fields...).Debug(\"Connection closing.\")\n\n\t// Update the state which will start blocking incoming calls.\n\tif err := c.withStateLock(func() error {\n\t\tswitch s := c.state; s {\n\t\tcase connectionActive:\n\t\t\tc.state = connectionStartClose\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"connection must be Active to Close, but it is %v\", s)\n\t\t}\n\t\treturn nil\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\t// Set a read deadline with any close timeout. This will cause a i/o timeout\n\t// if the connection isn't closed by then.\n\tif c.opts.MaxCloseTime > 0 {\n\t\tc.conn.SetReadDeadline(c.timeNow().Add(c.opts.MaxCloseTime))\n\t}\n\n\tc.log.WithFields(\n\t\tLogField{\"newState\", c.readState()},\n\t).Debug(\"Connection state updated in Close.\")\n\tc.callOnCloseStateChange()\n\n\t// Check all in-flight requests to see whether we can transition the Close state.\n\tc.checkExchanges()\n\n\treturn nil\n}\n\n// Close starts a graceful Close which will first reject incoming calls, reject outgoing calls\n// before finally marking the connection state as closed.\nfunc (c *Connection) Close() error {\n\treturn c.close(LogField{\"reason\", \"user initiated\"})\n}\n\n// closeNetwork closes the network connection and all network-related channels.\n// This should only be done in response to a fatal connection or protocol\n// error, or after all pending frames have been sent.\nfunc (c *Connection) closeNetwork() {\n\t// NB(mmihic): The sender goroutine will exit once the connection is\n\t// closed; no need to close the send channel (and closing the send\n\t// channel would be dangerous since other goroutine might be sending)\n\tc.log.Debugf(\"Closing underlying network connection\")\n\tc.stopHealthCheck()\n\tc.closeNetworkCalled.Store(true)\n\tif err := c.conn.Close(); err != nil {\n\t\tc.log.WithFields(\n\t\t\tLogField{\"remotePeer\", c.remotePeerInfo},\n\t\t\tErrField(err),\n\t\t).Warn(\"Couldn't close connection to peer.\")\n\t}\n}\n\n// getLastActivityReadTime returns the timestamp of the last frame read,\n// excluding pings. If no frames were transmitted yet, it will return the time\n// this connection was created.\nfunc (c *Connection) getLastActivityReadTime() time.Time {\n\treturn time.Unix(0, c.lastActivityRead.Load())\n}\n\n// getLastActivityWriteTime returns the timestamp of the last frame written,\n// excluding pings. If no frames were transmitted yet, it will return the time\n// this connection was created.\nfunc (c *Connection) getLastActivityWriteTime() time.Time {\n\treturn time.Unix(0, c.lastActivityWrite.Load())\n}\n\nfunc getSysConn(conn net.Conn, log Logger) syscall.RawConn {\n\tvar (\n\t\tconnSyscall syscall.Conn\n\t\tok          bool\n\t)\n\tswitch v := conn.(type) {\n\tcase syscall.Conn:\n\t\tconnSyscall = v\n\t\tok = true\n\tcase *tls.Conn:\n\t\tconnSyscall, ok = v.NetConn().(syscall.Conn)\n\t}\n\n\tif !ok {\n\t\tlog.WithFields(LogField{\"connectionType\", fmt.Sprintf(\"%T\", conn)}).\n\t\t\tError(\"Connection does not implement SyscallConn.\")\n\t\treturn nil\n\t}\n\n\tsysConn, err := connSyscall.SyscallConn()\n\tif err != nil {\n\t\tlog.WithFields(ErrField(err)).Error(\"Could not get SyscallConn.\")\n\t\treturn nil\n\t}\n\n\treturn sysConn\n}\n\nfunc isMessageTypeCall(frame *Frame) bool {\n\t// Pings are ignored for last activity.\n\tswitch frame.Header.messageType {\n\tcase messageTypeCallReq, messageTypeCallReqContinue, messageTypeCallRes, messageTypeCallResContinue, messageTypeError:\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "connection_bench_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"runtime\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/streadway/quantile\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"golang.org/x/net/context\"\n)\n\nconst benchService = \"bench-server\"\n\ntype benchmarkHandler struct{}\n\nfunc (h *benchmarkHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\treturn &raw.Res{\n\t\tArg2: args.Arg3,\n\t\tArg3: args.Arg2,\n\t}, nil\n}\n\nfunc (h *benchmarkHandler) OnError(ctx context.Context, err error) {\n}\n\ntype latencyTracker struct {\n\tsync.Mutex\n\n\tstarted   time.Time\n\testimator *quantile.Estimator\n}\n\nfunc newLatencyTracker() *latencyTracker {\n\treturn &latencyTracker{\n\t\testimator: quantile.New(\n\t\t\tquantile.Unknown(0.01),\n\t\t\tquantile.Known(0.50, 0.01),\n\t\t\tquantile.Known(0.95, 0.001),\n\t\t\tquantile.Known(0.99, 0.0005),\n\t\t\tquantile.Known(1.0, 0.0005),\n\t\t),\n\t\tstarted: time.Now(),\n\t}\n}\n\nfunc (lt *latencyTracker) addLatency(d time.Duration) {\n\tlt.Lock()\n\tlt.estimator.Add(float64(d))\n\tlt.Unlock()\n}\n\nfunc (lt *latencyTracker) report(t testing.TB) {\n\tduration := time.Since(lt.started)\n\tlt.Lock()\n\tt.Logf(\"%6v calls, %5.0f RPS (%v per call). Latency: Average: %v P95: %v P99: %v P100: %v\",\n\t\tlt.estimator.Samples(),\n\t\tfloat64(lt.estimator.Samples())/float64(duration)*float64(time.Second),\n\t\tduration/time.Duration(lt.estimator.Samples()),\n\t\ttime.Duration(lt.estimator.Get(0.50)),\n\t\ttime.Duration(lt.estimator.Get(0.95)),\n\t\ttime.Duration(lt.estimator.Get(0.99)),\n\t\ttime.Duration(lt.estimator.Get(1.0)),\n\t)\n\tlt.Unlock()\n}\n\nfunc setupServer(t testing.TB) *Channel {\n\tserverCh := testutils.NewServer(t, testutils.NewOpts().SetServiceName(\"bench-server\"))\n\thandler := &benchmarkHandler{}\n\tserverCh.Register(raw.Wrap(handler), \"echo\")\n\treturn serverCh\n}\n\ntype benchmarkConfig struct {\n\tnumCalls         int\n\tnumServers       int\n\tnumClients       int\n\tworkersPerClient int\n\tnumBytes         int\n}\n\nfunc benchmarkCallsN(b *testing.B, c benchmarkConfig) {\n\tvar (\n\t\tclients []*Channel\n\t\tservers []*Channel\n\t)\n\tlt := newLatencyTracker()\n\n\tif c.numBytes == 0 {\n\t\tc.numBytes = 100\n\t}\n\tdata := testutils.RandBytes(c.numBytes)\n\n\t// Set up clients and servers.\n\tfor i := 0; i < c.numServers; i++ {\n\t\tservers = append(servers, setupServer(b))\n\t}\n\tfor i := 0; i < c.numClients; i++ {\n\t\tclients = append(clients, testutils.NewClient(b, nil))\n\t\tfor _, s := range servers {\n\t\t\tclients[i].Peers().Add(s.PeerInfo().HostPort)\n\n\t\t\t// Initialize a connection\n\t\t\tctx, cancel := NewContext(50 * time.Millisecond)\n\t\t\tassert.NoError(b, clients[i].Ping(ctx, s.PeerInfo().HostPort), \"Initial ping failed\")\n\t\t\tcancel()\n\t\t}\n\t}\n\n\t// Make calls from clients to the servers\n\tcall := func(sc *SubChannel) {\n\t\tctx, cancel := NewContext(50 * time.Millisecond)\n\t\tstart := time.Now()\n\t\t_, _, _, err := raw.CallSC(ctx, sc, \"echo\", nil, data)\n\t\tduration := time.Since(start)\n\t\tcancel()\n\t\tif assert.NoError(b, err, \"Call failed\") {\n\t\t\tlt.addLatency(duration)\n\t\t}\n\t}\n\n\treqsLeft := testutils.Decrementor(c.numCalls)\n\tclientWorker := func(client *Channel, clientNum, workerNum int) {\n\t\tsc := client.GetSubChannel(benchService)\n\t\tfor reqsLeft.Single() {\n\t\t\tcall(sc)\n\t\t}\n\t}\n\tclientRunner := func(client *Channel, clientNum int) {\n\t\ttestutils.RunN(c.workersPerClient, func(i int) {\n\t\t\tclientWorker(client, clientNum, i)\n\t\t})\n\t}\n\n\tlt = newLatencyTracker()\n\tdefer lt.report(b)\n\tb.ResetTimer()\n\n\ttestutils.RunN(c.numClients, func(i int) {\n\t\tclientRunner(clients[i], i)\n\t})\n}\n\nfunc BenchmarkCallsSerial(b *testing.B) {\n\tbenchmarkCallsN(b, benchmarkConfig{\n\t\tnumCalls:         b.N,\n\t\tnumServers:       1,\n\t\tnumClients:       1,\n\t\tworkersPerClient: 1,\n\t})\n}\n\nfunc BenchmarkCallsConcurrentServer(b *testing.B) {\n\tbenchmarkCallsN(b, benchmarkConfig{\n\t\tnumCalls:         b.N,\n\t\tnumServers:       1,\n\t\tnumClients:       runtime.GOMAXPROCS(0),\n\t\tworkersPerClient: 1,\n\t})\n}\n\nfunc BenchmarkCallsConcurrentClient(b *testing.B) {\n\tparallelism := runtime.GOMAXPROCS(0)\n\tbenchmarkCallsN(b, benchmarkConfig{\n\t\tnumCalls:         b.N,\n\t\tnumServers:       parallelism,\n\t\tnumClients:       1,\n\t\tworkersPerClient: parallelism,\n\t})\n}\n"
  },
  {
    "path": "connection_direction.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport \"fmt\"\n\ntype connectionDirection int\n\nconst (\n\tinbound connectionDirection = iota + 1\n\toutbound\n)\n\nfunc (d connectionDirection) String() string {\n\tswitch d {\n\tcase inbound:\n\t\treturn \"inbound\"\n\tcase outbound:\n\t\treturn \"outbound\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"connectionDirection(%v)\", int(d))\n\t}\n}\n"
  },
  {
    "path": "connection_internal_test.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"net\"\n\t\"net/http/httptest\"\n\t\"syscall\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype errSyscallConn struct {\n\tnet.Conn\n}\n\nfunc (c errSyscallConn) SyscallConn() (syscall.RawConn, error) {\n\treturn nil, assert.AnError\n}\n\nfunc TestGetSysConn(t *testing.T) {\n\tt.Run(\"no SyscallConn\", func(t *testing.T) {\n\t\tloggerBuf := &bytes.Buffer{}\n\t\tlogger := NewLogger(loggerBuf)\n\n\t\ttype dummyConn struct {\n\t\t\tnet.Conn\n\t\t}\n\n\t\tsyscallConn := getSysConn(dummyConn{}, logger)\n\t\trequire.Nil(t, syscallConn, \"expected no syscall.RawConn to be returned\")\n\t\tassert.Contains(t, loggerBuf.String(), \"Connection does not implement SyscallConn\", \"missing log\")\n\t\tassert.Contains(t, loggerBuf.String(), \"dummyConn\", \"missing type in log\")\n\t})\n\n\tt.Run(\"SyscallConn returns error\", func(t *testing.T) {\n\t\tloggerBuf := &bytes.Buffer{}\n\t\tlogger := NewLogger(loggerBuf)\n\n\t\tsyscallConn := getSysConn(errSyscallConn{}, logger)\n\t\trequire.Nil(t, syscallConn, \"expected no syscall.RawConn to be returned\")\n\t\tassert.Contains(t, loggerBuf.String(), \"Could not get SyscallConn\", \"missing log\")\n\t\tassert.Contains(t, loggerBuf.String(), assert.AnError.Error(), \"missing error in log\")\n\t})\n\n\tt.Run(\"SyscallConn is successful\", func(t *testing.T) {\n\t\tloggerBuf := &bytes.Buffer{}\n\t\tlogger := NewLogger(loggerBuf)\n\n\t\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\t\trequire.NoError(t, err, \"Failed to listen\")\n\t\tdefer ln.Close()\n\n\t\tconn, err := net.Dial(\"tcp\", ln.Addr().String())\n\t\trequire.NoError(t, err, \"failed to dial\")\n\t\tdefer conn.Close()\n\n\t\tsysConn := getSysConn(conn, logger)\n\t\trequire.NotNil(t, sysConn)\n\t\tassert.Empty(t, loggerBuf.String(), \"expected no logs on success\")\n\t})\n\n\tt.Run(\"SyscallConn is successful with TLS\", func(t *testing.T) {\n\t\tvar (\n\t\t\tloggerBuf = &bytes.Buffer{}\n\t\t\tlogger    = NewLogger(loggerBuf)\n\t\t\tserver    = httptest.NewTLSServer(nil)\n\t\t)\n\t\tdefer server.Close()\n\n\t\tconn, err := tls.Dial(\"tcp\", server.Listener.Addr().String(), &tls.Config{InsecureSkipVerify: true})\n\t\trequire.NoError(t, err, \"failed to dial\")\n\t\tdefer conn.Close()\n\n\t\tsysConn := getSysConn(conn, logger)\n\t\trequire.NotNil(t, sysConn)\n\t\tassert.Empty(t, loggerBuf.String(), \"expected no logs on success\")\n\t})\n\n\tt.Run(\"no SyscallConn - nil net.Conn\", func(t *testing.T) {\n\t\tvar (\n\t\t\tloggerBuf   = &bytes.Buffer{}\n\t\t\tlogger      = NewLogger(loggerBuf)\n\t\t\tsyscallConn = getSysConn(nil /* conn */, logger)\n\t\t)\n\n\t\trequire.Nil(t, syscallConn, \"expected no syscall.RawConn to be returned\")\n\t\tassert.Contains(t, loggerBuf.String(), \"Connection does not implement SyscallConn\", \"missing log\")\n\t\tassert.Contains(t, loggerBuf.String(), \"{connectionType <nil>}\", \"missing type in log\")\n\t})\n\n\tt.Run(\"no SyscallConn - TLS with no net.Conn\", func(t *testing.T) {\n\t\tvar (\n\t\t\tloggerBuf   = &bytes.Buffer{}\n\t\t\tlogger      = NewLogger(loggerBuf)\n\t\t\tsyscallConn = getSysConn(&tls.Conn{}, logger)\n\t\t)\n\n\t\trequire.Nil(t, syscallConn, \"expected no syscall.RawConn to be returned\")\n\t\tassert.Contains(t, loggerBuf.String(), \"Connection does not implement SyscallConn\", \"missing log\")\n\t\tassert.Contains(t, loggerBuf.String(), \"{connectionType *tls.Conn}\", \"missing type in log\")\n\t})\n}\n"
  },
  {
    "path": "connection_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/relay/relaytest\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\t\"github.com/uber/tchannel-go/testutils/testreader\"\n\t\"github.com/uber/tchannel-go/tos\"\n\t\"github.com/uber/tchannel-go/typed\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n\t\"golang.org/x/net/ipv4\"\n\t\"golang.org/x/net/ipv6\"\n)\n\n// Values used in tests\nconst (\n\tinbound  = 0\n\toutbound = 1\n)\n\nvar (\n\ttestArg2 = []byte(\"Header in arg2\")\n\ttestArg3 = []byte(\"Body in arg3\")\n)\n\ntype testHandler struct {\n\tsync.Mutex\n\n\tt        testing.TB\n\tformat   Format\n\tcaller   string\n\tblockErr chan error\n}\n\nfunc newTestHandler(t testing.TB) *testHandler {\n\treturn &testHandler{t: t, blockErr: make(chan error, 1)}\n}\n\nfunc (h *testHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\th.Lock()\n\th.format = args.Format\n\th.caller = args.Caller\n\th.Unlock()\n\n\tassert.Equal(h.t, args.Caller, CurrentCall(ctx).CallerName())\n\n\tswitch args.Method {\n\tcase \"block\":\n\t\t<-ctx.Done()\n\t\th.blockErr <- ctx.Err()\n\t\treturn &raw.Res{\n\t\t\tIsErr: true,\n\t\t}, nil\n\tcase \"echo\":\n\t\treturn &raw.Res{\n\t\t\tArg2: args.Arg2,\n\t\t\tArg3: args.Arg3,\n\t\t}, nil\n\tcase \"busy\":\n\t\treturn &raw.Res{\n\t\t\tSystemErr: ErrServerBusy,\n\t\t}, nil\n\tcase \"app-error\":\n\t\treturn &raw.Res{\n\t\t\tIsErr: true,\n\t\t}, nil\n\t}\n\treturn nil, errors.New(\"unknown method\")\n}\n\nfunc (h *testHandler) OnError(ctx context.Context, err error) {\n\tstack := make([]byte, 4096)\n\truntime.Stack(stack, false /* all */)\n\th.t.Errorf(\"testHandler got error: %v stack:\\n%s\", err, stack)\n}\n\nfunc writeFlushStr(w ArgWriter, d string) error {\n\tif _, err := io.WriteString(w, d); err != nil {\n\t\treturn err\n\t}\n\treturn w.Flush()\n}\n\nfunc isTosPriority(c net.Conn, tosPriority tos.ToS) (bool, error) {\n\tvar connTosPriority int\n\tvar err error\n\n\tswitch ip := c.RemoteAddr().(*net.TCPAddr).IP; {\n\tcase ip.To16() != nil && ip.To4() == nil:\n\t\tconnTosPriority, err = ipv6.NewConn(c).TrafficClass()\n\tcase ip.To4() != nil:\n\t\tconnTosPriority, err = ipv4.NewConn(c).TOS()\n\t}\n\n\treturn connTosPriority == int(tosPriority), err\n}\n\nfunc getErrorFrame(t testing.TB) *Frame {\n\tvar errFrame *Frame\n\tserver := testutils.NewServer(t, testutils.NewOpts().DisableLogVerification())\n\tdefer server.Close()\n\n\tframeRelay, cancel := testutils.FrameRelay(t, server.PeerInfo().HostPort, func(outgoing bool, f *Frame) *Frame {\n\t\tif strings.Contains(f.Header.String(), \"Error\") {\n\t\t\terrFrame = f\n\t\t}\n\t\treturn f\n\t})\n\tdefer cancel()\n\n\ttestutils.CallEcho(server, frameRelay, \"unknown\", nil)\n\trequire.NotNil(t, errFrame, \"Failed to get error frame\")\n\treturn errFrame\n}\n\nfunc TestRoundTrip(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\thandler := newTestHandler(t)\n\t\tts.Register(raw.Wrap(handler), \"echo\")\n\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\tcall, err := ts.Server().BeginCall(ctx, ts.HostPort(), ts.ServiceName(), \"echo\", &CallOptions{Format: JSON})\n\t\trequire.NoError(t, err)\n\t\tassert.NotEmpty(t, call.RemotePeer().HostPort)\n\t\tassert.Equal(t, ts.Server().PeerInfo(), call.LocalPeer(), \"Unexpected local peer\")\n\n\t\trequire.NoError(t, NewArgWriter(call.Arg2Writer()).Write(testArg2))\n\t\trequire.NoError(t, NewArgWriter(call.Arg3Writer()).Write(testArg3))\n\n\t\tvar respArg2 []byte\n\t\trequire.NoError(t, NewArgReader(call.Response().Arg2Reader()).Read(&respArg2))\n\t\tassert.Equal(t, testArg2, []byte(respArg2))\n\n\t\tvar respArg3 []byte\n\t\trequire.NoError(t, NewArgReader(call.Response().Arg3Reader()).Read(&respArg3))\n\t\tassert.Equal(t, testArg3, []byte(respArg3))\n\n\t\tassert.Equal(t, JSON, handler.format)\n\t\tassert.Equal(t, ts.ServiceName(), handler.caller)\n\t\tassert.Equal(t, JSON, call.Response().Format(), \"response Format should match request Format\")\n\t})\n}\n\nfunc TestDefaultFormat(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\thandler := newTestHandler(t)\n\t\tts.Register(raw.Wrap(handler), \"echo\")\n\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\targ2, arg3, resp, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"echo\", testArg2, testArg3)\n\t\trequire.Nil(t, err)\n\n\t\trequire.Equal(t, testArg2, arg2)\n\t\trequire.Equal(t, testArg3, arg3)\n\t\trequire.Equal(t, Raw, handler.format)\n\t\tassert.Equal(t, Raw, resp.Format(), \"response Format should match request Format\")\n\t})\n}\n\nfunc TestRemotePeer(t *testing.T) {\n\twantVersion := PeerVersion{\n\t\tLanguage:        \"go\",\n\t\tLanguageVersion: strings.TrimPrefix(runtime.Version(), \"go\"),\n\t\tTChannelVersion: VersionInfo,\n\t}\n\ttests := []struct {\n\t\tname       string\n\t\tremote     func(testing.TB, *testutils.TestServer) *Channel\n\t\texpectedFn func(*RuntimeState, *testutils.TestServer) PeerInfo\n\t}{\n\t\t{\n\t\t\tname:   \"ephemeral client\",\n\t\t\tremote: func(t testing.TB, ts *testutils.TestServer) *Channel { return ts.NewClient(nil) },\n\t\t\texpectedFn: func(state *RuntimeState, ts *testutils.TestServer) PeerInfo {\n\t\t\t\treturn PeerInfo{\n\t\t\t\t\tHostPort:    state.RootPeers[ts.HostPort()].OutboundConnections[0].LocalHostPort,\n\t\t\t\t\tIsEphemeral: true,\n\t\t\t\t\tProcessName: state.LocalPeer.ProcessName,\n\t\t\t\t\tVersion:     wantVersion,\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:   \"listening server\",\n\t\t\tremote: func(t testing.TB, ts *testutils.TestServer) *Channel { return ts.NewServer(nil) },\n\t\t\texpectedFn: func(state *RuntimeState, ts *testutils.TestServer) PeerInfo {\n\t\t\t\treturn PeerInfo{\n\t\t\t\t\tHostPort:    state.LocalPeer.HostPort,\n\t\t\t\t\tIsEphemeral: false,\n\t\t\t\t\tProcessName: state.LocalPeer.ProcessName,\n\t\t\t\t\tVersion:     wantVersion,\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t}\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tfor _, tt := range tests {\n\t\topts := testutils.NewOpts().SetServiceName(\"fake-service\").NoRelay()\n\t\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\tremote := tt.remote(t, ts)\n\t\t\tdefer remote.Close()\n\n\t\t\tgotPeer := make(chan PeerInfo, 1)\n\t\t\tts.RegisterFunc(\"test\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\t\tgotPeer <- CurrentCall(ctx).RemotePeer()\n\t\t\t\tassert.Equal(t, ts.Server().PeerInfo(), CurrentCall(ctx).LocalPeer())\n\t\t\t\treturn &raw.Res{}, nil\n\t\t\t})\n\n\t\t\t_, _, _, err := raw.Call(ctx, remote, ts.HostPort(), ts.Server().ServiceName(), \"test\", nil, nil)\n\t\t\tassert.NoError(t, err, \"%v: Call failed\", tt.name)\n\t\t\texpected := tt.expectedFn(remote.IntrospectState(nil), ts)\n\t\t\tassert.Equal(t, expected, <-gotPeer, \"%v: RemotePeer mismatch\", tt.name)\n\t\t})\n\t}\n}\n\nfunc TestReuseConnection(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\t// Since we're specifically testing that connections between hosts are re-used,\n\t// we can't interpose a relay in this test.\n\ts1Opts := testutils.NewOpts().SetServiceName(\"s1\").NoRelay()\n\n\ttestutils.WithTestServer(t, s1Opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tch2 := ts.NewServer(&testutils.ChannelOpts{ServiceName: \"s2\"})\n\t\thostPort2 := ch2.PeerInfo().HostPort\n\t\tdefer ch2.Close()\n\n\t\tts.Register(raw.Wrap(newTestHandler(t)), \"echo\")\n\t\tch2.Register(raw.Wrap(newTestHandler(t)), \"echo\")\n\n\t\toutbound, err := ts.Server().BeginCall(ctx, hostPort2, \"s2\", \"echo\", nil)\n\t\trequire.NoError(t, err)\n\t\toutboundConn, outboundNetConn := OutboundConnection(outbound)\n\n\t\t// Try to make another call at the same time, should reuse the same connection.\n\t\toutbound2, err := ts.Server().BeginCall(ctx, hostPort2, \"s2\", \"echo\", nil)\n\t\trequire.NoError(t, err)\n\t\toutbound2Conn, _ := OutboundConnection(outbound)\n\t\tassert.Equal(t, outboundConn, outbound2Conn)\n\n\t\t// Wait for the connection to be marked as active in ch2.\n\t\tassert.True(t, testutils.WaitFor(time.Second, func() bool {\n\t\t\treturn ch2.IntrospectState(nil).NumConnections > 0\n\t\t}), \"ch2 does not have any active connections\")\n\n\t\t// When ch2 tries to call the test server, it should reuse the existing\n\t\t// inbound connection the test server. Of course, this only works if the\n\t\t// test server -> ch2 call wasn't relayed.\n\t\toutbound3, err := ch2.BeginCall(ctx, ts.HostPort(), \"s1\", \"echo\", nil)\n\t\trequire.NoError(t, err)\n\t\t_, outbound3NetConn := OutboundConnection(outbound3)\n\t\tassert.Equal(t, outboundNetConn.RemoteAddr(), outbound3NetConn.LocalAddr())\n\t\tassert.Equal(t, outboundNetConn.LocalAddr(), outbound3NetConn.RemoteAddr())\n\n\t\t// Ensure all calls can complete in parallel.\n\t\tvar wg sync.WaitGroup\n\t\tfor _, call := range []*OutboundCall{outbound, outbound2, outbound3} {\n\t\t\twg.Add(1)\n\t\t\tgo func(call *OutboundCall) {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tresp1, resp2, _, err := raw.WriteArgs(call, []byte(\"arg2\"), []byte(\"arg3\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, resp1, []byte(\"arg2\"), \"result does match argument\")\n\t\t\t\tassert.Equal(t, resp2, []byte(\"arg3\"), \"result does match argument\")\n\t\t\t}(call)\n\t\t}\n\t\twg.Wait()\n\t})\n}\n\nfunc TestPing(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\terrFrame := getErrorFrame(t)\n\t\tvar returnErr bool\n\t\tframeRelay, close := testutils.FrameRelay(t, ts.HostPort(), func(outgoing bool, f *Frame) *Frame {\n\t\t\tif !outgoing && returnErr {\n\t\t\t\terrFrame.Header.ID = f.Header.ID\n\t\t\t\tf = errFrame\n\t\t\t}\n\t\t\treturn f\n\t\t})\n\t\tdefer close()\n\n\t\tclientCh := ts.NewClient(nil)\n\t\tdefer clientCh.Close()\n\t\trequire.NoError(t, clientCh.Ping(ctx, frameRelay))\n\n\t\tconn, err := clientCh.RootPeers().GetOrAdd(frameRelay).GetConnection(ctx)\n\t\trequire.NoError(t, err, \"Failed to get connection\")\n\n\t\treturnErr = true\n\t\trequire.Error(t, conn.Ping(ctx), \"Expect error from error frame\")\n\n\t\trequire.True(t, conn.IsActive(), \"Connection should still be active after error frame\")\n\t\treturnErr = false\n\t\trequire.NoError(t, conn.Ping(ctx), \"Ping should succeed\")\n\t})\n}\n\nfunc TestBadRequest(t *testing.T) {\n\t// ch will log an error when it receives a request for an unknown handler.\n\topts := testutils.NewOpts().AddLogFilter(\"Couldn't find handler.\", 1)\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"Noone\", []byte(\"Headers\"), []byte(\"Body\"))\n\t\trequire.NotNil(t, err)\n\t\tassert.Equal(t, ErrCodeBadRequest, GetSystemErrorCode(err))\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(ts.ServiceName(), ts.ServiceName(), \"Noone\").Failed(\"bad-request\").End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestNoTimeout(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(raw.Wrap(newTestHandler(t)), \"Echo\")\n\n\t\tctx := context.Background()\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), \"svc\", \"Echo\", []byte(\"Headers\"), []byte(\"Body\"))\n\t\tassert.Equal(t, ErrTimeoutRequired, err)\n\n\t\tts.AssertRelayStats(relaytest.NewMockStats())\n\t})\n}\n\nfunc TestCancelled(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(raw.Wrap(newTestHandler(t)), \"echo\")\n\t\tctx, cancel := NewContext(time.Second)\n\n\t\t// Make a call first to make sure we have a connection.\n\t\t// We want to test the BeginCall path.\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"echo\", []byte(\"Headers\"), []byte(\"Body\"))\n\t\tassert.NoError(t, err, \"Call failed\")\n\n\t\t// Now cancel the context.\n\t\tcancel()\n\t\t_, _, _, err = raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"echo\", []byte(\"Headers\"), []byte(\"Body\"))\n\t\tassert.Equal(t, ErrRequestCancelled, err, \"Unexpected error when making call with canceled context\")\n\t})\n}\n\nfunc TestNoServiceNaming(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), \"\", \"Echo\", []byte(\"Headers\"), []byte(\"Body\"))\n\t\tassert.Equal(t, ErrNoServiceName, err)\n\n\t\tts.AssertRelayStats(relaytest.NewMockStats())\n\t})\n}\n\nfunc TestServerBusy(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(ErrorHandlerFunc(func(ctx context.Context, call *InboundCall) error {\n\t\t\tif _, err := raw.ReadArgs(call); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn ErrServerBusy\n\t\t}), \"busy\")\n\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"busy\", []byte(\"Arg2\"), []byte(\"Arg3\"))\n\t\trequire.NotNil(t, err)\n\t\tassert.Equal(t, ErrCodeBusy, GetSystemErrorCode(err), \"err: %v\", err)\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(ts.ServiceName(), ts.ServiceName(), \"busy\").Failed(\"busy\").End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestUnexpectedHandlerError(t *testing.T) {\n\topts := testutils.NewOpts().\n\t\tAddLogFilter(\"Unexpected handler error\", 1)\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(ErrorHandlerFunc(func(ctx context.Context, call *InboundCall) error {\n\t\t\tif _, err := raw.ReadArgs(call); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"nope\")\n\t\t}), \"nope\")\n\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"nope\", []byte(\"Arg2\"), []byte(\"Arg3\"))\n\t\trequire.NotNil(t, err)\n\t\tassert.Equal(t, ErrCodeUnexpected, GetSystemErrorCode(err), \"err: %v\", err)\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(ts.ServiceName(), ts.ServiceName(), \"nope\").Failed(\"unexpected-error\").End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\ntype onErrorTestHandler struct {\n\t*testHandler\n\tonError func(ctx context.Context, err error)\n}\n\nfunc (h onErrorTestHandler) OnError(ctx context.Context, err error) {\n\th.onError(ctx, err)\n}\n\nfunc TestTimeout(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\t// onError may be called when the block call tries to write the call response.\n\t\tonError := func(ctx context.Context, err error) {\n\t\t\tassert.Equal(t, ErrTimeout, err, \"onError err should be ErrTimeout\")\n\t\t\tassert.Equal(t, context.DeadlineExceeded, ctx.Err(), \"Context should timeout\")\n\t\t}\n\t\ttestHandler := onErrorTestHandler{newTestHandler(t), onError}\n\t\tts.Register(raw.Wrap(testHandler), \"block\")\n\n\t\tctx, cancel := NewContext(testutils.Timeout(100 * time.Millisecond))\n\t\tdefer cancel()\n\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"block\", []byte(\"Arg2\"), []byte(\"Arg3\"))\n\t\tassert.Equal(t, ErrTimeout, err)\n\n\t\t// Verify the server-side receives an error from the context.\n\t\tselect {\n\t\tcase err := <-testHandler.blockErr:\n\t\t\tassert.Equal(t, context.DeadlineExceeded, err, \"Server should have received timeout\")\n\t\tcase <-time.After(time.Second):\n\t\t\tt.Errorf(\"Server did not receive call, may need higher timeout\")\n\t\t}\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(ts.ServiceName(), ts.ServiceName(), \"block\").Failed(\"timeout\").End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestServerClientCancellation(t *testing.T) {\n\topts := testutils.NewOpts()\n\topts.DefaultConnectionOptions.SendCancelOnContextCanceled = true\n\topts.DefaultConnectionOptions.PropagateCancel = true\n\n\tserverStats := newRecordingStatsReporter()\n\topts.StatsReporter = serverStats\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tcallReceived := make(chan struct{})\n\t\ttestutils.RegisterFunc(ts.Server(), \"ctxWait\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\trequire.NoError(t, ctx.Err(), \"context valid before cancellation\")\n\t\t\tclose(callReceived)\n\n\t\t\t<-ctx.Done()\n\t\t\tassert.Equal(t, context.Canceled, ctx.Err())\n\t\t\treturn nil, assert.AnError\n\t\t})\n\n\t\tctx, cancel := NewContext(3 * time.Second)\n\t\tdefer cancel()\n\n\t\t// Wait for the call to be recieved by the server before cancelling the context.\n\t\tgo func() {\n\t\t\t<-callReceived\n\t\t\tcancel()\n\t\t}()\n\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"ctxWait\", nil, nil)\n\t\tassert.Equal(t, ErrRequestCancelled, err, \"client call result\")\n\n\t\tstatsTags := ts.Server().StatsTags()\n\t\tserverStats.Expected.IncCounter(\"inbound.cancels.requested\", statsTags, 1)\n\t\tserverStats.Expected.IncCounter(\"inbound.cancels.honored\", statsTags, 1)\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(ts.ServiceName(), ts.ServiceName(), \"ctxWait\").Failed(\"canceled\").End()\n\t\tts.AssertRelayStats(calls)\n\t})\n\n\tserverStats.ValidateExpected(t)\n}\n\nfunc TestCancelWithoutSendCancelOnContextCanceled(t *testing.T) {\n\ttests := []struct {\n\t\tmsg                         string\n\t\tsendCancelOnContextCanceled bool\n\t\twantCancelRequested         bool\n\t}{\n\t\t{\n\t\t\tmsg:                         \"no send or process cancel\",\n\t\t\tsendCancelOnContextCanceled: false,\n\t\t},\n\t\t{\n\t\t\tmsg:                         \"only enable cancels on outbounds\",\n\t\t\tsendCancelOnContextCanceled: true,\n\t\t\twantCancelRequested:         true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\topts := testutils.NewOpts()\n\t\t\topts.DefaultConnectionOptions.SendCancelOnContextCanceled = tt.sendCancelOnContextCanceled\n\n\t\t\tserverStats := newRecordingStatsReporter()\n\t\t\topts.StatsReporter = serverStats\n\n\t\t\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\t\tserverStats.Reset()\n\n\t\t\t\tcallReceived := make(chan struct{})\n\t\t\t\ttestutils.RegisterFunc(ts.Server(), \"ctxWait\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\t\t\trequire.NoError(t, ctx.Err(), \"context valid before cancellation\")\n\t\t\t\t\tclose(callReceived)\n\n\t\t\t\t\t<-ctx.Done()\n\t\t\t\t\tassert.Equal(t, context.DeadlineExceeded, ctx.Err())\n\t\t\t\t\treturn nil, assert.AnError\n\t\t\t\t})\n\n\t\t\t\tctx, cancel := NewContext(testutils.Timeout(300 * time.Millisecond))\n\t\t\t\tdefer cancel()\n\n\t\t\t\t// Wait for the call to be recieved by the server before cancelling the context.\n\t\t\t\tgo func() {\n\t\t\t\t\t<-callReceived\n\t\t\t\t\tcancel()\n\t\t\t\t}()\n\n\t\t\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"ctxWait\", nil, nil)\n\t\t\t\tassert.Equal(t, ErrRequestCancelled, err, \"client call result\")\n\n\t\t\t\tcalls := relaytest.NewMockStats()\n\t\t\t\tcalls.Add(ts.ServiceName(), ts.ServiceName(), \"ctxWait\").Failed(\"timeout\").End()\n\t\t\t\tts.AssertRelayStats(calls)\n\n\t\t\t\tts.AddPostFn(func() {\n\t\t\t\t\t// Validating these at the end of the test, when server has fully processed the cancellation.\n\t\t\t\t\tif tt.wantCancelRequested && !ts.HasRelay() {\n\t\t\t\t\t\tserverStats.Expected.IncCounter(\"inbound.cancels.requested\", ts.Server().StatsTags(), 1)\n\t\t\t\t\t\tserverStats.ValidateExpected(t)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tserverStats.EnsureNotPresent(t, \"inbound.cancels.requested\")\n\t\t\t\t\t}\n\t\t\t\t\tserverStats.EnsureNotPresent(t, \"inbound.cancels.honored\")\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestLargeMethod(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\tlargeMethod := testutils.RandBytes(16*1024 + 1)\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), string(largeMethod), nil, nil)\n\t\tassert.Equal(t, ErrMethodTooLarge, err)\n\t})\n}\n\nfunc TestLargeTimeout(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(raw.Wrap(newTestHandler(t)), \"echo\")\n\n\t\tctx, cancel := NewContext(1000 * time.Second)\n\t\tdefer cancel()\n\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"echo\", testArg2, testArg3)\n\t\tassert.NoError(t, err, \"Call failed\")\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(ts.ServiceName(), ts.ServiceName(), \"echo\").Succeeded().End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestFragmentation(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(raw.Wrap(newTestHandler(t)), \"echo\")\n\n\t\targ2 := make([]byte, MaxFramePayloadSize*2)\n\t\tfor i := 0; i < len(arg2); i++ {\n\t\t\targ2[i] = byte('a' + (i % 10))\n\t\t}\n\n\t\targ3 := make([]byte, MaxFramePayloadSize*3)\n\t\tfor i := 0; i < len(arg3); i++ {\n\t\t\targ3[i] = byte('A' + (i % 10))\n\t\t}\n\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\trespArg2, respArg3, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"echo\", arg2, arg3)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, arg2, respArg2)\n\t\tassert.Equal(t, arg3, respArg3)\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(ts.ServiceName(), ts.ServiceName(), \"echo\").Succeeded().End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestFragmentationSlowReader(t *testing.T) {\n\t// Inbound forward will timeout and cause a warning log.\n\topts := testutils.NewOpts().\n\t\tAddLogFilter(\"Unable to forward frame\", 1).\n\t\tAddLogFilter(\"Connection error\", 1)\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tstartReading, handlerComplete := make(chan struct{}), make(chan struct{})\n\t\thandler := func(ctx context.Context, call *InboundCall) {\n\t\t\t<-startReading\n\t\t\t<-ctx.Done()\n\t\t\t_, err := raw.ReadArgs(call)\n\t\t\tassert.Error(t, err, \"ReadArgs should fail since frames will be dropped due to slow reading\")\n\t\t\tclose(handlerComplete)\n\t\t}\n\n\t\tts.Register(HandlerFunc(handler), \"echo\")\n\n\t\targ2 := testutils.RandBytes(MaxFramePayloadSize * MexChannelBufferSize)\n\t\targ3 := testutils.RandBytes(MaxFramePayloadSize * (MexChannelBufferSize + 1))\n\n\t\tctx, cancel := NewContext(testutils.Timeout(30 * time.Millisecond))\n\t\tdefer cancel()\n\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"echo\", arg2, arg3)\n\t\tassert.Error(t, err, \"Call should timeout due to slow reader\")\n\n\t\tclose(startReading)\n\t\tselect {\n\t\tcase <-handlerComplete:\n\t\tcase <-time.After(testutils.Timeout(70 * time.Millisecond)):\n\t\t\tt.Errorf(\"Handler not called, context timeout may be too low\")\n\t\t}\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(ts.ServiceName(), ts.ServiceName(), \"echo\").Failed(\"timeout\").End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestWriteArg3AfterTimeout(t *testing.T) {\n\t// TODO: Debug why this is flaky in github\n\tif os.Getenv(\"GITHUB_WORKFLOW\") != \"\" {\n\t\tt.Skip(\"skipping test flaky in github actions.\")\n\t}\n\t// The channel reads and writes during timeouts, causing warning logs.\n\topts := testutils.NewOpts().DisableLogVerification()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttimedOut := make(chan struct{})\n\n\t\thandler := func(ctx context.Context, call *InboundCall) {\n\t\t\t_, err := raw.ReadArgs(call)\n\t\t\tassert.NoError(t, err, \"Read args failed\")\n\t\t\tresponse := call.Response()\n\t\t\tassert.NoError(t, NewArgWriter(response.Arg2Writer()).Write(nil), \"Write Arg2 failed\")\n\t\t\twriter, err := response.Arg3Writer()\n\t\t\tassert.NoError(t, err, \"Arg3Writer failed\")\n\n\t\t\tfor {\n\t\t\t\tif _, err := writer.Write(testutils.RandBytes(4)); err != nil {\n\t\t\t\t\tassert.Equal(t, err, ErrTimeout, \"Handler should timeout\")\n\t\t\t\t\tclose(timedOut)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\truntime.Gosched()\n\t\t\t}\n\t\t}\n\t\tts.Register(HandlerFunc(handler), \"call\")\n\n\t\tctx, cancel := NewContext(testutils.Timeout(100 * time.Millisecond))\n\t\tdefer cancel()\n\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"call\", nil, nil)\n\t\tassert.Equal(t, err, ErrTimeout, \"Call should timeout\")\n\n\t\t// Wait for the write to complete, make sure there are no errors.\n\t\tselect {\n\t\tcase <-time.After(testutils.Timeout(300 * time.Millisecond)):\n\t\t\tt.Errorf(\"Handler should have failed due to timeout\")\n\t\tcase <-timedOut:\n\t\t}\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(ts.ServiceName(), ts.ServiceName(), \"call\").Failed(\"timeout\").Succeeded().End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestLargeSendSystemError(t *testing.T) {\n\tlargeStr := strings.Repeat(\"0123456789\", 10000)\n\n\ttests := []struct {\n\t\tmsg     string\n\t\terr     error\n\t\twantErr string\n\t}{\n\t\t{\n\t\t\tmsg:     \"error message too long\",\n\t\t\terr:     errors.New(largeStr),\n\t\t\twantErr: \"too long\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"max allowed error message\",\n\t\t\terr:     errors.New(largeStr[:math.MaxUint16-1]),\n\t\t\twantErr: typed.ErrBufferFull.Error(), // error message is within length, but it overflows the frame.\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\t\tdefer cancel()\n\n\t\t\t\topts := testutils.NewOpts().AddLogFilter(\"Couldn't create outbound frame.\", 1)\n\t\t\t\tclient := ts.NewClient(opts)\n\t\t\t\tconn, err := client.Connect(ctx, ts.HostPort())\n\t\t\t\trequire.NoError(t, err, \"Connect failed\")\n\n\t\t\t\terr = conn.SendSystemError(1, Span{}, tt.err)\n\t\t\t\trequire.Error(t, err, \"Expect err\")\n\t\t\t\tassert.Contains(t, err.Error(), tt.wantErr, \"unexpected error\")\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestWriteErrorAfterTimeout(t *testing.T) {\n\t// TODO: Make this test block at different points (e.g. before, during read/write).\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttimedOut := make(chan struct{})\n\t\tdone := make(chan struct{})\n\t\thandler := func(ctx context.Context, call *InboundCall) {\n\t\t\t<-ctx.Done()\n\t\t\t<-timedOut\n\t\t\t_, err := raw.ReadArgs(call)\n\t\t\tassert.Equal(t, ErrTimeout, err, \"Read args should fail with timeout\")\n\t\t\tresponse := call.Response()\n\t\t\tassert.Equal(t, ErrTimeout, response.SendSystemError(ErrServerBusy), \"SendSystemError should fail\")\n\t\t\tclose(done)\n\t\t}\n\t\tts.Register(HandlerFunc(handler), \"call\")\n\n\t\tctx, cancel := NewContext(testutils.Timeout(30 * time.Millisecond))\n\t\tdefer cancel()\n\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"call\", nil, testutils.RandBytes(100000))\n\t\tassert.Equal(t, err, ErrTimeout, \"Call should timeout\")\n\t\tclose(timedOut)\n\n\t\tselect {\n\t\tcase <-done:\n\t\tcase <-time.After(time.Second):\n\t\t\tt.Errorf(\"Handler not called, timeout may be too low\")\n\t\t}\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(ts.ServiceName(), ts.ServiceName(), \"call\").Failed(\"timeout\").End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestWriteAfterConnectionError(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\t// Closing network connections can lead to warnings in many places.\n\t// TODO: Relay is disabled due to https://github.com/uber/tchannel-go/issues/390\n\t// Enabling relay causes the test to be flaky.\n\topts := testutils.NewOpts().DisableLogVerification().NoRelay()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\t\tserver := ts.Server()\n\n\t\tcall, err := server.BeginCall(ctx, ts.HostPort(), server.ServiceName(), \"echo\", nil)\n\t\trequire.NoError(t, err, \"Call failed\")\n\n\t\tw, err := call.Arg2Writer()\n\t\trequire.NoError(t, err, \"Arg2Writer failed\")\n\t\trequire.NoError(t, writeFlushStr(w, \"initial\"), \"write initial failed\")\n\n\t\t// Now close the underlying network connection, writes should fail.\n\t\t_, conn := OutboundConnection(call)\n\t\tconn.Close()\n\n\t\t// Writes should start failing pretty soon.\n\t\tvar writeErr error\n\t\tfor i := 0; i < 100; i++ {\n\t\t\tif writeErr = writeFlushStr(w, \"f\"); writeErr != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\ttime.Sleep(time.Millisecond)\n\t\t}\n\t\tif assert.Error(t, writeErr, \"Writes should fail after a connection is closed\") {\n\t\t\tassert.Equal(t, ErrCodeNetwork, GetSystemErrorCode(writeErr), \"write should fail due to network error\")\n\t\t}\n\t})\n}\n\nfunc TestReadTimeout(t *testing.T) {\n\t// The error frame may fail to send since the connection closes before the handler sends it\n\t// or the handler connection may be closed as it sends when the other side closes the conn.\n\topts := testutils.NewOpts().\n\t\tAddLogFilter(\"Couldn't send outbound error frame\", 1).\n\t\tAddLogFilter(\"Connection error\", 1, \"site\", \"read frames\").\n\t\tAddLogFilter(\"Connection error\", 1, \"site\", \"write frames\").\n\t\tAddLogFilter(\"simpleHandler OnError\", 1,\n\t\t\t\"error\", \"failed to send error frame, connection state connectionClosed\")\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tsn := ts.ServiceName()\n\t\tcalls := relaytest.NewMockStats()\n\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tctx, cancel := NewContext(time.Second)\n\t\t\thandler := func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\t\tdefer cancel()\n\t\t\t\treturn nil, ErrRequestCancelled\n\t\t\t}\n\t\t\tts.RegisterFunc(\"call\", handler)\n\n\t\t\t_, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), \"call\", nil, nil)\n\t\t\tassert.Equal(t, err, ErrRequestCancelled, \"Call should fail due to cancel\")\n\t\t\tcalls.Add(sn, sn, \"call\").Failed(\"cancelled\").End()\n\t\t}\n\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestWriteTimeout(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tch := ts.Server()\n\t\tctx, cancel := NewContext(testutils.Timeout(100 * time.Millisecond))\n\t\tdefer cancel()\n\n\t\tcall, err := ch.BeginCall(ctx, ts.HostPort(), ch.ServiceName(), \"call\", nil)\n\t\trequire.NoError(t, err, \"Call failed\")\n\n\t\twriter, err := call.Arg2Writer()\n\t\trequire.NoError(t, err, \"Arg2Writer failed\")\n\n\t\t_, err = writer.Write([]byte{1})\n\t\trequire.NoError(t, err, \"Write initial bytes failed\")\n\t\t<-ctx.Done()\n\n\t\t_, err = io.Copy(writer, testreader.Looper([]byte{1}))\n\t\tassert.Equal(t, ErrTimeout, err, \"Write should fail with timeout\")\n\n\t\tts.AssertRelayStats(relaytest.NewMockStats())\n\t})\n}\n\nfunc TestGracefulClose(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tch2 := ts.NewServer(nil)\n\t\thp2 := ch2.PeerInfo().HostPort\n\t\tdefer ch2.Close()\n\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\tassert.NoError(t, ts.Server().Ping(ctx, hp2), \"Ping from ch1 -> ch2 failed\")\n\t\tassert.NoError(t, ch2.Ping(ctx, ts.HostPort()), \"Ping from ch2 -> ch1 failed\")\n\n\t\t// No stats for pings.\n\t\tts.AssertRelayStats(relaytest.NewMockStats())\n\t})\n}\n\nfunc TestNetDialTimeout(t *testing.T) {\n\t// timeoutHostPort uses a blackholed address (RFC 6890) with a port\n\t// reserved for documentation. This address should always cause a timeout.\n\tconst timeoutHostPort = \"192.18.0.254:44444\"\n\ttimeoutPeriod := testutils.Timeout(50 * time.Millisecond)\n\n\tclient := testutils.NewClient(t, nil)\n\tdefer client.Close()\n\n\tstarted := time.Now()\n\tctx, cancel := NewContext(timeoutPeriod)\n\tdefer cancel()\n\n\terr := client.Ping(ctx, timeoutHostPort)\n\tif !assert.Error(t, err, \"Ping to blackhole address should fail\") {\n\t\treturn\n\t}\n\n\tif strings.Contains(err.Error(), \"network is unreachable\") {\n\t\tt.Skipf(\"Skipping test, as network interface may not be available\")\n\t}\n\n\td := time.Since(started)\n\tassert.Equal(t, ErrTimeout, err, \"Ping expected to fail with timeout\")\n\tassert.True(t, d >= timeoutPeriod, \"Timeout should take more than %v, took %v\", timeoutPeriod, d)\n}\n\nfunc TestConnectTimeout(t *testing.T) {\n\topts := testutils.NewOpts().DisableLogVerification()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t// Set up a relay that will delay the initial init req.\n\t\ttestComplete := make(chan struct{})\n\n\t\trelayFunc := func(outgoing bool, f *Frame) *Frame {\n\t\t\tselect {\n\t\t\tcase <-time.After(testutils.Timeout(200 * time.Millisecond)):\n\t\t\t\treturn f\n\t\t\tcase <-testComplete:\n\t\t\t\t// TODO: We should be able to forward the frame and have this test not fail.\n\t\t\t\t// Currently, it fails since the sequence of events is:\n\t\t\t\t// Server receives a TCP connection\n\t\t\t\t// Channel.Close() is called on the server\n\t\t\t\t// Server's TCP connection receives an init req\n\t\t\t\t// Since we don't currently track pending connections, the open TCP connection is not closed, and\n\t\t\t\t// we process the init req. This leaves an open connection at the end of the test.\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\trelay, shutdown := testutils.FrameRelay(t, ts.HostPort(), relayFunc)\n\t\tdefer shutdown()\n\n\t\t// Make a call with a long timeout, but short connect timeout.\n\t\t// We expect the call to fall almost immediately with ErrTimeout.\n\t\tctx, cancel := NewContextBuilder(2 * time.Second).\n\t\t\tSetConnectTimeout(testutils.Timeout(100 * time.Millisecond)).\n\t\t\tBuild()\n\t\tdefer cancel()\n\n\t\tclient := ts.NewClient(opts)\n\t\terr := client.Ping(ctx, relay)\n\t\tassert.Equal(t, ErrTimeout, err, \"Ping should timeout due to timeout relay\")\n\n\t\t// Note: we do not defer this, as we need to close(testComplete) before\n\t\t// we call shutdown since shutdown waits for the relay to close, which\n\t\t// is stuck waiting inside of our custom relay function.\n\t\tclose(testComplete)\n\t})\n}\n\nfunc TestParallelConnectionAccepts(t *testing.T) {\n\topts := testutils.NewOpts().AddLogFilter(\"Failed during connection handshake\", 1)\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\n\t\t// Start a connection attempt that should timeout.\n\t\tconn, err := net.Dial(\"tcp\", ts.HostPort())\n\t\tdefer conn.Close()\n\t\trequire.NoError(t, err, \"Dial failed\")\n\n\t\t// When we try to make a call using a new client, it will require a\n\t\t// new connection, and this verifies that the previous connection attempt\n\t\t// and handshake do not impact the call.\n\t\tclient := ts.NewClient(nil)\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t})\n}\n\nfunc TestConnectionIDs(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tvar inbound, outbound []uint32\n\t\trelayFunc := func(outgoing bool, f *Frame) *Frame {\n\t\t\tif outgoing {\n\t\t\t\toutbound = append(outbound, f.Header.ID)\n\t\t\t} else {\n\t\t\t\tinbound = append(inbound, f.Header.ID)\n\t\t\t}\n\t\t\treturn f\n\t\t}\n\t\trelay, shutdown := testutils.FrameRelay(t, ts.HostPort(), relayFunc)\n\t\tdefer shutdown()\n\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\ts2 := ts.NewServer(nil)\n\t\trequire.NoError(t, s2.Ping(ctx, relay), \"Ping failed\")\n\t\tassert.Equal(t, []uint32{1, 2}, outbound, \"Unexpected outbound IDs\")\n\t\tassert.Equal(t, []uint32{1, 2}, inbound, \"Unexpected outbound IDs\")\n\n\t\t// We want to reuse the same connection for the rest of the test which\n\t\t// only makes sense when the relay is not used.\n\t\tif ts.HasRelay() {\n\t\t\treturn\n\t\t}\n\n\t\tinbound = nil\n\t\toutbound = nil\n\t\t// We will reuse the inbound connection, but since the inbound connection\n\t\t// hasn't originated any outbound requests, we'll use id 1.\n\t\trequire.NoError(t, ts.Server().Ping(ctx, s2.PeerInfo().HostPort), \"Ping failed\")\n\t\tassert.Equal(t, []uint32{1}, outbound, \"Unexpected outbound IDs\")\n\t\tassert.Equal(t, []uint32{1}, inbound, \"Unexpected outbound IDs\")\n\t})\n}\n\nfunc TestTosPriority(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\topts := testutils.NewOpts().SetServiceName(\"s1\").SetTosPriority(tos.Lowdelay)\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(raw.Wrap(newTestHandler(t)), \"echo\")\n\n\t\toutbound, err := ts.Server().BeginCall(ctx, ts.HostPort(), \"s1\", \"echo\", nil)\n\t\trequire.NoError(t, err, \"BeginCall failed\")\n\n\t\t_, outboundNetConn := OutboundConnection(outbound)\n\t\tconnTosPriority, err := isTosPriority(outboundNetConn, tos.Lowdelay)\n\t\trequire.NoError(t, err, \"Checking TOS priority failed\")\n\t\tassert.Equal(t, connTosPriority, true)\n\t\t_, _, _, err = raw.WriteArgs(outbound, []byte(\"arg2\"), []byte(\"arg3\"))\n\t\trequire.NoError(t, err, \"Failed to write to outbound conn\")\n\t})\n}\n\nfunc TestPeerStatusChangeClientReduction(t *testing.T) {\n\tsopts := testutils.NewOpts().NoRelay()\n\ttestutils.WithTestServer(t, sopts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tserver := ts.Server()\n\t\ttestutils.RegisterEcho(server, nil)\n\t\tchanges := make(chan int, 2)\n\n\t\tcopts := testutils.NewOpts().SetOnPeerStatusChanged(func(p *Peer) {\n\t\t\ti, o := p.NumConnections()\n\t\t\tassert.Equal(t, 0, i, \"no inbound connections to client\")\n\t\t\tchanges <- o\n\t\t})\n\n\t\t// Induce the creation of a connection from client to server.\n\t\tclient := ts.NewClient(copts)\n\t\trequire.NoError(t, testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil))\n\t\tassert.Equal(t, 1, <-changes, \"event for first connection\")\n\n\t\t// Re-use\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\n\t\t// Induce the destruction of a connection from the server to the client.\n\t\tserver.Close()\n\t\tassert.Equal(t, 0, <-changes, \"event for second disconnection\")\n\n\t\tclient.Close()\n\t\tassert.Len(t, changes, 0, \"unexpected peer status changes\")\n\t})\n}\n\nfunc TestPeerStatusChangeClient(t *testing.T) {\n\tsopts := testutils.NewOpts().NoRelay()\n\ttestutils.WithTestServer(t, sopts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tserver := ts.Server()\n\t\ttestutils.RegisterEcho(server, nil)\n\t\tchanges := make(chan int, 2)\n\n\t\tcopts := testutils.NewOpts().SetOnPeerStatusChanged(func(p *Peer) {\n\t\t\ti, o := p.NumConnections()\n\t\t\tassert.Equal(t, 0, i, \"no inbound connections to client\")\n\t\t\tchanges <- o\n\t\t})\n\n\t\t// Induce the creation of a connection from client to server.\n\t\tclient := ts.NewClient(copts)\n\t\trequire.NoError(t, testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil))\n\t\tassert.Equal(t, 1, <-changes, \"event for first connection\")\n\n\t\t// Re-use\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\n\t\t// Induce the creation of a second connection from client to server.\n\t\tpl := client.RootPeers()\n\t\tp := pl.GetOrAdd(ts.HostPort())\n\t\tctx := context.Background()\n\t\tctx, cancel := context.WithTimeout(ctx, testutils.Timeout(100*time.Millisecond))\n\t\tdefer cancel()\n\t\t_, err := p.Connect(ctx)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, 2, <-changes, \"event for second connection\")\n\n\t\t// Induce the destruction of a connection from the server to the client.\n\t\tserver.Close()\n\t\t<-changes // May be 1 or 0 depending on timing.\n\t\tassert.Equal(t, 0, <-changes, \"event for second disconnection\")\n\n\t\tclient.Close()\n\t\tassert.Len(t, changes, 0, \"unexpected peer status changes\")\n\t})\n}\n\nfunc TestPeerStatusChangeServer(t *testing.T) {\n\tchanges := make(chan int, 10)\n\tsopts := testutils.NewOpts().NoRelay().SetOnPeerStatusChanged(func(p *Peer) {\n\t\ti, o := p.NumConnections()\n\t\tassert.Equal(t, 0, o, \"no outbound connections from server\")\n\t\tchanges <- i\n\t})\n\ttestutils.WithTestServer(t, sopts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tserver := ts.Server()\n\t\ttestutils.RegisterEcho(server, nil)\n\n\t\tcopts := testutils.NewOpts()\n\t\tfor i := 0; i < 5; i++ {\n\t\t\tclient := ts.NewClient(copts)\n\n\t\t\t// Open\n\t\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\t\tassert.Equal(t, 1, <-changes, \"one event on new connection\")\n\n\t\t\t// Re-use\n\t\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\t\tassert.Len(t, changes, 0, \"no new events on re-used connection\")\n\n\t\t\t// Close\n\t\t\tclient.Close()\n\t\t\tassert.Equal(t, 0, <-changes, \"one event on lost connection\")\n\t\t}\n\t})\n\tassert.Len(t, changes, 0, \"unexpected peer status changes\")\n}\n\nfunc TestContextCanceledOnTCPClose(t *testing.T) {\n\t// 1. Context canceled warning is expected as part of this test\n\t// add log filter to ignore this error\n\t// 2. We use our own relay in this test, so disable the relay\n\t// that comes with the test server\n\topts := testutils.NewOpts().NoRelay().AddLogFilter(\"simpleHandler OnError\", 1)\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tserverDoneC := make(chan struct{})\n\t\tcallForwarded := make(chan struct{})\n\n\t\tts.RegisterFunc(\"test\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\tdefer close(serverDoneC)\n\t\t\tclose(callForwarded)\n\t\t\t<-ctx.Done()\n\t\t\tassert.EqualError(t, ctx.Err(), \"context canceled\")\n\t\t\treturn &raw.Res{}, nil\n\t\t})\n\n\t\t// Set up a relay that can be used to terminate conns\n\t\t// on both sides i.e. client and server\n\t\trelayFunc := func(outgoing bool, f *Frame) *Frame {\n\t\t\treturn f\n\t\t}\n\t\trelayHostPort, shutdown := testutils.FrameRelay(t, ts.HostPort(), relayFunc)\n\n\t\t// Make a call with a long timeout. We shutdown the relay\n\t\t// immediately after the server receives the call. Expected\n\t\t// behavior is for both client/server to be done with the call\n\t\t// immediately after relay shutsdown\n\t\tctx, cancel := NewContext(20 * time.Second)\n\t\tdefer cancel()\n\n\t\tclientCh := ts.NewClient(nil)\n\t\t// initiate the call in a background routine and\n\t\t// make it wait for the response\n\t\tclientDoneC := make(chan struct{})\n\t\tgo func() {\n\t\t\traw.Call(ctx, clientCh, relayHostPort, ts.ServiceName(), \"test\", nil, nil)\n\t\t\tclose(clientDoneC)\n\t\t}()\n\n\t\t// wait for server to receive the call\n\t\tselect {\n\t\tcase <-callForwarded:\n\t\tcase <-time.After(2 * time.Second):\n\t\t\tassert.Fail(t, \"timed waiting for call to be forwarded\")\n\t\t}\n\n\t\t// now shutdown the relay to close conns\n\t\t// on both sides\n\t\tshutdown()\n\n\t\t// wait for both the client & server to be done\n\t\tselect {\n\t\tcase <-serverDoneC:\n\t\tcase <-time.After(2 * time.Second):\n\t\t\tassert.Fail(t, \"timed out waiting for server handler to exit\")\n\t\t}\n\n\t\tselect {\n\t\tcase <-clientDoneC:\n\t\tcase <-time.After(2 * time.Second):\n\t\t\tassert.Fail(t, \"timed out waiting for client to exit\")\n\t\t}\n\n\t\tclientCh.Close()\n\t})\n}\n\n// getConnection returns the introspection result for the unique inbound or\n// outbound connection. An assert will be raised if there is more than one\n// connection of the given type.\nfunc getConnection(t testing.TB, ch *Channel, direction int) *ConnectionRuntimeState {\n\tstate := ch.IntrospectState(nil)\n\n\tfor _, peer := range state.RootPeers {\n\t\tvar connections []ConnectionRuntimeState\n\t\tif direction == inbound {\n\t\t\tconnections = peer.InboundConnections\n\t\t} else {\n\t\t\tconnections = peer.OutboundConnections\n\t\t}\n\n\t\tassert.True(t, len(connections) <= 1, \"Too many connections found: %+v\", connections)\n\t\tif len(connections) == 1 {\n\t\t\treturn &connections[0]\n\t\t}\n\t}\n\n\tassert.FailNow(t, \"No connections found\")\n\treturn nil\n}\n\nfunc TestLastActivityTime(t *testing.T) {\n\tinitialTime := time.Date(2017, 11, 27, 21, 0, 0, 0, time.UTC)\n\tclock := testutils.NewStubClock(initialTime)\n\topts := testutils.NewOpts().SetTimeNow(clock.Now)\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tclient := ts.NewClient(opts)\n\t\tserver := ts.Server()\n\n\t\t// Channels for synchronization.\n\t\tcallReceived := make(chan struct{})\n\t\tblockResponse := make(chan struct{})\n\t\tresponseReceived := make(chan struct{})\n\n\t\t// Helper function that checks the last activity time on client, server and relay.\n\t\tvalidateLastActivityTime := func(expectedReq time.Time, expectedResp time.Time) {\n\t\t\tclientConn := getConnection(t, client, outbound)\n\t\t\tserverConn := getConnection(t, server, inbound)\n\t\t\treqTime := expectedReq.UnixNano()\n\t\t\trespTime := expectedResp.UnixNano()\n\n\t\t\tassert.Equal(t, reqTime, clientConn.LastActivityWrite)\n\t\t\tassert.Equal(t, reqTime, serverConn.LastActivityRead)\n\n\t\t\tassert.Equal(t, respTime, clientConn.LastActivityRead)\n\t\t\tassert.Equal(t, respTime, serverConn.LastActivityWrite)\n\n\t\t\t// Relays should act like both clients and servers.\n\t\t\tif ts.HasRelay() {\n\t\t\t\trelayInbound := getConnection(t, ts.Relay(), inbound)\n\t\t\t\trelayOutbound := getConnection(t, ts.Relay(), outbound)\n\n\t\t\t\tassert.Equal(t, reqTime, relayInbound.LastActivityRead)\n\t\t\t\tassert.Equal(t, reqTime, relayOutbound.LastActivityWrite)\n\n\t\t\t\tassert.Equal(t, respTime, relayInbound.LastActivityWrite)\n\t\t\t\tassert.Equal(t, respTime, relayOutbound.LastActivityRead)\n\t\t\t}\n\t\t}\n\n\t\t// The 'echo' handler emulates a process that takes 1 second to complete.\n\t\ttestutils.RegisterEcho(server, func() {\n\t\t\tcallReceived <- struct{}{}\n\t\t\t<-blockResponse\n\n\t\t\t// Increment the time and return a response.\n\t\t\tclock.Elapse(1 * time.Second)\n\t\t})\n\n\t\tinitTime := clock.Now()\n\t\t// Run the test twice, because the first call will also establish a connection.\n\t\tfor i := 0; i < 2; i++ {\n\t\t\tbeforeCallSent := clock.Now()\n\n\t\t\tgo func() {\n\t\t\t\trequire.NoError(t, testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil))\n\t\t\t\tresponseReceived <- struct{}{}\n\t\t\t}()\n\n\t\t\t// Verify that the last activity time was updated before a response is received.\n\t\t\t<-callReceived\n\t\t\tvalidateLastActivityTime(beforeCallSent, initTime)\n\n\t\t\t// Let the server respond.\n\t\t\tblockResponse <- struct{}{}\n\n\t\t\t// After a response was received, time of the response should be +1s,\n\t\t\t// without a change to the requet time.  Validate again that the last\n\t\t\t// activity time was updated.\n\t\t\t<-responseReceived\n\t\t\tvalidateLastActivityTime(beforeCallSent, beforeCallSent.Add(1*time.Second))\n\n\t\t\t// Set the initTime as the time of the last response.\n\t\t\tinitTime = beforeCallSent.Add(1 * time.Second)\n\n\t\t\t// Elapse the clock for our next iteration.\n\t\t\tclock.Elapse(1 * time.Minute)\n\t\t}\n\n\t\tclose(responseReceived)\n\t\tclose(blockResponse)\n\t\tclose(callReceived)\n\t})\n}\n\nfunc TestLastActivityTimePings(t *testing.T) {\n\tinitialTime := time.Date(2017, 11, 27, 21, 0, 0, 0, time.UTC)\n\tclock := testutils.NewStubClock(initialTime)\n\n\topts := testutils.NewOpts().SetTimeNow(clock.Now)\n\tctx, cancel := NewContext(testutils.Timeout(100 * time.Millisecond))\n\tdefer cancel()\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tclient := ts.NewClient(opts)\n\n\t\t// Send an 'echo' to establish the connection.\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\t\trequire.NoError(t, testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil))\n\n\t\ttimeAtStart := clock.Now().UnixNano()\n\n\t\tfor i := 0; i < 2; i++ {\n\t\t\trequire.NoError(t, client.Ping(ctx, ts.HostPort()))\n\n\t\t\t// Verify last activity time.\n\t\t\tclientConn := getConnection(t, client, outbound)\n\t\t\tassert.Equal(t, timeAtStart, clientConn.LastActivityRead)\n\t\t\tassert.Equal(t, timeAtStart, clientConn.LastActivityWrite)\n\n\t\t\t// Relays do not pass pings on to the server.\n\t\t\tif ts.HasRelay() {\n\t\t\t\trelayInbound := getConnection(t, ts.Relay(), inbound)\n\t\t\t\tassert.Equal(t, timeAtStart, relayInbound.LastActivityRead)\n\t\t\t\tassert.Equal(t, timeAtStart, relayInbound.LastActivityWrite)\n\t\t\t}\n\n\t\t\tserverConn := getConnection(t, ts.Server(), inbound)\n\t\t\tassert.Equal(t, timeAtStart, serverConn.LastActivityRead)\n\t\t\tassert.Equal(t, timeAtStart, serverConn.LastActivityWrite)\n\n\t\t\tclock.Elapse(1 * time.Second)\n\t\t}\n\t})\n}\n\nfunc TestSendBufferSize(t *testing.T) {\n\topts := testutils.NewOpts().SetSendBufferSize(512).SetSendBufferSizeOverrides([]SendBufferSizeOverride{\n\t\t{\"abc\", 1024},\n\t\t{\"abcd\", 2048}, // This should never match, since we match the list in order.\n\t\t{\"xyz\", 3072},\n\t})\n\ttests := []struct {\n\t\tprocessName          string\n\t\texpectSendChCapacity int\n\t}{\n\t\t{\n\t\t\tprocessName:          \"abc\",\n\t\t\texpectSendChCapacity: 1024,\n\t\t},\n\t\t{\n\t\t\tprocessName:          \"abcd\",\n\t\t\texpectSendChCapacity: 1024,\n\t\t},\n\t\t{\n\t\t\tprocessName:          \"bcd\",\n\t\t\texpectSendChCapacity: DefaultConnectionBufferSize,\n\t\t},\n\t\t{\n\t\t\tprocessName:          \"dabc\",\n\t\t\texpectSendChCapacity: DefaultConnectionBufferSize,\n\t\t},\n\t\t{\n\t\t\tprocessName:          \"dabcd\",\n\t\t\texpectSendChCapacity: DefaultConnectionBufferSize,\n\t\t},\n\t\t{\n\t\t\tprocessName:          \"abcde\",\n\t\t\texpectSendChCapacity: 1024,\n\t\t},\n\t\t{\n\t\t\tprocessName:          \"xyzabc\",\n\t\t\texpectSendChCapacity: 3072,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.processName, func(t *testing.T) {\n\t\t\ttestutils.WithTestServer(t, opts, func(tb testing.TB, ts *testutils.TestServer) {\n\t\t\t\tclient := ts.NewClient(opts.SetProcessName(tt.processName))\n\n\t\t\t\t// Send an 'echo' to establish the connection.\n\t\t\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\t\t\t\trequire.NoError(t, testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil))\n\n\t\t\t\t// WithTestSever will test with and without relay.\n\t\t\t\tif ts.HasRelay() {\n\t\t\t\t\tassert.Equal(t, tt.expectSendChCapacity, getConnection(t, ts.Relay(), inbound).SendChCapacity)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Equal(t, tt.expectSendChCapacity, getConnection(t, ts.Server(), inbound).SendChCapacity)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestInvalidTransportHeaders(t *testing.T) {\n\tlong100 := strings.Repeat(\"0123456789\", 10)\n\tlong300 := strings.Repeat(\"0123456789\", 30)\n\n\ttests := []struct {\n\t\tmsg         string\n\t\tctxFn       func(*ContextBuilder)\n\t\tsvcOverride string\n\t\twantErr     string\n\t}{\n\t\t{\n\t\t\tmsg: \"valid long fields\",\n\t\t\tctxFn: func(cb *ContextBuilder) {\n\t\t\t\tcb.SetRoutingKey(long100)\n\t\t\t\tcb.SetShardKey(long100)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"long routing key\",\n\t\t\tctxFn: func(cb *ContextBuilder) {\n\t\t\t\tcb.SetRoutingKey(long300)\n\t\t\t},\n\t\t\twantErr: \"too long\",\n\t\t},\n\t\t{\n\t\t\tmsg: \"long shard key\",\n\t\t\tctxFn: func(cb *ContextBuilder) {\n\t\t\t\tcb.SetShardKey(long300)\n\t\t\t},\n\t\t\twantErr: \"too long\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\n\t\t\t\tclient := ts.NewClient(nil)\n\n\t\t\t\tcb := NewContextBuilder(time.Second)\n\t\t\t\ttt.ctxFn(cb)\n\t\t\t\tctx, cancel := cb.Build()\n\t\t\t\tdefer cancel()\n\n\t\t\t\tsvc := ts.ServiceName()\n\t\t\t\tif tt.svcOverride != \"\" {\n\t\t\t\t\tsvc = tt.svcOverride\n\t\t\t\t}\n\n\t\t\t\t_, _, _, err := raw.Call(ctx, client, ts.HostPort(), svc, \"echo\", nil, nil)\n\t\t\t\tif tt.wantErr == \"\" {\n\t\t\t\t\trequire.NoError(t, err, \"unexpected error\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\trequire.Error(t, err)\n\t\t\t\tassert.Contains(t, err.Error(), tt.wantErr, \"unexpected error\")\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestCustomDialer(t *testing.T) {\n\tsopts := testutils.NewOpts()\n\ttestutils.WithTestServer(t, sopts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tserver := ts.Server()\n\t\ttestutils.RegisterEcho(server, nil)\n\t\tcustomDialerCalledCount := 0\n\n\t\tcopts := testutils.NewOpts().SetDialer(func(ctx context.Context, network, hostPort string) (net.Conn, error) {\n\t\t\tcustomDialerCalledCount++\n\t\t\td := net.Dialer{}\n\t\t\treturn d.DialContext(ctx, network, hostPort)\n\t\t})\n\n\t\t// Induce the creation of a connection from client to server.\n\t\tclient := ts.NewClient(copts)\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\tassert.Equal(t, 1, customDialerCalledCount, \"custom dialer used for establishing connection\")\n\n\t\t// Re-use\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\tassert.Equal(t, 1, customDialerCalledCount, \"custom dialer used for establishing connection\")\n\t})\n}\n\nfunc TestInboundConnContext(t *testing.T) {\n\topts := testutils.NewOpts().NoRelay().SetConnContext(func(ctx context.Context, conn net.Conn) context.Context {\n\t\treturn context.WithValue(ctx, \"foo\", \"bar\")\n\t})\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\talice := ts.Server()\n\t\ttestutils.RegisterFunc(alice, \"echo\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\t// Verify that the context passed into the handler inherits from the base context\n\t\t\t// set by ConnContext\n\t\t\tassert.Equal(t, \"bar\", ctx.Value(\"foo\"), \"Value unexpectedly different from base context\")\n\t\t\treturn &raw.Res{Arg2: args.Arg2, Arg3: args.Arg3}, nil\n\t\t})\n\n\t\tcopts := testutils.NewOpts()\n\t\tbob := ts.NewClient(copts)\n\t\ttestutils.AssertEcho(t, bob, ts.HostPort(), ts.ServiceName())\n\t})\n}\n\nfunc TestOutboundConnContext(t *testing.T) {\n\topts := testutils.NewOpts().NoRelay()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\talice := ts.Server()\n\t\ttestutils.RegisterFunc(alice, \"echo\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\tassert.Equal(t, \"bar\", ctx.Value(\"foo\"), \"Base context key unexpectedly absent\")\n\t\t\treturn &raw.Res{Arg2: args.Arg2, Arg3: args.Arg3}, nil\n\t\t})\n\n\t\tbobOpts := testutils.NewOpts().SetServiceName(\"bob\")\n\t\tbob := ts.NewServer(bobOpts)\n\t\ttestutils.RegisterEcho(bob, nil)\n\n\t\tbaseCtx := context.WithValue(context.Background(), \"foo\", \"bar\")\n\t\tctx, cancel := NewContextBuilder(time.Second).SetConnectBaseContext(baseCtx).Build()\n\t\tdefer cancel()\n\t\terr := alice.Ping(ctx, bob.PeerInfo().HostPort)\n\t\trequire.NoError(t, err)\n\n\t\ttestutils.AssertEcho(t, bob, ts.HostPort(), ts.ServiceName())\n\t})\n}\n\nfunc TestWithTLSNoRelay(t *testing.T) {\n\t// NOTE: \"Connection does not implement SyscallConn.\" logs are filtered as tls.Conn doesn't implement syscall.Conn.\n\n\tsopts := testutils.NewOpts().SetServeTLS(true).NoRelay().\n\t\tAddLogFilter(\"Connection does not implement SyscallConn.\", 1)\n\n\ttestutils.WithTestServer(t, sopts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tserver := ts.Server()\n\t\ttestutils.RegisterEcho(server, nil)\n\t\tcustomDialerCalledCount := 0\n\n\t\tcopts := testutils.NewOpts().SetDialer(func(ctx context.Context, network, hostPort string) (net.Conn, error) {\n\t\t\tcustomDialerCalledCount++\n\t\t\td := tls.Dialer{\n\t\t\t\tConfig: &tls.Config{InsecureSkipVerify: true},\n\t\t\t}\n\t\t\treturn d.DialContext(ctx, network, hostPort)\n\t\t}).AddLogFilter(\"Connection does not implement SyscallConn.\", 1)\n\n\t\t// Induce the creation of a connection from client to server.\n\t\tclient := ts.NewClient(copts)\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\tassert.Equal(t, 1, customDialerCalledCount, \"custom dialer used for establishing connection\")\n\n\t\t// Re-use\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\tassert.Equal(t, 1, customDialerCalledCount, \"custom dialer used for establishing connection\")\n\t})\n}\n\nfunc TestWithTLSRelayOnly(t *testing.T) {\n\t// NOTE: \"Connection does not implement SyscallConn.\" logs are filtered as tls.Conn doesn't implement syscall.Conn.\n\n\t// SetDialer with tls.Dial as relay uses dialer from server opts to make outbound connections.\n\tsopts := testutils.NewOpts().SetServeTLS(true).SetRelayOnly().SetDialer(func(ctx context.Context, network, hostPort string) (net.Conn, error) {\n\t\td := tls.Dialer{\n\t\t\tConfig: &tls.Config{InsecureSkipVerify: true},\n\t\t}\n\t\treturn d.DialContext(ctx, network, hostPort)\n\t}).AddLogFilter(\"Connection does not implement SyscallConn.\", 2) // 1 + 1 for server & relay\n\n\ttestutils.WithTestServer(t, sopts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tserver := ts.Server()\n\t\ttestutils.RegisterEcho(server, nil)\n\t\tcustomDialerCalledCount := 0\n\n\t\tcopts := testutils.NewOpts().SetDialer(func(ctx context.Context, network, hostPort string) (net.Conn, error) {\n\t\t\tcustomDialerCalledCount++\n\t\t\td := tls.Dialer{\n\t\t\t\tConfig: &tls.Config{InsecureSkipVerify: true},\n\t\t\t}\n\t\t\treturn d.DialContext(ctx, network, hostPort)\n\t\t}).AddLogFilter(\"Connection does not implement SyscallConn.\", 1)\n\n\t\t// Induce the creation of a connection from client to server.\n\t\tclient := ts.NewClient(copts)\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\tassert.Equal(t, 1, customDialerCalledCount, \"custom dialer used for establishing connection\")\n\n\t\t// Re-use\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\tassert.Equal(t, 1, customDialerCalledCount, \"custom dialer used for establishing connection\")\n\t})\n}\n"
  },
  {
    "path": "connectionstate_string.go",
    "content": "// Code generated by \"stringer -type=connectionState\"; DO NOT EDIT\n\npackage tchannel\n\nimport \"fmt\"\n\nconst _connectionState_name = \"connectionActiveconnectionStartCloseconnectionInboundClosedconnectionClosed\"\n\nvar _connectionState_index = [...]uint8{0, 16, 36, 59, 75}\n\nfunc (i connectionState) String() string {\n\ti -= 1\n\tif i < 0 || i >= connectionState(len(_connectionState_index)-1) {\n\t\treturn fmt.Sprintf(\"connectionState(%d)\", i+1)\n\t}\n\treturn _connectionState_name[_connectionState_index[i]:_connectionState_index[i+1]]\n}\n"
  },
  {
    "path": "context.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"time\"\n\n\t\"golang.org/x/net/context\"\n)\n\nconst defaultTimeout = time.Second\n\ntype contextKey int\n\nconst (\n\tcontextKeyTChannel contextKey = iota\n\tcontextKeyHeaders\n)\n\ntype tchannelCtxParams struct {\n\ttracingDisabled         bool\n\thideListeningOnOutbound bool\n\tcall                    IncomingCall\n\toptions                 *CallOptions\n\tretryOptions            *RetryOptions\n\tconnectTimeout          time.Duration\n\tconnectBaseContext      context.Context\n}\n\n// IncomingCall exposes properties for incoming calls through the context.\ntype IncomingCall interface {\n\t// CallerName returns the caller name from the CallerName transport header.\n\tCallerName() string\n\n\t// ShardKey returns the shard key from the ShardKey transport header.\n\tShardKey() string\n\n\t// RoutingKey returns the routing key (referring to a traffic group) from\n\t// RoutingKey transport header.\n\tRoutingKey() string\n\n\t// RoutingDelegate returns the routing delegate from RoutingDelegate\n\t// transport header.\n\tRoutingDelegate() string\n\n\t// LocalPeer returns the local peer information.\n\tLocalPeer() LocalPeerInfo\n\n\t// RemotePeer returns the caller's peer information.\n\t// If the caller is an ephemeral peer, then the HostPort cannot be used to make new\n\t// connections to the caller.\n\tRemotePeer() PeerInfo\n\n\t// CallOptions returns the call options set for the incoming call. It can be\n\t// useful for forwarding requests.\n\tCallOptions() *CallOptions\n}\n\nfunc getTChannelParams(ctx context.Context) *tchannelCtxParams {\n\tif params, ok := ctx.Value(contextKeyTChannel).(*tchannelCtxParams); ok {\n\t\treturn params\n\t}\n\treturn nil\n}\n\n// NewContext returns a new root context used to make TChannel requests.\nfunc NewContext(timeout time.Duration) (context.Context, context.CancelFunc) {\n\treturn NewContextBuilder(timeout).Build()\n}\n\n// WrapContextForTest returns a copy of the given Context that is associated with the call.\n// This should be used in units test only.\n// NOTE: This method is deprecated. Callers should use NewContextBuilder().SetIncomingCallForTest.\nfunc WrapContextForTest(ctx context.Context, call IncomingCall) context.Context {\n\tgetTChannelParams(ctx).call = call\n\treturn ctx\n}\n\n// newIncomingContext creates a new context for an incoming call with the given span.\nfunc newIncomingContext(ctx context.Context, call IncomingCall, timeout time.Duration) (context.Context, context.CancelFunc) {\n\treturn NewContextBuilder(timeout).\n\t\tSetParentContext(ctx).\n\t\tsetIncomingCall(call).\n\t\tBuild()\n}\n\n// CurrentCall returns the current incoming call, or nil if this is not an incoming call context.\nfunc CurrentCall(ctx context.Context) IncomingCall {\n\tif params := getTChannelParams(ctx); params != nil {\n\t\treturn params.call\n\t}\n\treturn nil\n}\n\nfunc currentCallOptions(ctx context.Context) *CallOptions {\n\tif params := getTChannelParams(ctx); params != nil {\n\t\treturn params.options\n\t}\n\treturn nil\n}\n\nfunc isTracingDisabled(ctx context.Context) bool {\n\tif params := getTChannelParams(ctx); params != nil {\n\t\treturn params.tracingDisabled\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "context_builder.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"time\"\n\n\t\"golang.org/x/net/context\"\n)\n\n// ContextBuilder stores all TChannel-specific parameters that will\n// be stored inside of a context.\ntype ContextBuilder struct {\n\t// TracingDisabled disables trace reporting for calls using this context.\n\tTracingDisabled bool\n\n\t// hideListeningOnOutbound disables sending the listening server's host:port\n\t// when creating new outgoing connections.\n\thideListeningOnOutbound bool\n\n\t// replaceParentHeaders is set to true when SetHeaders() method is called.\n\t// It forces headers from ParentContext to be ignored. When false, parent\n\t// headers will be merged with headers accumulated by the builder.\n\treplaceParentHeaders bool\n\n\t// If Timeout is zero, Build will default to defaultTimeout.\n\tTimeout time.Duration\n\n\t// Headers are application headers that json/thrift will encode into arg2.\n\tHeaders map[string]string\n\n\t// CallOptions are TChannel call options for the specific call.\n\tCallOptions *CallOptions\n\n\t// RetryOptions are the retry options for this call.\n\tRetryOptions *RetryOptions\n\n\t// ConnectTimeout is the timeout for creating a TChannel connection.\n\tConnectTimeout time.Duration\n\n\t// ConnectBaseContext is the base context for all connections\n\tConnectBaseContext context.Context\n\n\t// ParentContext to build the new context from. If empty, context.Background() is used.\n\t// The new (child) context inherits a number of properties from the parent context:\n\t//   - context fields, accessible via `ctx.Value(key)`\n\t//   - headers if parent is a ContextWithHeaders, unless replaced via SetHeaders()\n\tParentContext context.Context\n\n\t// Hidden fields: we do not want users outside of tchannel to set these.\n\tincomingCall IncomingCall\n}\n\n// NewContextBuilder returns a builder that can be used to create a Context.\nfunc NewContextBuilder(timeout time.Duration) *ContextBuilder {\n\treturn &ContextBuilder{\n\t\tTimeout: timeout,\n\t}\n}\n\n// SetTimeout sets the timeout for the Context.\nfunc (cb *ContextBuilder) SetTimeout(timeout time.Duration) *ContextBuilder {\n\tcb.Timeout = timeout\n\treturn cb\n}\n\n// AddHeader adds a single application header to the Context.\nfunc (cb *ContextBuilder) AddHeader(key, value string) *ContextBuilder {\n\tif cb.Headers == nil {\n\t\tcb.Headers = map[string]string{key: value}\n\t} else {\n\t\tcb.Headers[key] = value\n\t}\n\treturn cb\n}\n\n// SetHeaders sets the application headers for this Context.\n// If there is a ParentContext, its headers will be ignored after the call to this method.\nfunc (cb *ContextBuilder) SetHeaders(headers map[string]string) *ContextBuilder {\n\tcb.Headers = headers\n\tcb.replaceParentHeaders = true\n\treturn cb\n}\n\n// SetShardKey sets the ShardKey call option (\"sk\" transport header).\nfunc (cb *ContextBuilder) SetShardKey(sk string) *ContextBuilder {\n\tif cb.CallOptions == nil {\n\t\tcb.CallOptions = new(CallOptions)\n\t}\n\tcb.CallOptions.ShardKey = sk\n\treturn cb\n}\n\n// SetFormat sets the Format call option (\"as\" transport header).\nfunc (cb *ContextBuilder) SetFormat(f Format) *ContextBuilder {\n\tif cb.CallOptions == nil {\n\t\tcb.CallOptions = new(CallOptions)\n\t}\n\tcb.CallOptions.Format = f\n\treturn cb\n}\n\n// SetRoutingKey sets the RoutingKey call options (\"rk\" transport header).\nfunc (cb *ContextBuilder) SetRoutingKey(rk string) *ContextBuilder {\n\tif cb.CallOptions == nil {\n\t\tcb.CallOptions = new(CallOptions)\n\t}\n\tcb.CallOptions.RoutingKey = rk\n\treturn cb\n}\n\n// SetRoutingDelegate sets the RoutingDelegate call options (\"rd\" transport header).\nfunc (cb *ContextBuilder) SetRoutingDelegate(rd string) *ContextBuilder {\n\tif cb.CallOptions == nil {\n\t\tcb.CallOptions = new(CallOptions)\n\t}\n\tcb.CallOptions.RoutingDelegate = rd\n\treturn cb\n}\n\n// SetConnectTimeout sets the ConnectionTimeout for this context.\n// The context timeout applies to the whole call, while the connect\n// timeout only applies to creating a new connection.\nfunc (cb *ContextBuilder) SetConnectTimeout(d time.Duration) *ContextBuilder {\n\tcb.ConnectTimeout = d\n\treturn cb\n}\n\n// SetConnectBaseContext sets the base context for any outbound connection created\nfunc (cb *ContextBuilder) SetConnectBaseContext(ctx context.Context) *ContextBuilder {\n\tcb.ConnectBaseContext = ctx\n\treturn cb\n}\n\n// HideListeningOnOutbound hides the host:port when creating new outbound\n// connections.\nfunc (cb *ContextBuilder) HideListeningOnOutbound() *ContextBuilder {\n\tcb.hideListeningOnOutbound = true\n\treturn cb\n}\n\n// DisableTracing disables tracing.\nfunc (cb *ContextBuilder) DisableTracing() *ContextBuilder {\n\tcb.TracingDisabled = true\n\treturn cb\n}\n\n// SetIncomingCallForTest sets an IncomingCall in the context.\n// This should only be used in unit tests.\nfunc (cb *ContextBuilder) SetIncomingCallForTest(call IncomingCall) *ContextBuilder {\n\treturn cb.setIncomingCall(call)\n}\n\n// SetRetryOptions sets RetryOptions in the context.\nfunc (cb *ContextBuilder) SetRetryOptions(retryOptions *RetryOptions) *ContextBuilder {\n\tcb.RetryOptions = retryOptions\n\treturn cb\n}\n\n// SetTimeoutPerAttempt sets TimeoutPerAttempt in RetryOptions.\nfunc (cb *ContextBuilder) SetTimeoutPerAttempt(timeoutPerAttempt time.Duration) *ContextBuilder {\n\tif cb.RetryOptions == nil {\n\t\tcb.RetryOptions = &RetryOptions{}\n\t}\n\tcb.RetryOptions.TimeoutPerAttempt = timeoutPerAttempt\n\treturn cb\n}\n\n// SetParentContext sets the parent for the Context.\nfunc (cb *ContextBuilder) SetParentContext(ctx context.Context) *ContextBuilder {\n\tcb.ParentContext = ctx\n\treturn cb\n}\n\nfunc (cb *ContextBuilder) setIncomingCall(call IncomingCall) *ContextBuilder {\n\tcb.incomingCall = call\n\treturn cb\n}\n\nfunc (cb *ContextBuilder) getHeaders() map[string]string {\n\tif cb.ParentContext == nil || cb.replaceParentHeaders {\n\t\treturn cb.Headers\n\t}\n\n\tparent, ok := cb.ParentContext.Value(contextKeyHeaders).(*headersContainer)\n\tif !ok || len(parent.reqHeaders) == 0 {\n\t\treturn cb.Headers\n\t}\n\n\tmergedHeaders := make(map[string]string, len(cb.Headers)+len(parent.reqHeaders))\n\tfor k, v := range parent.reqHeaders {\n\t\tmergedHeaders[k] = v\n\t}\n\tfor k, v := range cb.Headers {\n\t\tmergedHeaders[k] = v\n\t}\n\treturn mergedHeaders\n}\n\n// Build returns a ContextWithHeaders that can be used to make calls.\nfunc (cb *ContextBuilder) Build() (ContextWithHeaders, context.CancelFunc) {\n\tparams := &tchannelCtxParams{\n\t\toptions:                 cb.CallOptions,\n\t\tcall:                    cb.incomingCall,\n\t\tretryOptions:            cb.RetryOptions,\n\t\tconnectTimeout:          cb.ConnectTimeout,\n\t\thideListeningOnOutbound: cb.hideListeningOnOutbound,\n\t\ttracingDisabled:         cb.TracingDisabled,\n\t\tconnectBaseContext:      cb.ConnectBaseContext,\n\t}\n\n\tparent := cb.ParentContext\n\tif parent == nil {\n\t\tparent = context.Background()\n\t} else if headerCtx, ok := parent.(headerCtx); ok {\n\t\t// Unwrap any headerCtx, since we'll be rewrapping anyway.\n\t\tparent = headerCtx.Context\n\t}\n\n\tvar (\n\t\tctx    context.Context\n\t\tcancel context.CancelFunc\n\t)\n\t// All contexts created must have a timeout, but if the parent\n\t// already has a timeout, and the user has not specified one, then we\n\t// can use context.WithCancel\n\t_, parentHasDeadline := parent.Deadline()\n\tif cb.Timeout == 0 && parentHasDeadline {\n\t\tctx, cancel = context.WithCancel(parent)\n\t} else {\n\t\tctx, cancel = context.WithTimeout(parent, cb.Timeout)\n\t}\n\n\tctx = context.WithValue(ctx, contextKeyTChannel, params)\n\treturn WrapWithHeaders(ctx, cb.getHeaders()), cancel\n}\n"
  },
  {
    "path": "context_header.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport \"golang.org/x/net/context\"\n\n// ContextWithHeaders is a Context which contains request and response headers.\ntype ContextWithHeaders interface {\n\tcontext.Context\n\n\t// Headers returns the call request headers.\n\tHeaders() map[string]string\n\n\t// ResponseHeaders returns the call response headers.\n\tResponseHeaders() map[string]string\n\n\t// SetResponseHeaders sets the given response headers on the context.\n\tSetResponseHeaders(map[string]string)\n\n\t// Child creates a child context which stores headers separately from\n\t// the parent context.\n\tChild() ContextWithHeaders\n}\n\ntype headerCtx struct {\n\tcontext.Context\n}\n\n// headersContainer stores the headers, and is itself stored in the context under `contextKeyHeaders`\ntype headersContainer struct {\n\treqHeaders  map[string]string\n\trespHeaders map[string]string\n}\n\nfunc (c headerCtx) headers() *headersContainer {\n\tif h, ok := c.Value(contextKeyHeaders).(*headersContainer); ok {\n\t\treturn h\n\t}\n\treturn nil\n}\n\n// Headers gets application headers out of the context.\nfunc (c headerCtx) Headers() map[string]string {\n\tif h := c.headers(); h != nil {\n\t\treturn h.reqHeaders\n\t}\n\treturn nil\n}\n\n// ResponseHeaders returns the response headers.\nfunc (c headerCtx) ResponseHeaders() map[string]string {\n\tif h := c.headers(); h != nil {\n\t\treturn h.respHeaders\n\t}\n\treturn nil\n}\n\n// SetResponseHeaders sets the response headers.\nfunc (c headerCtx) SetResponseHeaders(headers map[string]string) {\n\tif h := c.headers(); h != nil {\n\t\th.respHeaders = headers\n\t\treturn\n\t}\n\tpanic(\"SetResponseHeaders called on ContextWithHeaders not created via WrapWithHeaders\")\n}\n\n// Child creates a child context with a separate container for headers.\nfunc (c headerCtx) Child() ContextWithHeaders {\n\tvar headersCopy headersContainer\n\tif h := c.headers(); h != nil {\n\t\theadersCopy = *h\n\t}\n\n\treturn Wrap(context.WithValue(c.Context, contextKeyHeaders, &headersCopy))\n}\n\n// Wrap wraps an existing context.Context into a ContextWithHeaders.\n// If the underlying context has headers, they are preserved.\nfunc Wrap(ctx context.Context) ContextWithHeaders {\n\thctx := headerCtx{Context: ctx}\n\tif h := hctx.headers(); h != nil {\n\t\treturn hctx\n\t}\n\n\t// If there is no header container, we should create an empty one.\n\treturn WrapWithHeaders(ctx, nil)\n}\n\n// WrapWithHeaders returns a Context that can be used to make a call with request headers.\n// If the parent `ctx` is already an instance of ContextWithHeaders, its existing headers\n// will be ignored. In order to merge new headers with parent headers, use ContextBuilder.\nfunc WrapWithHeaders(ctx context.Context, headers map[string]string) ContextWithHeaders {\n\th := &headersContainer{\n\t\treqHeaders: headers,\n\t}\n\tnewCtx := context.WithValue(ctx, contextKeyHeaders, h)\n\treturn headerCtx{Context: newCtx}\n}\n\n// WithoutHeaders hides any TChannel headers from the given context.\nfunc WithoutHeaders(ctx context.Context) context.Context {\n\treturn context.WithValue(context.WithValue(ctx, contextKeyTChannel, nil), contextKeyHeaders, nil)\n}\n"
  },
  {
    "path": "context_internal_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\npackage tchannel\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/mocktracer\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\nfunc TestNewContextBuilderDisableTracing(t *testing.T) {\n\tctx, cancel := NewContextBuilder(time.Second).\n\t\tDisableTracing().Build()\n\tdefer cancel()\n\n\tassert.True(t, isTracingDisabled(ctx), \"Tracing should be disabled\")\n}\n\nfunc TestCurrentSpan(t *testing.T) {\n\tctx := context.Background()\n\tspan := CurrentSpan(ctx)\n\trequire.NotNil(t, span, \"CurrentSpan() should always return something\")\n\n\ttracer := mocktracer.New()\n\tsp := tracer.StartSpan(\"test\")\n\tctx = opentracing.ContextWithSpan(ctx, sp)\n\tspan = CurrentSpan(ctx)\n\trequire.NotNil(t, span, \"CurrentSpan() should always return something\")\n\tassert.EqualValues(t, 0, span.TraceID(), \"mock tracer is not Zipkin-compatible\")\n\n\ttracer.RegisterInjector(zipkinSpanFormat, new(zipkinInjector))\n\tspan = CurrentSpan(ctx)\n\trequire.NotNil(t, span, \"CurrentSpan() should always return something\")\n\tassert.NotEqual(t, uint64(0), span.TraceID(), \"mock tracer is now Zipkin-compatible\")\n}\n\nfunc TestContextWithoutHeadersKeyHeaders(t *testing.T) {\n\tctx := WrapWithHeaders(context.Background(), map[string]string{\"k1\": \"v1\"})\n\tassert.Equal(t, map[string]string{\"k1\": \"v1\"}, ctx.Headers())\n\tctx2 := WithoutHeaders(ctx)\n\tassert.Nil(t, ctx2.Value(contextKeyHeaders))\n\t_, ok := ctx2.(ContextWithHeaders)\n\tassert.False(t, ok)\n}\n\nfunc TestContextWithoutHeadersKeyTChannel(t *testing.T) {\n\tctx, _ := NewContextBuilder(time.Second).SetShardKey(\"s1\").Build()\n\tctx2 := WithoutHeaders(ctx)\n\tassert.Nil(t, ctx2.Value(contextKeyTChannel))\n\t_, ok := ctx2.(ContextWithHeaders)\n\tassert.False(t, ok)\n}\n"
  },
  {
    "path": "context_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\t\"github.com/uber/tchannel-go/testutils/goroutines\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\nvar cn = \"hello\"\n\nfunc TestWrapContextForTest(t *testing.T) {\n\tcall := testutils.NewIncomingCall(cn)\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\tactual := WrapContextForTest(ctx, call)\n\tassert.Equal(t, call, CurrentCall(actual), \"Incorrect call object returned.\")\n}\n\nfunc TestNewContextTimeoutZero(t *testing.T) {\n\tctx, cancel := NewContextBuilder(0).Build()\n\tdefer cancel()\n\n\tdeadline, ok := ctx.Deadline()\n\tassert.True(t, ok, \"Context missing deadline\")\n\tassert.True(t, deadline.Sub(time.Now()) <= 0, \"Deadline should be Now or earlier\")\n}\n\nfunc TestRoutingDelegatePropagates(t *testing.T) {\n\tWithVerifiedServer(t, nil, func(ch *Channel, hostPort string) {\n\t\tpeerInfo := ch.PeerInfo()\n\t\ttestutils.RegisterFunc(ch, \"test\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\treturn &raw.Res{\n\t\t\t\tArg3: []byte(CurrentCall(ctx).RoutingDelegate()),\n\t\t\t}, nil\n\t\t})\n\n\t\tctx, cancel := NewContextBuilder(time.Second).Build()\n\t\tdefer cancel()\n\t\t_, arg3, _, err := raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, \"test\", nil, nil)\n\t\tassert.NoError(t, err, \"Call failed\")\n\t\tassert.Equal(t, \"\", string(arg3), \"Expected no routing delegate header\")\n\n\t\tctx, cancel = NewContextBuilder(time.Second).SetRoutingDelegate(\"xpr\").Build()\n\t\tdefer cancel()\n\t\t_, arg3, _, err = raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, \"test\", nil, nil)\n\t\tassert.NoError(t, err, \"Call failed\")\n\t\tassert.Equal(t, \"xpr\", string(arg3), \"Expected routing delegate header to be set\")\n\t})\n}\n\nfunc TestRoutingKeyPropagates(t *testing.T) {\n\tWithVerifiedServer(t, nil, func(ch *Channel, hostPort string) {\n\t\tpeerInfo := ch.PeerInfo()\n\t\ttestutils.RegisterFunc(ch, \"test\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\treturn &raw.Res{\n\t\t\t\tArg3: []byte(CurrentCall(ctx).RoutingKey()),\n\t\t\t}, nil\n\t\t})\n\n\t\tctx, cancel := NewContextBuilder(time.Second).Build()\n\t\tdefer cancel()\n\t\t_, arg3, _, err := raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, \"test\", nil, nil)\n\t\tassert.NoError(t, err, \"Call failed\")\n\t\tassert.Equal(t, \"\", string(arg3), \"Expected no routing key header\")\n\n\t\tctx, cancel = NewContextBuilder(time.Second).SetRoutingKey(\"canary\").Build()\n\t\tdefer cancel()\n\t\t_, arg3, _, err = raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, \"test\", nil, nil)\n\t\tassert.NoError(t, err, \"Call failed\")\n\t\tassert.Equal(t, \"canary\", string(arg3), \"Expected routing key header to be set\")\n\t})\n}\n\nfunc TestShardKeyPropagates(t *testing.T) {\n\tWithVerifiedServer(t, nil, func(ch *Channel, hostPort string) {\n\t\tpeerInfo := ch.PeerInfo()\n\t\ttestutils.RegisterFunc(ch, \"test\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\treturn &raw.Res{\n\t\t\t\tArg3: []byte(CurrentCall(ctx).ShardKey()),\n\t\t\t}, nil\n\t\t})\n\n\t\tctx, cancel := NewContextBuilder(time.Second).Build()\n\t\tdefer cancel()\n\t\t_, arg3, _, err := raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, \"test\", nil, nil)\n\t\tassert.NoError(t, err, \"Call failed\")\n\t\tassert.Equal(t, arg3, []byte(\"\"))\n\n\t\tctx, cancel = NewContextBuilder(time.Second).\n\t\t\tSetShardKey(\"shard\").Build()\n\t\tdefer cancel()\n\t\t_, arg3, _, err = raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, \"test\", nil, nil)\n\t\tassert.NoError(t, err, \"Call failed\")\n\t\tassert.Equal(t, string(arg3), \"shard\")\n\t})\n}\n\nfunc TestCurrentCallWithNilResult(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\tcall := CurrentCall(ctx)\n\tassert.Nil(t, call, \"Should return nil.\")\n}\n\nfunc getParentContext(t *testing.T) ContextWithHeaders {\n\tctx := context.WithValue(context.Background(), \"some key\", \"some value\")\n\tassert.Equal(t, \"some value\", ctx.Value(\"some key\"))\n\n\tctx1, _ := NewContextBuilder(time.Second).\n\t\tSetParentContext(ctx).\n\t\tAddHeader(\"header key\", \"header value\").\n\t\tBuild()\n\tassert.Equal(t, \"some value\", ctx1.Value(\"some key\"))\n\treturn ctx1\n}\n\nfunc TestContextBuilderParentContextNoHeaders(t *testing.T) {\n\tctx := getParentContext(t)\n\tassert.Equal(t, map[string]string{\"header key\": \"header value\"}, ctx.Headers())\n\tassert.EqualValues(t, \"some value\", ctx.Value(\"some key\"), \"inherited from parent ctx\")\n}\n\nfunc TestContextBuilderParentContextMergeHeaders(t *testing.T) {\n\tctx := getParentContext(t)\n\tctx.Headers()[\"fixed header\"] = \"fixed value\"\n\n\t// append header to parent\n\tctx2, _ := NewContextBuilder(time.Second).\n\t\tSetParentContext(ctx).\n\t\tAddHeader(\"header key 2\", \"header value 2\").\n\t\tBuild()\n\tassert.Equal(t, map[string]string{\n\t\t\"header key\":   \"header value\",   // inherited\n\t\t\"fixed header\": \"fixed value\",    // inherited\n\t\t\"header key 2\": \"header value 2\", // appended\n\t}, ctx2.Headers())\n\n\t// override parent header\n\tctx3, _ := NewContextBuilder(time.Second).\n\t\tSetParentContext(ctx).\n\t\tAddHeader(\"header key\", \"header value 2\"). // override\n\t\tBuild()\n\n\tassert.Equal(t, map[string]string{\n\t\t\"header key\":   \"header value 2\", // overwritten\n\t\t\"fixed header\": \"fixed value\",    // inherited\n\t}, ctx3.Headers())\n\n\tgoroutines.VerifyNoLeaks(t, nil)\n}\n\nfunc TestContextBuilderParentContextReplaceHeaders(t *testing.T) {\n\tctx := getParentContext(t)\n\tctx.Headers()[\"fixed header\"] = \"fixed value\"\n\tassert.Equal(t, map[string]string{\n\t\t\"header key\":   \"header value\",\n\t\t\"fixed header\": \"fixed value\",\n\t}, ctx.Headers())\n\n\t// replace headers with a new map\n\tctx2, _ := NewContextBuilder(time.Second).\n\t\tSetParentContext(ctx).\n\t\tSetHeaders(map[string]string{\"header key\": \"header value 2\"}).\n\t\tBuild()\n\tassert.Equal(t, map[string]string{\"header key\": \"header value 2\"}, ctx2.Headers())\n\n\tgoroutines.VerifyNoLeaks(t, nil)\n}\n\nfunc TestContextWrapWithHeaders(t *testing.T) {\n\theaders1 := map[string]string{\n\t\t\"k1\": \"v1\",\n\t}\n\tctx, _ := NewContextBuilder(time.Second).\n\t\tSetHeaders(headers1).\n\t\tBuild()\n\tassert.Equal(t, headers1, ctx.Headers(), \"Headers mismatch after Build\")\n\n\theaders2 := map[string]string{\n\t\t\"k1\": \"v1\",\n\t}\n\tctx2 := WrapWithHeaders(ctx, headers2)\n\tassert.Equal(t, headers2, ctx2.Headers(), \"Headers mismatch after WrapWithHeaders\")\n}\n\nfunc TestContextWithHeadersAsContext(t *testing.T) {\n\tvar ctx context.Context = getParentContext(t)\n\tassert.EqualValues(t, \"some value\", ctx.Value(\"some key\"), \"inherited from parent ctx\")\n}\n\nfunc TestContextBuilderParentContextSpan(t *testing.T) {\n\tctx := getParentContext(t)\n\tassert.Equal(t, \"some value\", ctx.Value(\"some key\"))\n\n\tctx2, _ := NewContextBuilder(time.Second).\n\t\tSetParentContext(ctx).\n\t\tBuild()\n\tassert.Equal(t, \"some value\", ctx2.Value(\"some key\"), \"key/value propagated from parent ctx\")\n\n\tgoroutines.VerifyNoLeaks(t, nil)\n}\n\nfunc TestContextWrapChild(t *testing.T) {\n\ttests := []struct {\n\t\tmsg         string\n\t\tctxFn       func() ContextWithHeaders\n\t\twantHeaders map[string]string\n\t\twantValue   interface{}\n\t}{\n\t\t{\n\t\t\tmsg: \"Basic context\",\n\t\t\tctxFn: func() ContextWithHeaders {\n\t\t\t\tctxNoHeaders, _ := NewContextBuilder(time.Second).Build()\n\t\t\t\treturn ctxNoHeaders\n\t\t\t},\n\t\t\twantHeaders: nil,\n\t\t\twantValue:   nil,\n\t\t},\n\t\t{\n\t\t\tmsg: \"Wrap basic context with value\",\n\t\t\tctxFn: func() ContextWithHeaders {\n\t\t\t\tctxNoHeaders, _ := NewContextBuilder(time.Second).Build()\n\t\t\t\treturn Wrap(context.WithValue(ctxNoHeaders, \"1\", \"2\"))\n\t\t\t},\n\t\t\twantHeaders: nil,\n\t\t\twantValue:   \"2\",\n\t\t},\n\t\t{\n\t\t\tmsg: \"Wrap context with headers and value\",\n\t\t\tctxFn: func() ContextWithHeaders {\n\t\t\t\tctxWithHeaders, _ := NewContextBuilder(time.Second).AddHeader(\"h1\", \"v1\").Build()\n\t\t\t\treturn Wrap(context.WithValue(ctxWithHeaders, \"1\", \"2\"))\n\t\t\t},\n\t\t\twantHeaders: map[string]string{\"h1\": \"v1\"},\n\t\t\twantValue:   \"2\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tfor _, child := range []bool{false, true} {\n\t\t\torigCtx := tt.ctxFn()\n\t\t\tctx := origCtx\n\t\t\tif child {\n\t\t\t\tctx = origCtx.Child()\n\t\t\t}\n\n\t\t\tassert.Equal(t, tt.wantValue, ctx.Value(\"1\"), \"%v: Unexpected value\", tt.msg)\n\t\t\tassert.Equal(t, tt.wantHeaders, ctx.Headers(), \"%v: Unexpected headers\", tt.msg)\n\n\t\t\trespHeaders := map[string]string{\"r\": \"v\"}\n\t\t\tctx.SetResponseHeaders(respHeaders)\n\t\t\tassert.Equal(t, respHeaders, ctx.ResponseHeaders(), \"%v: Unexpected response headers\", tt.msg)\n\n\t\t\tif child {\n\t\t\t\t// If we're working with a child context, changes to response headers\n\t\t\t\t// should not affect the original context.\n\t\t\t\tassert.Nil(t, origCtx.ResponseHeaders(), \"%v: Child modified original context's headers\", tt.msg)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestContextInheritParentTimeout(t *testing.T) {\n\tdeadlineAfter := time.Now().Add(time.Hour)\n\tpctx, cancel := context.WithTimeout(context.Background(), time.Hour)\n\tdefer cancel()\n\n\tctxBuilder := &ContextBuilder{\n\t\tParentContext: pctx,\n\t}\n\tctx, cancel := ctxBuilder.Build()\n\tdefer cancel()\n\n\t// Ensure deadline is in the future\n\tdeadline, ok := ctx.Deadline()\n\trequire.True(t, ok, \"Missing deadline\")\n\n\tassert.False(t, deadline.Before(deadlineAfter),\n\t\t\"Expected deadline to be after %v, got %v\", deadlineAfter, deadline)\n}\n"
  },
  {
    "path": "deps_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Our glide.yaml lists a set of directories in excludeDirs to avoid packages\n// only used in testing from pulling in dependencies that should not affect\n// package resolution for clients.\n// However, we really want these directories to be part of test imports. Since\n// glide does not provide a \"testDirs\" option, we add dependencies required\n// for tests in this _test.go file.\n\npackage tchannel_test\n\nimport (\n\t\"testing\"\n\n\tjcg \"github.com/uber/jaeger-client-go\"\n)\n\nfunc TestJaegerDeps(t *testing.T) {\n\tm := jcg.Metrics{}\n\t_ = m.SamplerUpdateFailure\n}\n"
  },
  {
    "path": "dial_16.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build !go1.7\n// +build !go1.7\n\npackage tchannel\n\nimport (\n\t\"net\"\n\n\t\"golang.org/x/net/context\"\n)\n\nfunc dialContext(ctx context.Context, hostPort string) (net.Conn, error) {\n\ttimeout := getTimeout(ctx)\n\treturn net.DialTimeout(\"tcp\", hostPort, timeout)\n}\n"
  },
  {
    "path": "dial_17.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build go1.7\n// +build go1.7\n\npackage tchannel\n\nimport (\n\t\"context\"\n\t\"net\"\n)\n\nfunc dialContext(ctx context.Context, hostPort string) (net.Conn, error) {\n\td := net.Dialer{}\n\treturn d.DialContext(ctx, \"tcp\", hostPort)\n}\n"
  },
  {
    "path": "dial_17_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build go1.7\n// +build go1.7\n\npackage tchannel_test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/uber/tchannel-go/testutils\"\n)\n\nfunc TestNetDialCancelContext(t *testing.T) {\n\t// timeoutHostPort uses a blackholed address (RFC 6890) with a port\n\t// reserved for documentation. This address should always cause a timeout.\n\tconst timeoutHostPort = \"192.18.0.254:44444\"\n\ttimeoutPeriod := testutils.Timeout(50 * time.Millisecond)\n\n\tclient := testutils.NewClient(t, nil)\n\tdefer client.Close()\n\n\tstarted := time.Now()\n\tctx, cancel := NewContext(time.Minute)\n\n\tgo func() {\n\t\ttime.Sleep(timeoutPeriod)\n\t\tcancel()\n\t}()\n\n\terr := client.Ping(ctx, timeoutHostPort)\n\tif !assert.Error(t, err, \"Ping to blackhole address should fail\") {\n\t\treturn\n\t}\n\n\tif strings.Contains(err.Error(), \"network is unreachable\") {\n\t\tt.Skipf(\"Skipping test, as network interface may not be available\")\n\t}\n\n\td := time.Since(started)\n\tassert.Equal(t, ErrCodeCancelled, GetSystemErrorCode(err), \"Ping expected to fail with context cancelled\")\n\tassert.True(t, d < 2*timeoutPeriod, \"Timeout should take less than %v, took %v\", 2*timeoutPeriod, d)\n}\n"
  },
  {
    "path": "doc.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n/*\nPackage tchannel implements Go bindings for the TChannel protocol (https://github.com/uber/tchannel).\n\nA single Channel can be used for many concurrent requests to many hosts.\n*/\npackage tchannel\n"
  },
  {
    "path": "errors.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\n\t\"golang.org/x/net/context\"\n)\n\nconst (\n\t// Message id for protocol level errors\n\tinvalidMessageID uint32 = 0xFFFFFFFF\n)\n\n// A SystemErrCode indicates how a caller should handle a system error returned from a peer\ntype SystemErrCode byte\n\n//go:generate stringer -type=SystemErrCode\n\nconst (\n\t// ErrCodeInvalid is an invalid error code, and should not be used\n\tErrCodeInvalid SystemErrCode = 0x00\n\n\t// ErrCodeTimeout indicates the peer timed out.  Callers can retry the request\n\t// on another peer if the request is safe to retry.\n\tErrCodeTimeout SystemErrCode = 0x01\n\n\t// ErrCodeCancelled indicates that the request was cancelled on the peer.  Callers\n\t// can retry the request on the same or another peer if the request is safe to retry\n\tErrCodeCancelled SystemErrCode = 0x02\n\n\t// ErrCodeBusy indicates that the request was not dispatched because the peer\n\t// was too busy to handle it.  Callers can retry the request on another peer, and should\n\t// reweight their connections to direct less traffic to this peer until it recovers.\n\tErrCodeBusy SystemErrCode = 0x03\n\n\t// ErrCodeDeclined indicates that the request not dispatched because the peer\n\t// declined to handle it, typically because the peer is not yet ready to handle it.\n\t// Callers can retry the request on another peer, but should not reweight their connections\n\t// and should continue to send traffic to this peer.\n\tErrCodeDeclined SystemErrCode = 0x04\n\n\t// ErrCodeUnexpected indicates that the request failed for an unexpected reason, typically\n\t// a crash or other unexpected handling.  The request may have been processed before the failure;\n\t// callers should retry the request on this or another peer only if the request is safe to retry\n\tErrCodeUnexpected SystemErrCode = 0x05\n\n\t// ErrCodeBadRequest indicates that the request was malformed, and could not be processed.\n\t// Callers should not bother to retry the request, as there is no chance it will be handled.\n\tErrCodeBadRequest SystemErrCode = 0x06\n\n\t// ErrCodeNetwork indicates a network level error, such as a connection reset.\n\t// Callers can retry the request if the request is safe to retry\n\tErrCodeNetwork SystemErrCode = 0x07\n\n\t// ErrCodeProtocol indincates a fatal protocol error communicating with the peer.  The connection\n\t// will be terminated.\n\tErrCodeProtocol SystemErrCode = 0xFF\n)\n\nvar (\n\t// ErrServerBusy is a SystemError indicating the server is busy\n\tErrServerBusy = NewSystemError(ErrCodeBusy, \"server busy\")\n\n\t// ErrRequestCancelled is a SystemError indicating the request has been cancelled on the peer\n\tErrRequestCancelled = NewSystemError(ErrCodeCancelled, \"request cancelled\")\n\n\t// ErrTimeout is a SytemError indicating the request has timed out\n\tErrTimeout = NewSystemError(ErrCodeTimeout, \"timeout\")\n\n\t// ErrTimeoutRequired is a SystemError indicating that timeouts must be specified.\n\tErrTimeoutRequired = NewSystemError(ErrCodeBadRequest, \"timeout required\")\n\n\t// ErrChannelClosed is a SystemError indicating that the channel has been closed.\n\tErrChannelClosed = NewSystemError(ErrCodeDeclined, \"closed channel\")\n\n\t// ErrMethodTooLarge is a SystemError indicating that the method is too large.\n\tErrMethodTooLarge = NewSystemError(ErrCodeProtocol, \"method too large\")\n)\n\n// MetricsKey is a string representation of the error code that's suitable for\n// inclusion in metrics tags.\nfunc (c SystemErrCode) MetricsKey() string {\n\tswitch c {\n\tcase ErrCodeInvalid:\n\t\t// Shouldn't ever need this.\n\t\treturn \"invalid\"\n\tcase ErrCodeTimeout:\n\t\treturn \"timeout\"\n\tcase ErrCodeCancelled:\n\t\treturn \"cancelled\"\n\tcase ErrCodeBusy:\n\t\treturn \"busy\"\n\tcase ErrCodeDeclined:\n\t\treturn \"declined\"\n\tcase ErrCodeUnexpected:\n\t\treturn \"unexpected-error\"\n\tcase ErrCodeBadRequest:\n\t\treturn \"bad-request\"\n\tcase ErrCodeNetwork:\n\t\treturn \"network-error\"\n\tcase ErrCodeProtocol:\n\t\treturn \"protocol-error\"\n\tdefault:\n\t\treturn c.String()\n\t}\n}\n\nfunc (c SystemErrCode) relayMetricsKey() string {\n\tswitch c {\n\tcase ErrCodeInvalid:\n\t\treturn \"relay-invalid\"\n\tcase ErrCodeTimeout:\n\t\treturn \"relay-timeout\"\n\tcase ErrCodeCancelled:\n\t\treturn \"relay-cancelled\"\n\tcase ErrCodeBusy:\n\t\treturn \"relay-busy\"\n\tcase ErrCodeDeclined:\n\t\treturn \"relay-declined\"\n\tcase ErrCodeUnexpected:\n\t\treturn \"relay-unexpected-error\"\n\tcase ErrCodeBadRequest:\n\t\treturn \"relay-bad-request\"\n\tcase ErrCodeNetwork:\n\t\treturn \"relay-network-error\"\n\tcase ErrCodeProtocol:\n\t\treturn \"relay-protocol-error\"\n\tdefault:\n\t\treturn \"relay-\" + c.String()\n\t}\n}\n\n// A SystemError is a system-level error, containing an error code and message\n// TODO(mmihic): Probably we want to hide this interface, and let application code\n// just deal with standard raw errors.\ntype SystemError struct {\n\tcode    SystemErrCode\n\tmsg     string\n\twrapped error\n}\n\n// NewSystemError defines a new SystemError with a code and message\nfunc NewSystemError(code SystemErrCode, msg string, args ...interface{}) error {\n\treturn SystemError{code: code, msg: fmt.Sprintf(msg, args...)}\n}\n\n// NewWrappedSystemError defines a new SystemError wrapping an existing error\nfunc NewWrappedSystemError(code SystemErrCode, wrapped error) error {\n\tif se, ok := wrapped.(SystemError); ok {\n\t\treturn se\n\t}\n\n\treturn SystemError{code: code, msg: fmt.Sprint(wrapped), wrapped: wrapped}\n}\n\n// Error returns the code and message, conforming to the error interface\nfunc (se SystemError) Error() string {\n\treturn fmt.Sprintf(\"tchannel error %v: %s\", se.Code(), se.msg)\n}\n\n// Wrapped returns the wrapped error\nfunc (se SystemError) Wrapped() error { return se.wrapped }\n\n// Code returns the SystemError code, for sending to a peer\nfunc (se SystemError) Code() SystemErrCode {\n\treturn se.code\n}\n\n// Message returns the SystemError message.\nfunc (se SystemError) Message() string {\n\treturn se.msg\n}\n\n// GetContextError converts the context error to a tchannel error.\nfunc GetContextError(err error) error {\n\tif err == context.DeadlineExceeded {\n\t\treturn ErrTimeout\n\t}\n\tif err == context.Canceled {\n\t\treturn ErrRequestCancelled\n\t}\n\treturn err\n}\n\n// GetSystemErrorCode returns the code to report for the given error.  If the error is a\n// SystemError, we can get the code directly.  Otherwise treat it as an unexpected error\nfunc GetSystemErrorCode(err error) SystemErrCode {\n\tif err == nil {\n\t\treturn ErrCodeInvalid\n\t}\n\n\tif se, ok := err.(SystemError); ok {\n\t\treturn se.Code()\n\t}\n\n\treturn ErrCodeUnexpected\n}\n\n// GetSystemErrorMessage returns the message to report for the given error.  If the error is a\n// SystemError, we can get the underlying message. Otherwise, use the Error() method.\nfunc GetSystemErrorMessage(err error) string {\n\tif se, ok := err.(SystemError); ok {\n\t\treturn se.Message()\n\t}\n\n\treturn err.Error()\n}\n\ntype errConnNotActive struct {\n\tinfo  string\n\tstate connectionState\n}\n\nfunc (e errConnNotActive) Error() string {\n\treturn fmt.Sprintf(\"%v connection is not active: %v\", e.info, e.state)\n}\n"
  },
  {
    "path": "errors_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"io\"\n\t\"regexp\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestErrorMetricKeys(t *testing.T) {\n\tcodes := []SystemErrCode{\n\t\tErrCodeInvalid,\n\t\tErrCodeTimeout,\n\t\tErrCodeCancelled,\n\t\tErrCodeBusy,\n\t\tErrCodeDeclined,\n\t\tErrCodeUnexpected,\n\t\tErrCodeBadRequest,\n\t\tErrCodeNetwork,\n\t\tErrCodeProtocol,\n\t}\n\n\t// Metrics keys should be all lowercase letters and dashes. No spaces,\n\t// underscores, or other characters.\n\texpected := regexp.MustCompile(`^[[:lower:]-]+$`)\n\tfor _, c := range codes {\n\t\tassert.True(t, expected.MatchString(c.MetricsKey()), \"Expected metrics key for code %s to be well-formed.\", c.String())\n\t}\n\n\t// Unexpected codes may have poorly-formed keys.\n\tassert.Equal(t, \"SystemErrCode(13)\", SystemErrCode(13).MetricsKey(), \"Expected invalid error codes to use a fallback metrics key format.\")\n}\n\nfunc TestInvalidError(t *testing.T) {\n\tcode := GetSystemErrorCode(nil)\n\tassert.Equal(t, ErrCodeInvalid, code, \"nil error should produce ErrCodeInvalid\")\n}\n\nfunc TestUnexpectedError(t *testing.T) {\n\tcode := GetSystemErrorCode(io.EOF)\n\tassert.Equal(t, ErrCodeUnexpected, code, \"non-tchannel SystemError should produce ErrCodeUnexpected\")\n}\n\nfunc TestSystemError(t *testing.T) {\n\tcode := GetSystemErrorCode(ErrTimeout)\n\tassert.Equal(t, ErrCodeTimeout, code, \"tchannel timeout error produces ErrCodeTimeout\")\n}\n\nfunc TestRelayMetricsKey(t *testing.T) {\n\tfor i := 0; i <= 256; i++ {\n\t\tcode := SystemErrCode(i)\n\t\tassert.Equal(t, \"relay-\"+code.MetricsKey(), code.relayMetricsKey(), \"Unexpected relay metrics key for %v\", code)\n\t}\n}\n"
  },
  {
    "path": "examples/bench/client/client.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"log\"\n\t\"net/http\"\n\t_ \"net/http/pprof\"\n\t\"runtime\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\nvar (\n\thostPort      = flag.String(\"hostPort\", \"localhost:12345\", \"listening socket of the bench server\")\n\tnumGoroutines = flag.Int(\"numGo\", 1, \"The number of goroutines to spawn\")\n\tnumOSThreads  = flag.Int(\"numThreads\", 1, \"The number of OS threads to use (sets GOMAXPROCS)\")\n\tsetBlockSize  = flag.Int(\"setBlockSize\", 4096, \"The size in bytes of the data being set\")\n\tgetToSetRatio = flag.Int(\"getToSetRatio\", 1, \"The number of Gets to do per Set call\")\n\n\t// counter tracks the total number of requests completed in the past second.\n\tcounter atomic.Int64\n)\n\nfunc main() {\n\tflag.Parse()\n\truntime.GOMAXPROCS(*numOSThreads)\n\n\t// Sets up a listener for pprof.\n\tgo func() {\n\t\tlog.Println(http.ListenAndServe(\"localhost:6061\", nil))\n\t}()\n\n\tch, err := tchannel.NewChannel(\"benchmark-client\", nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"NewChannel failed: %v\", err)\n\t}\n\tfor i := 0; i < *numGoroutines; i++ {\n\t\tgo worker(ch)\n\t}\n\n\tlog.Printf(\"client config: %v workers on %v threads, setBlockSize %v, getToSetRatio %v\",\n\t\t*numGoroutines, *numOSThreads, *setBlockSize, *getToSetRatio)\n\trequestCountReporter()\n}\n\nfunc requestCountReporter() {\n\tfor {\n\t\ttime.Sleep(time.Second)\n\t\tcur := counter.Swap(0)\n\t\tlog.Printf(\"%v requests\", cur)\n\t}\n}\n\nfunc worker(ch *tchannel.Channel) {\n\tdata := make([]byte, *setBlockSize)\n\tfor {\n\t\tif err := setRequest(ch, \"key\", string(data)); err != nil {\n\t\t\tlog.Fatalf(\"set failed: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tcounter.Inc()\n\n\t\tfor i := 0; i < *getToSetRatio; i++ {\n\t\t\t_, err := getRequest(ch, \"key\")\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatalf(\"get failed: %v\", err)\n\t\t\t}\n\t\t\tcounter.Inc()\n\t\t}\n\t}\n}\n\nfunc setRequest(ch *tchannel.Channel, key, value string) error {\n\tctx, _ := context.WithTimeout(context.Background(), time.Second*10)\n\t_, _, _, err := raw.Call(ctx, ch, *hostPort, \"benchmark\", \"set\", []byte(key), []byte(value))\n\treturn err\n}\n\nfunc getRequest(ch *tchannel.Channel, key string) (string, error) {\n\tctx, _ := context.WithTimeout(context.Background(), time.Second)\n\t_, arg3, _, err := raw.Call(ctx, ch, *hostPort, \"benchmark\", \"get\", []byte(key), nil)\n\treturn string(arg3), err\n}\n"
  },
  {
    "path": "examples/bench/runner.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/exec\"\n\t\"time\"\n)\n\nvar (\n\tflagHostPort         = flag.String(\"hostPort\", \"127.0.0.1:12345\", \"The host:port to run the benchmark on\")\n\tflagServerNumThreads = flag.Int(\"serverThreads\", 1, \"The number of OS threads to use for the server\")\n\n\tflagServerBinary = flag.String(\"serverBinary\", \"./build/examples/bench/server\", \"Server binary location\")\n\tflagClientBinary = flag.String(\"clientBinary\", \"./build/examples/bench/client\", \"Client binary location\")\n\n\tflagProfileAfter = flag.Duration(\"profileAfter\", 0,\n\t\t\"Duration to wait before profiling. 0 disables profiling. Process is stopped after the profile.\")\n\tflagProfileSeconds = flag.Int(\"profileSeconds\", 30, \"The number of seconds to profile\")\n\tflagProfileStop    = flag.Bool(\"profileStopProcess\", true, \"Whether to stop the benchmarks after profiling\")\n)\n\nfunc main() {\n\tflag.Parse()\n\n\tserver, err := runServer(*flagHostPort, *flagServerNumThreads)\n\tif err != nil {\n\t\tlog.Fatalf(\"Server failed: %v\", err)\n\t}\n\tdefer server.Process.Kill()\n\n\tclient, err := runClient(flag.Args())\n\tif err != nil {\n\t\tlog.Fatalf(\"Client failed: %v\", err)\n\t}\n\tdefer client.Process.Kill()\n\n\tif *flagProfileAfter != 0 {\n\t\tgo func() {\n\t\t\ttime.Sleep(*flagProfileAfter)\n\n\t\t\t// Profile the server and the client, which have pprof endpoints at :6060, and :6061\n\t\t\tp1, err := dumpProfile(\"localhost:6060\", \"server.pb.gz\")\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"Server profile failed: %v\", err)\n\t\t\t}\n\t\t\tp2, err := dumpProfile(\"localhost:6061\", \"client.pb.gz\")\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"Client profile failed: %v\", err)\n\t\t\t}\n\t\t\tif err := p1.Wait(); err != nil {\n\t\t\t\tlog.Printf(\"Server profile error: %v\", err)\n\t\t\t}\n\t\t\tif err := p2.Wait(); err != nil {\n\t\t\t\tlog.Printf(\"Client profile error: %v\", err)\n\t\t\t}\n\n\t\t\tif !*flagProfileStop {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tserver.Process.Signal(os.Interrupt)\n\t\t\tclient.Process.Signal(os.Interrupt)\n\n\t\t\t// After a while, kill the processes if we're still running.\n\t\t\ttime.Sleep(300 * time.Millisecond)\n\t\t\tlog.Printf(\"Still waiting for processes to stop, sending kill\")\n\t\t\tserver.Process.Kill()\n\t\t\tclient.Process.Kill()\n\t\t}()\n\t}\n\n\tserver.Wait()\n\tclient.Wait()\n}\n\nfunc runServer(hostPort string, numThreads int) (*exec.Cmd, error) {\n\thost, port, err := net.SplitHostPort(hostPort)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn runCmd(*flagServerBinary,\n\t\t\"--host\", host,\n\t\t\"--port\", port,\n\t\t\"--numThreads\", fmt.Sprint(numThreads))\n}\n\nfunc runClient(clientArgs []string) (*exec.Cmd, error) {\n\treturn runCmd(*flagClientBinary, clientArgs...)\n}\n\nfunc dumpProfile(baseHostPort, profileFile string) (*exec.Cmd, error) {\n\tprofileURL := fmt.Sprintf(\"http://%v/debug/pprof/profile\", baseHostPort)\n\treturn runCmd(\"go\", \"tool\", \"pprof\", \"--proto\", \"--output=\"+profileFile,\n\t\tfmt.Sprintf(\"--seconds=%v\", *flagProfileSeconds), profileURL)\n}\n\nfunc runCmd(cmdBinary string, args ...string) (*exec.Cmd, error) {\n\tcmd := exec.Command(cmdBinary, args...)\n\tcmd.Stdin = os.Stdin\n\tcmd.Stdout = os.Stdout\n\tcmd.Stderr = os.Stderr\n\treturn cmd, cmd.Start()\n}\n"
  },
  {
    "path": "examples/bench/server/server.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t_ \"net/http/pprof\"\n\t\"runtime\"\n\t\"sync\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\n\t\"golang.org/x/net/context\"\n)\n\nvar (\n\tflagHost      = flag.String(\"host\", \"localhost\", \"The hostname to listen on\")\n\tflagPort      = flag.Int(\"port\", 12345, \"The base port to listen on\")\n\tflagInstances = flag.Int(\"instances\", 1, \"The number of instances to start\")\n\tflagOSThreads = flag.Int(\"numThreads\", 1, \"The number of OS threads to use (sets GOMAXPROCS)\")\n)\n\nfunc main() {\n\tflag.Parse()\n\truntime.GOMAXPROCS(*flagOSThreads)\n\n\t// Sets up a listener for pprof.\n\tgo func() {\n\t\tlog.Printf(\"server pprof endpoint failed: %v\", http.ListenAndServe(\"localhost:6060\", nil))\n\t}()\n\n\tfor i := 0; i < *flagInstances; i++ {\n\t\tif err := setupServer(*flagHost, *flagPort, i); err != nil {\n\t\t\tlog.Fatalf(\"setupServer %v failed: %v\", i, err)\n\t\t}\n\t}\n\n\tlog.Printf(\"server config: %v threads listening on %v:%v\", *flagOSThreads, *flagHost, *flagPort)\n\n\t// Listen indefinitely.\n\tselect {}\n}\n\nfunc setupServer(host string, basePort, instanceNum int) error {\n\thostPort := fmt.Sprintf(\"%s:%v\", host, basePort+instanceNum)\n\tch, err := tchannel.NewChannel(\"benchmark\", &tchannel.ChannelOptions{\n\t\tProcessName: fmt.Sprintf(\"benchmark-%v\", instanceNum),\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"NewChannel failed: %v\", err)\n\t}\n\n\thandler := raw.Wrap(&kvHandler{vals: make(map[string]string)})\n\tch.Register(handler, \"ping\")\n\tch.Register(handler, \"get\")\n\tch.Register(handler, \"set\")\n\n\tif err := ch.ListenAndServe(hostPort); err != nil {\n\t\treturn fmt.Errorf(\"ListenAndServe failed: %v\", err)\n\t}\n\n\treturn nil\n}\n\ntype kvHandler struct {\n\tsync.RWMutex\n\tvals map[string]string\n}\n\nfunc (h *kvHandler) WithLock(write bool, f func()) {\n\tif write {\n\t\th.Lock()\n\t} else {\n\t\th.RLock()\n\t}\n\n\tf()\n\n\tif write {\n\t\th.Unlock()\n\t} else {\n\t\th.RUnlock()\n\t}\n}\n\nfunc (h *kvHandler) Ping(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\treturn &raw.Res{\n\t\tArg2: []byte(\"pong\"),\n\t}, nil\n}\n\nfunc (h *kvHandler) Get(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\tvar arg3 []byte\n\th.WithLock(false /* write */, func() {\n\t\targ3 = []byte(h.vals[string(args.Arg2)])\n\t})\n\n\treturn &raw.Res{\n\t\tArg2: []byte(fmt.Sprint(len(arg3))),\n\t\tArg3: arg3,\n\t}, nil\n}\n\nfunc (h *kvHandler) Set(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\th.WithLock(true /* write */, func() {\n\t\th.vals[string(args.Arg2)] = string(args.Arg3)\n\t})\n\treturn &raw.Res{\n\t\tArg2: []byte(\"ok\"),\n\t\tArg3: []byte(\"really ok\"),\n\t}, nil\n}\n\nfunc (h *kvHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\tswitch args.Method {\n\tcase \"ping\":\n\t\treturn h.Ping(ctx, args)\n\tcase \"get\":\n\t\treturn h.Get(ctx, args)\n\tcase \"put\":\n\t\treturn h.Set(ctx, args)\n\tdefault:\n\t\treturn nil, errors.New(\"unknown method\")\n\t}\n}\n\nfunc (h *kvHandler) OnError(ctx context.Context, err error) {\n\tlog.Fatalf(\"OnError %v\", err)\n}\n"
  },
  {
    "path": "examples/hyperbahn/echo-server/main.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/hyperbahn\"\n\t\"github.com/uber/tchannel-go/raw\"\n\n\t\"golang.org/x/net/context\"\n)\n\nfunc main() {\n\ttchan, err := tchannel.NewChannel(\"go-echo-server\", nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to create channel: %v\", err)\n\t}\n\n\tlistenIP, err := tchannel.ListenIP()\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to get IP to listen on: %v\", err)\n\t}\n\n\tl, err := net.Listen(\"tcp\", listenIP.String()+\":61543\")\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not listen: %v\", err)\n\t}\n\tlog.Printf(\"Listening on %v\", l.Addr())\n\n\tsc := tchan.GetSubChannel(\"go-echo-2\")\n\ttchan.Register(raw.Wrap(handler{\"\"}), \"echo\")\n\tsc.Register(raw.Wrap(handler{\"subchannel:\"}), \"echo\")\n\ttchan.Serve(l)\n\n\tif len(os.Args[1:]) == 0 {\n\t\tlog.Fatalf(\"You must provide Hyperbahn nodes as arguments\")\n\t}\n\n\t// advertise service with Hyperbahn.\n\tconfig := hyperbahn.Configuration{InitialNodes: os.Args[1:]}\n\tclient, err := hyperbahn.NewClient(tchan, config, &hyperbahn.ClientOptions{\n\t\tHandler: eventHandler{},\n\t\tTimeout: time.Second,\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"hyperbahn.NewClient failed: %v\", err)\n\t}\n\tif err := client.Advertise(sc); err != nil {\n\t\tlog.Fatalf(\"Advertise failed: %v\", err)\n\t}\n\n\t// Server will keep running till Ctrl-C.\n\tselect {}\n}\n\ntype eventHandler struct{}\n\nfunc (eventHandler) On(event hyperbahn.Event) {\n\tfmt.Printf(\"On(%v)\\n\", event)\n}\n\nfunc (eventHandler) OnError(err error) {\n\tfmt.Printf(\"OnError(%v)\\n\", err)\n}\n\ntype handler struct {\n\tprefix string\n}\n\nfunc (h handler) OnError(ctx context.Context, err error) {\n\tlog.Fatalf(\"OnError: %v\", err)\n}\n\nfunc (h handler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\targ2 := h.prefix + string(args.Arg2)\n\targ3 := h.prefix + string(args.Arg3)\n\n\treturn &raw.Res{\n\t\tArg2: []byte(arg2),\n\t\tArg3: []byte(arg3),\n\t}, nil\n}\n"
  },
  {
    "path": "examples/hypercat/main.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/exec\"\n\n\t\"github.com/jessevdk/go-flags\"\n\t\"github.com/uber/tchannel-go\"\n\t\"golang.org/x/net/context\"\n)\n\nvar options = struct {\n\tServiceName string `short:\"s\" long:\"service\" required:\"true\" description:\"The TChannel/Hyperbahn service name\"`\n\n\t// MethodName can be specified multiple times to listen on multiple methods.\n\tMethodName []string `short:\"o\" long:\"method\" required:\"true\" description:\"The method name to handle\"`\n\n\t// HostPort can just be :port or port, in which case host defaults to tchannel's ListenIP.\n\tHostPort string `short:\"l\" long:\"hostPort\" default:\":0\" description:\"The port or host:port to listen on\"`\n\n\tMaxConcurrency int `short:\"m\" long:\"maxSpawn\" default:\"1\" description:\"The maximum number concurrent processes\"`\n\n\tCmd struct {\n\t\tCommand string   `long:\"command\" description:\"The command to execute\" positional-arg-name:\"command\"`\n\t\tArgs    []string `long:\"args\" description:\"The arguments to pass to the command\" positional-arg-name:\"args\"`\n\t} `positional-args:\"yes\" required:\"yes\"`\n}{}\n\nvar running chan struct{}\n\nfunc parseArgs() {\n\tvar err error\n\tif _, err = flags.Parse(&options); err != nil {\n\t\tos.Exit(-1)\n\t}\n\n\t// Convert host port to a real host port.\n\thost, port, err := net.SplitHostPort(options.HostPort)\n\tif err != nil {\n\t\tport = options.HostPort\n\t}\n\tif host == \"\" {\n\t\thostIP, err := tchannel.ListenIP()\n\t\tif err != nil {\n\t\t\tlog.Printf(\"could not get ListenIP: %v, defaulting to 127.0.0.1\", err)\n\t\t\thost = \"127.0.0.1\"\n\t\t} else {\n\t\t\thost = hostIP.String()\n\t\t}\n\t}\n\toptions.HostPort = host + \":\" + port\n\n\trunning = make(chan struct{}, options.MaxConcurrency)\n}\n\nfunc main() {\n\tparseArgs()\n\n\tch, err := tchannel.NewChannel(options.ServiceName, nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"NewChannel failed: %v\", err)\n\t}\n\n\tfor _, op := range options.MethodName {\n\t\tch.Register(tchannel.HandlerFunc(handler), op)\n\t}\n\n\tif err := ch.ListenAndServe(options.HostPort); err != nil {\n\t\tlog.Fatalf(\"ListenAndServe failed: %v\", err)\n\t}\n\n\tpeerInfo := ch.PeerInfo()\n\tlog.Printf(\"listening for %v:%v on %v\", peerInfo.ServiceName, options.MethodName, peerInfo.HostPort)\n\tselect {}\n}\n\nfunc onError(msg string, args ...interface{}) {\n\tlog.Fatalf(msg, args...)\n}\n\nfunc handler(ctx context.Context, call *tchannel.InboundCall) {\n\trunning <- struct{}{}\n\tdefer func() { <-running }()\n\n\tvar arg2 []byte\n\tif err := tchannel.NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil {\n\t\tlog.Fatalf(\"Arg2Reader failed: %v\", err)\n\t}\n\n\targ3Reader, err := call.Arg3Reader()\n\tif err != nil {\n\t\tlog.Fatalf(\"Arg3Reader failed: %v\", err)\n\t}\n\n\tresponse := call.Response()\n\tif err := tchannel.NewArgWriter(response.Arg2Writer()).Write(nil); err != nil {\n\t\tlog.Fatalf(\"Arg2Writer failed: %v\", err)\n\t}\n\n\targ3Writer, err := response.Arg3Writer()\n\tif err != nil {\n\t\tlog.Fatalf(\"Arg3Writer failed: %v\", err)\n\t}\n\n\tif err := spawnProcess(arg3Reader, arg3Writer); err != nil {\n\t\tlog.Fatalf(\"spawnProcess failed: %v\", err)\n\t}\n\n\tif err := arg3Reader.Close(); err != nil {\n\t\tlog.Fatalf(\"Arg3Reader.Close failed: %v\", err)\n\t}\n\tif err := arg3Writer.Close(); err != nil {\n\t\tlog.Fatalf(\"Arg3Writer.Close failed: %v\", err)\n\t}\n}\n\nfunc spawnProcess(reader io.Reader, writer io.Writer) error {\n\tcmd := exec.Command(options.Cmd.Command, options.Cmd.Args...)\n\tcmd.Stdin = reader\n\tcmd.Stdout = writer\n\tcmd.Stderr = os.Stderr\n\treturn cmd.Run()\n}\n"
  },
  {
    "path": "examples/keyvalue/README.md",
    "content": "# Key-Value Store\n\n```bash\n./build/examples/keyvalue/server\n./build/examples/keyvalue/client\n```\n\nThis example exposes a simple key-value store over TChannel using the Thrift\nprotocol.  The client has an interactive CLI that can be used to make calls to\nthe server.\n"
  },
  {
    "path": "examples/keyvalue/client/client.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue\"\n\t\"github.com/uber/tchannel-go/hyperbahn\"\n\t\"github.com/uber/tchannel-go/thrift\"\n)\n\nvar curUser = \"anonymous\"\n\nfunc printHelp() {\n\tfmt.Println(\"Usage:\\n get [key]\\n set [key] [value]\")\n\tfmt.Println(\" user [newUser]\\n clearAll\")\n}\n\nfunc main() {\n\t// Create a TChannel.\n\tch, err := tchannel.NewChannel(\"keyvalue-client\", nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to create tchannel: %v\", err)\n\t}\n\n\t// Set up Hyperbahn client.\n\tconfig := hyperbahn.Configuration{InitialNodes: os.Args[1:]}\n\tif len(config.InitialNodes) == 0 {\n\t\tlog.Fatalf(\"No Autobahn nodes to connect to given\")\n\t}\n\thyperbahn.NewClient(ch, config, nil)\n\n\tthriftClient := thrift.NewClient(ch, \"keyvalue\", nil)\n\tclient := keyvalue.NewTChanKeyValueClient(thriftClient)\n\tadminClient := keyvalue.NewTChanAdminClient(thriftClient)\n\n\t// Read commands from the command line and execute them.\n\tscanner := bufio.NewScanner(os.Stdin)\n\tprintHelp()\n\tfmt.Printf(\"> \")\n\tfor scanner.Scan() {\n\t\tparts := strings.Split(scanner.Text(), \" \")\n\t\tif parts[0] == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tswitch parts[0] {\n\t\tcase \"help\":\n\t\t\tprintHelp()\n\t\tcase \"get\":\n\t\t\tif len(parts) < 2 {\n\t\t\t\tprintHelp()\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tget(client, parts[1])\n\t\tcase \"set\":\n\t\t\tif len(parts) < 3 {\n\t\t\t\tprintHelp()\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tset(client, parts[1], parts[2])\n\t\tcase \"user\":\n\t\t\tif len(parts) < 2 {\n\t\t\t\tprintHelp()\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcurUser = parts[1]\n\t\tcase \"clearAll\":\n\t\t\tclear(adminClient)\n\t\tdefault:\n\t\t\tlog.Printf(\"Unsupported command %q\\n\", parts[0])\n\t\t}\n\t\tfmt.Print(\"> \")\n\t}\n\tscanner.Text()\n}\n\nfunc get(client keyvalue.TChanKeyValue, key string) {\n\tctx, cancel := createContext()\n\tdefer cancel()\n\n\tval, err := client.Get(ctx, key)\n\tif err != nil {\n\t\tswitch err := err.(type) {\n\t\tcase *keyvalue.InvalidKey:\n\t\t\tlog.Printf(\"Get %v failed: invalid key\", key)\n\t\tcase *keyvalue.KeyNotFound:\n\t\t\tlog.Printf(\"Get %v failed: key not found\", key)\n\t\tdefault:\n\t\t\tlog.Printf(\"Get %v failed unexpectedly: %v\", key, err)\n\t\t}\n\t\treturn\n\t}\n\n\tlog.Printf(\"Get %v: %v\", key, val)\n}\n\nfunc set(client keyvalue.TChanKeyValue, key, value string) {\n\tctx, cancel := createContext()\n\tdefer cancel()\n\n\tif err := client.Set(ctx, key, value); err != nil {\n\t\tswitch err := err.(type) {\n\t\tcase *keyvalue.InvalidKey:\n\t\t\tlog.Printf(\"Set %v failed: invalid key\", key)\n\t\tdefault:\n\t\t\tlog.Printf(\"Set %v:%v failed unexpectedly: %#v\", key, value, err)\n\t\t}\n\t\treturn\n\t}\n\n\tlog.Printf(\"Set %v:%v succeeded with headers: %v\", key, value, ctx.ResponseHeaders())\n}\n\nfunc clear(adminClient keyvalue.TChanAdmin) {\n\tctx, cancel := createContext()\n\tdefer cancel()\n\n\tif err := adminClient.ClearAll(ctx); err != nil {\n\t\tswitch err := err.(type) {\n\t\tcase *keyvalue.NotAuthorized:\n\t\t\tlog.Printf(\"You are not authorized to perform this method\")\n\t\tdefault:\n\t\t\tlog.Printf(\"ClearAll failed unexpectedly: %v\", err)\n\t\t}\n\t\treturn\n\t}\n\n\tlog.Printf(\"ClearAll completed, all keys cleared\")\n}\n\nfunc createContext() (thrift.Context, func()) {\n\tctx, cancel := thrift.NewContext(time.Second)\n\tctx = thrift.WithHeaders(ctx, map[string]string{\"user\": curUser})\n\treturn ctx, cancel\n}\n"
  },
  {
    "path": "examples/keyvalue/gen-go/keyvalue/admin.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage keyvalue\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\ntype Admin interface {\n\tBaseService\n\n\tClearAll() (err error)\n}\n\ntype AdminClient struct {\n\t*BaseServiceClient\n}\n\nfunc NewAdminClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AdminClient {\n\treturn &AdminClient{BaseServiceClient: NewBaseServiceClientFactory(t, f)}\n}\n\nfunc NewAdminClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AdminClient {\n\treturn &AdminClient{BaseServiceClient: NewBaseServiceClientProtocol(t, iprot, oprot)}\n}\n\nfunc (p *AdminClient) ClearAll() (err error) {\n\tif err = p.sendClearAll(); err != nil {\n\t\treturn\n\t}\n\treturn p.recvClearAll()\n}\n\nfunc (p *AdminClient) sendClearAll() (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"clearAll\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := AdminClearAllArgs{}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *AdminClient) recvClearAll() (err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"clearAll\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"clearAll failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"clearAll failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror12 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error13 error\n\t\terror13, err = error12.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error13\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"clearAll failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := AdminClearAllResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tif result.NotAuthorized != nil {\n\t\terr = result.NotAuthorized\n\t\treturn\n\t}\n\treturn\n}\n\ntype AdminProcessor struct {\n\t*BaseServiceProcessor\n}\n\nfunc NewAdminProcessor(handler Admin) *AdminProcessor {\n\tself14 := &AdminProcessor{NewBaseServiceProcessor(handler)}\n\tself14.AddToProcessorMap(\"clearAll\", &adminProcessorClearAll{handler: handler})\n\treturn self14\n}\n\ntype adminProcessorClearAll struct {\n\thandler Admin\n}\n\nfunc (p *adminProcessorClearAll) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := AdminClearAllArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"clearAll\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := AdminClearAllResult{}\n\tvar err2 error\n\tif err2 = p.handler.ClearAll(); err2 != nil {\n\t\tswitch v := err2.(type) {\n\t\tcase *NotAuthorized:\n\t\t\tresult.NotAuthorized = v\n\t\tdefault:\n\t\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing clearAll: \"+err2.Error())\n\t\t\toprot.WriteMessageBegin(\"clearAll\", thrift.EXCEPTION, seqId)\n\t\t\tx.Write(oprot)\n\t\t\toprot.WriteMessageEnd()\n\t\t\toprot.Flush()\n\t\t\treturn true, err2\n\t\t}\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"clearAll\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\n// HELPER FUNCTIONS AND STRUCTURES\n\ntype AdminClearAllArgs struct {\n}\n\nfunc NewAdminClearAllArgs() *AdminClearAllArgs {\n\treturn &AdminClearAllArgs{}\n}\n\nfunc (p *AdminClearAllArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *AdminClearAllArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"clearAll_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *AdminClearAllArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"AdminClearAllArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - NotAuthorized\ntype AdminClearAllResult struct {\n\tNotAuthorized *NotAuthorized `thrift:\"notAuthorized,1\" db:\"notAuthorized\" json:\"notAuthorized,omitempty\"`\n}\n\nfunc NewAdminClearAllResult() *AdminClearAllResult {\n\treturn &AdminClearAllResult{}\n}\n\nvar AdminClearAllResult_NotAuthorized_DEFAULT *NotAuthorized\n\nfunc (p *AdminClearAllResult) GetNotAuthorized() *NotAuthorized {\n\tif !p.IsSetNotAuthorized() {\n\t\treturn AdminClearAllResult_NotAuthorized_DEFAULT\n\t}\n\treturn p.NotAuthorized\n}\nfunc (p *AdminClearAllResult) IsSetNotAuthorized() bool {\n\treturn p.NotAuthorized != nil\n}\n\nfunc (p *AdminClearAllResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *AdminClearAllResult) ReadField1(iprot thrift.TProtocol) error {\n\tp.NotAuthorized = &NotAuthorized{}\n\tif err := p.NotAuthorized.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.NotAuthorized), err)\n\t}\n\treturn nil\n}\n\nfunc (p *AdminClearAllResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"clearAll_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *AdminClearAllResult) writeField1(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetNotAuthorized() {\n\t\tif err := oprot.WriteFieldBegin(\"notAuthorized\", thrift.STRUCT, 1); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:notAuthorized: \", p), err)\n\t\t}\n\t\tif err := p.NotAuthorized.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.NotAuthorized), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:notAuthorized: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *AdminClearAllResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"AdminClearAllResult(%+v)\", *p)\n}\n"
  },
  {
    "path": "examples/keyvalue/gen-go/keyvalue/baseservice.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage keyvalue\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\ntype BaseService interface {\n\tHealthCheck() (r string, err error)\n}\n\ntype BaseServiceClient struct {\n\tTransport       thrift.TTransport\n\tProtocolFactory thrift.TProtocolFactory\n\tInputProtocol   thrift.TProtocol\n\tOutputProtocol  thrift.TProtocol\n\tSeqId           int32\n}\n\nfunc NewBaseServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *BaseServiceClient {\n\treturn &BaseServiceClient{Transport: t,\n\t\tProtocolFactory: f,\n\t\tInputProtocol:   f.GetProtocol(t),\n\t\tOutputProtocol:  f.GetProtocol(t),\n\t\tSeqId:           0,\n\t}\n}\n\nfunc NewBaseServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *BaseServiceClient {\n\treturn &BaseServiceClient{Transport: t,\n\t\tProtocolFactory: nil,\n\t\tInputProtocol:   iprot,\n\t\tOutputProtocol:  oprot,\n\t\tSeqId:           0,\n\t}\n}\n\nfunc (p *BaseServiceClient) HealthCheck() (r string, err error) {\n\tif err = p.sendHealthCheck(); err != nil {\n\t\treturn\n\t}\n\treturn p.recvHealthCheck()\n}\n\nfunc (p *BaseServiceClient) sendHealthCheck() (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"HealthCheck\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := BaseServiceHealthCheckArgs{}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *BaseServiceClient) recvHealthCheck() (value string, err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"HealthCheck\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"HealthCheck failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"HealthCheck failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error1 error\n\t\terror1, err = error0.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error1\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"HealthCheck failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := BaseServiceHealthCheckResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tvalue = result.GetSuccess()\n\treturn\n}\n\ntype BaseServiceProcessor struct {\n\tprocessorMap map[string]thrift.TProcessorFunction\n\thandler      BaseService\n}\n\nfunc (p *BaseServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {\n\tp.processorMap[key] = processor\n}\n\nfunc (p *BaseServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {\n\tprocessor, ok = p.processorMap[key]\n\treturn processor, ok\n}\n\nfunc (p *BaseServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction {\n\treturn p.processorMap\n}\n\nfunc NewBaseServiceProcessor(handler BaseService) *BaseServiceProcessor {\n\n\tself2 := &BaseServiceProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)}\n\tself2.processorMap[\"HealthCheck\"] = &baseServiceProcessorHealthCheck{handler: handler}\n\treturn self2\n}\n\nfunc (p *BaseServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\tname, _, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif processor, ok := p.GetProcessorFunction(name); ok {\n\t\treturn processor.Process(seqId, iprot, oprot)\n\t}\n\tiprot.Skip(thrift.STRUCT)\n\tiprot.ReadMessageEnd()\n\tx3 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function \"+name)\n\toprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)\n\tx3.Write(oprot)\n\toprot.WriteMessageEnd()\n\toprot.Flush()\n\treturn false, x3\n\n}\n\ntype baseServiceProcessorHealthCheck struct {\n\thandler BaseService\n}\n\nfunc (p *baseServiceProcessorHealthCheck) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := BaseServiceHealthCheckArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"HealthCheck\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := BaseServiceHealthCheckResult{}\n\tvar retval string\n\tvar err2 error\n\tif retval, err2 = p.handler.HealthCheck(); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing HealthCheck: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"HealthCheck\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t} else {\n\t\tresult.Success = &retval\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"HealthCheck\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\n// HELPER FUNCTIONS AND STRUCTURES\n\ntype BaseServiceHealthCheckArgs struct {\n}\n\nfunc NewBaseServiceHealthCheckArgs() *BaseServiceHealthCheckArgs {\n\treturn &BaseServiceHealthCheckArgs{}\n}\n\nfunc (p *BaseServiceHealthCheckArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *BaseServiceHealthCheckArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"HealthCheck_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *BaseServiceHealthCheckArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"BaseServiceHealthCheckArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Success\ntype BaseServiceHealthCheckResult struct {\n\tSuccess *string `thrift:\"success,0\" db:\"success\" json:\"success,omitempty\"`\n}\n\nfunc NewBaseServiceHealthCheckResult() *BaseServiceHealthCheckResult {\n\treturn &BaseServiceHealthCheckResult{}\n}\n\nvar BaseServiceHealthCheckResult_Success_DEFAULT string\n\nfunc (p *BaseServiceHealthCheckResult) GetSuccess() string {\n\tif !p.IsSetSuccess() {\n\t\treturn BaseServiceHealthCheckResult_Success_DEFAULT\n\t}\n\treturn *p.Success\n}\nfunc (p *BaseServiceHealthCheckResult) IsSetSuccess() bool {\n\treturn p.Success != nil\n}\n\nfunc (p *BaseServiceHealthCheckResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 0:\n\t\t\tif err := p.ReadField0(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *BaseServiceHealthCheckResult) ReadField0(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 0: \", err)\n\t} else {\n\t\tp.Success = &v\n\t}\n\treturn nil\n}\n\nfunc (p *BaseServiceHealthCheckResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"HealthCheck_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField0(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *BaseServiceHealthCheckResult) writeField0(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSuccess() {\n\t\tif err := oprot.WriteFieldBegin(\"success\", thrift.STRING, 0); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 0:success: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteString(string(*p.Success)); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.success (0) field write error: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 0:success: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *BaseServiceHealthCheckResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"BaseServiceHealthCheckResult(%+v)\", *p)\n}\n"
  },
  {
    "path": "examples/keyvalue/gen-go/keyvalue/constants.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage keyvalue\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\nfunc init() {\n}\n"
  },
  {
    "path": "examples/keyvalue/gen-go/keyvalue/keyvalue.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage keyvalue\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\ntype KeyValue interface {\n\tBaseService\n\n\t// Parameters:\n\t//  - Key\n\tGet(key string) (r string, err error)\n\t// Parameters:\n\t//  - Key\n\t//  - Value\n\tSet(key string, value string) (err error)\n}\n\ntype KeyValueClient struct {\n\t*BaseServiceClient\n}\n\nfunc NewKeyValueClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *KeyValueClient {\n\treturn &KeyValueClient{BaseServiceClient: NewBaseServiceClientFactory(t, f)}\n}\n\nfunc NewKeyValueClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *KeyValueClient {\n\treturn &KeyValueClient{BaseServiceClient: NewBaseServiceClientProtocol(t, iprot, oprot)}\n}\n\n// Parameters:\n//   - Key\nfunc (p *KeyValueClient) Get(key string) (r string, err error) {\n\tif err = p.sendGet(key); err != nil {\n\t\treturn\n\t}\n\treturn p.recvGet()\n}\n\nfunc (p *KeyValueClient) sendGet(key string) (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"Get\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := KeyValueGetArgs{\n\t\tKey: key,\n\t}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *KeyValueClient) recvGet() (value string, err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"Get\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"Get failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"Get failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror4 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error5 error\n\t\terror5, err = error4.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error5\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"Get failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := KeyValueGetResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tif result.NotFound != nil {\n\t\terr = result.NotFound\n\t\treturn\n\t} else if result.InvalidKey != nil {\n\t\terr = result.InvalidKey\n\t\treturn\n\t}\n\tvalue = result.GetSuccess()\n\treturn\n}\n\n// Parameters:\n//   - Key\n//   - Value\nfunc (p *KeyValueClient) Set(key string, value string) (err error) {\n\tif err = p.sendSet(key, value); err != nil {\n\t\treturn\n\t}\n\treturn p.recvSet()\n}\n\nfunc (p *KeyValueClient) sendSet(key string, value string) (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"Set\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := KeyValueSetArgs{\n\t\tKey:   key,\n\t\tValue: value,\n\t}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *KeyValueClient) recvSet() (err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"Set\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"Set failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"Set failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror6 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error7 error\n\t\terror7, err = error6.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error7\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"Set failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := KeyValueSetResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tif result.InvalidKey != nil {\n\t\terr = result.InvalidKey\n\t\treturn\n\t}\n\treturn\n}\n\ntype KeyValueProcessor struct {\n\t*BaseServiceProcessor\n}\n\nfunc NewKeyValueProcessor(handler KeyValue) *KeyValueProcessor {\n\tself8 := &KeyValueProcessor{NewBaseServiceProcessor(handler)}\n\tself8.AddToProcessorMap(\"Get\", &keyValueProcessorGet{handler: handler})\n\tself8.AddToProcessorMap(\"Set\", &keyValueProcessorSet{handler: handler})\n\treturn self8\n}\n\ntype keyValueProcessorGet struct {\n\thandler KeyValue\n}\n\nfunc (p *keyValueProcessorGet) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := KeyValueGetArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"Get\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := KeyValueGetResult{}\n\tvar retval string\n\tvar err2 error\n\tif retval, err2 = p.handler.Get(args.Key); err2 != nil {\n\t\tswitch v := err2.(type) {\n\t\tcase *KeyNotFound:\n\t\t\tresult.NotFound = v\n\t\tcase *InvalidKey:\n\t\t\tresult.InvalidKey = v\n\t\tdefault:\n\t\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing Get: \"+err2.Error())\n\t\t\toprot.WriteMessageBegin(\"Get\", thrift.EXCEPTION, seqId)\n\t\t\tx.Write(oprot)\n\t\t\toprot.WriteMessageEnd()\n\t\t\toprot.Flush()\n\t\t\treturn true, err2\n\t\t}\n\t} else {\n\t\tresult.Success = &retval\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"Get\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\ntype keyValueProcessorSet struct {\n\thandler KeyValue\n}\n\nfunc (p *keyValueProcessorSet) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := KeyValueSetArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"Set\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := KeyValueSetResult{}\n\tvar err2 error\n\tif err2 = p.handler.Set(args.Key, args.Value); err2 != nil {\n\t\tswitch v := err2.(type) {\n\t\tcase *InvalidKey:\n\t\t\tresult.InvalidKey = v\n\t\tdefault:\n\t\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing Set: \"+err2.Error())\n\t\t\toprot.WriteMessageBegin(\"Set\", thrift.EXCEPTION, seqId)\n\t\t\tx.Write(oprot)\n\t\t\toprot.WriteMessageEnd()\n\t\t\toprot.Flush()\n\t\t\treturn true, err2\n\t\t}\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"Set\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\n// HELPER FUNCTIONS AND STRUCTURES\n\n// Attributes:\n//   - Key\ntype KeyValueGetArgs struct {\n\tKey string `thrift:\"key,1\" db:\"key\" json:\"key\"`\n}\n\nfunc NewKeyValueGetArgs() *KeyValueGetArgs {\n\treturn &KeyValueGetArgs{}\n}\n\nfunc (p *KeyValueGetArgs) GetKey() string {\n\treturn p.Key\n}\nfunc (p *KeyValueGetArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueGetArgs) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Key = v\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueGetArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Get_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueGetArgs) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"key\", thrift.STRING, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:key: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Key)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.key (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:key: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *KeyValueGetArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"KeyValueGetArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Success\n//   - NotFound\n//   - InvalidKey\ntype KeyValueGetResult struct {\n\tSuccess    *string      `thrift:\"success,0\" db:\"success\" json:\"success,omitempty\"`\n\tNotFound   *KeyNotFound `thrift:\"notFound,1\" db:\"notFound\" json:\"notFound,omitempty\"`\n\tInvalidKey *InvalidKey  `thrift:\"invalidKey,2\" db:\"invalidKey\" json:\"invalidKey,omitempty\"`\n}\n\nfunc NewKeyValueGetResult() *KeyValueGetResult {\n\treturn &KeyValueGetResult{}\n}\n\nvar KeyValueGetResult_Success_DEFAULT string\n\nfunc (p *KeyValueGetResult) GetSuccess() string {\n\tif !p.IsSetSuccess() {\n\t\treturn KeyValueGetResult_Success_DEFAULT\n\t}\n\treturn *p.Success\n}\n\nvar KeyValueGetResult_NotFound_DEFAULT *KeyNotFound\n\nfunc (p *KeyValueGetResult) GetNotFound() *KeyNotFound {\n\tif !p.IsSetNotFound() {\n\t\treturn KeyValueGetResult_NotFound_DEFAULT\n\t}\n\treturn p.NotFound\n}\n\nvar KeyValueGetResult_InvalidKey_DEFAULT *InvalidKey\n\nfunc (p *KeyValueGetResult) GetInvalidKey() *InvalidKey {\n\tif !p.IsSetInvalidKey() {\n\t\treturn KeyValueGetResult_InvalidKey_DEFAULT\n\t}\n\treturn p.InvalidKey\n}\nfunc (p *KeyValueGetResult) IsSetSuccess() bool {\n\treturn p.Success != nil\n}\n\nfunc (p *KeyValueGetResult) IsSetNotFound() bool {\n\treturn p.NotFound != nil\n}\n\nfunc (p *KeyValueGetResult) IsSetInvalidKey() bool {\n\treturn p.InvalidKey != nil\n}\n\nfunc (p *KeyValueGetResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 0:\n\t\t\tif err := p.ReadField0(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueGetResult) ReadField0(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 0: \", err)\n\t} else {\n\t\tp.Success = &v\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueGetResult) ReadField1(iprot thrift.TProtocol) error {\n\tp.NotFound = &KeyNotFound{}\n\tif err := p.NotFound.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.NotFound), err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueGetResult) ReadField2(iprot thrift.TProtocol) error {\n\tp.InvalidKey = &InvalidKey{}\n\tif err := p.InvalidKey.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.InvalidKey), err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueGetResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Get_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField0(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueGetResult) writeField0(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSuccess() {\n\t\tif err := oprot.WriteFieldBegin(\"success\", thrift.STRING, 0); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 0:success: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteString(string(*p.Success)); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.success (0) field write error: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 0:success: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *KeyValueGetResult) writeField1(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetNotFound() {\n\t\tif err := oprot.WriteFieldBegin(\"notFound\", thrift.STRUCT, 1); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:notFound: \", p), err)\n\t\t}\n\t\tif err := p.NotFound.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.NotFound), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:notFound: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *KeyValueGetResult) writeField2(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetInvalidKey() {\n\t\tif err := oprot.WriteFieldBegin(\"invalidKey\", thrift.STRUCT, 2); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:invalidKey: \", p), err)\n\t\t}\n\t\tif err := p.InvalidKey.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.InvalidKey), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:invalidKey: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *KeyValueGetResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"KeyValueGetResult(%+v)\", *p)\n}\n\n// Attributes:\n//   - Key\n//   - Value\ntype KeyValueSetArgs struct {\n\tKey   string `thrift:\"key,1\" db:\"key\" json:\"key\"`\n\tValue string `thrift:\"value,2\" db:\"value\" json:\"value\"`\n}\n\nfunc NewKeyValueSetArgs() *KeyValueSetArgs {\n\treturn &KeyValueSetArgs{}\n}\n\nfunc (p *KeyValueSetArgs) GetKey() string {\n\treturn p.Key\n}\n\nfunc (p *KeyValueSetArgs) GetValue() string {\n\treturn p.Value\n}\nfunc (p *KeyValueSetArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueSetArgs) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Key = v\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueSetArgs) ReadField2(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 2: \", err)\n\t} else {\n\t\tp.Value = v\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueSetArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Set_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueSetArgs) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"key\", thrift.STRING, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:key: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Key)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.key (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:key: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *KeyValueSetArgs) writeField2(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"value\", thrift.STRING, 2); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:value: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Value)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.value (2) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:value: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *KeyValueSetArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"KeyValueSetArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - InvalidKey\ntype KeyValueSetResult struct {\n\tInvalidKey *InvalidKey `thrift:\"invalidKey,1\" db:\"invalidKey\" json:\"invalidKey,omitempty\"`\n}\n\nfunc NewKeyValueSetResult() *KeyValueSetResult {\n\treturn &KeyValueSetResult{}\n}\n\nvar KeyValueSetResult_InvalidKey_DEFAULT *InvalidKey\n\nfunc (p *KeyValueSetResult) GetInvalidKey() *InvalidKey {\n\tif !p.IsSetInvalidKey() {\n\t\treturn KeyValueSetResult_InvalidKey_DEFAULT\n\t}\n\treturn p.InvalidKey\n}\nfunc (p *KeyValueSetResult) IsSetInvalidKey() bool {\n\treturn p.InvalidKey != nil\n}\n\nfunc (p *KeyValueSetResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueSetResult) ReadField1(iprot thrift.TProtocol) error {\n\tp.InvalidKey = &InvalidKey{}\n\tif err := p.InvalidKey.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.InvalidKey), err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueSetResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Set_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyValueSetResult) writeField1(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetInvalidKey() {\n\t\tif err := oprot.WriteFieldBegin(\"invalidKey\", thrift.STRUCT, 1); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:invalidKey: \", p), err)\n\t\t}\n\t\tif err := p.InvalidKey.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.InvalidKey), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:invalidKey: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *KeyValueSetResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"KeyValueSetResult(%+v)\", *p)\n}\n"
  },
  {
    "path": "examples/keyvalue/gen-go/keyvalue/tchan-keyvalue.go",
    "content": "// @generated Code generated by thrift-gen. Do not modify.\n\n// Package keyvalue is generated code used to make or handle TChannel calls using Thrift.\npackage keyvalue\n\nimport (\n\t\"fmt\"\n\n\tathrift \"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n\t\"github.com/uber/tchannel-go/thrift\"\n)\n\n// Interfaces for the service and client for the services defined in the IDL.\n\n// TChanAdmin is the interface that defines the server handler and client interface.\ntype TChanAdmin interface {\n\tTChanBaseService\n\n\tClearAll(ctx thrift.Context) error\n}\n\n// TChanKeyValue is the interface that defines the server handler and client interface.\ntype TChanKeyValue interface {\n\tTChanBaseService\n\n\tGet(ctx thrift.Context, key string) (string, error)\n\tSet(ctx thrift.Context, key string, value string) error\n}\n\n// TChanBaseService is the interface that defines the server handler and client interface.\ntype TChanBaseService interface {\n\tHealthCheck(ctx thrift.Context) (string, error)\n}\n\n// Implementation of a client and service handler.\n\ntype tchanAdminClient struct {\n\tTChanBaseService\n\n\tthriftService string\n\tclient        thrift.TChanClient\n}\n\nfunc NewTChanAdminInheritedClient(thriftService string, client thrift.TChanClient) *tchanAdminClient {\n\treturn &tchanAdminClient{\n\t\tNewTChanBaseServiceInheritedClient(thriftService, client),\n\t\tthriftService,\n\t\tclient,\n\t}\n}\n\n// NewTChanAdminClient creates a client that can be used to make remote calls.\nfunc NewTChanAdminClient(client thrift.TChanClient) TChanAdmin {\n\treturn NewTChanAdminInheritedClient(\"Admin\", client)\n}\n\nfunc (c *tchanAdminClient) ClearAll(ctx thrift.Context) error {\n\tvar resp AdminClearAllResult\n\targs := AdminClearAllArgs{}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"clearAll\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tcase resp.NotAuthorized != nil:\n\t\t\terr = resp.NotAuthorized\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for clearAll\")\n\t\t}\n\t}\n\n\treturn err\n}\n\ntype tchanAdminServer struct {\n\tthrift.TChanServer\n\n\thandler TChanAdmin\n}\n\n// NewTChanAdminServer wraps a handler for TChanAdmin so it can be\n// registered with a thrift.Server.\nfunc NewTChanAdminServer(handler TChanAdmin) thrift.TChanServer {\n\treturn &tchanAdminServer{\n\t\tNewTChanBaseServiceServer(handler),\n\t\thandler,\n\t}\n}\n\nfunc (s *tchanAdminServer) Service() string {\n\treturn \"Admin\"\n}\n\nfunc (s *tchanAdminServer) Methods() []string {\n\treturn []string{\n\t\t\"clearAll\",\n\n\t\t\"HealthCheck\",\n\t}\n}\n\nfunc (s *tchanAdminServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\tcase \"clearAll\":\n\t\treturn s.handleClearAll(ctx, protocol)\n\n\tcase \"HealthCheck\":\n\t\treturn s.TChanServer.Handle(ctx, methodName, protocol)\n\tdefault:\n\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\nfunc (s *tchanAdminServer) handleClearAll(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req AdminClearAllArgs\n\tvar res AdminClearAllResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\terr :=\n\t\ts.handler.ClearAll(ctx)\n\n\tif err != nil {\n\t\tswitch v := err.(type) {\n\t\tcase *NotAuthorized:\n\t\t\tif v == nil {\n\t\t\t\treturn false, nil, fmt.Errorf(\"Handler for notAuthorized returned non-nil error type *NotAuthorized but nil value\")\n\t\t\t}\n\t\t\tres.NotAuthorized = v\n\t\tdefault:\n\t\t\treturn false, nil, err\n\t\t}\n\t} else {\n\t}\n\n\treturn err == nil, &res, nil\n}\n\ntype tchanKeyValueClient struct {\n\tTChanBaseService\n\n\tthriftService string\n\tclient        thrift.TChanClient\n}\n\nfunc NewTChanKeyValueInheritedClient(thriftService string, client thrift.TChanClient) *tchanKeyValueClient {\n\treturn &tchanKeyValueClient{\n\t\tNewTChanBaseServiceInheritedClient(thriftService, client),\n\t\tthriftService,\n\t\tclient,\n\t}\n}\n\n// NewTChanKeyValueClient creates a client that can be used to make remote calls.\nfunc NewTChanKeyValueClient(client thrift.TChanClient) TChanKeyValue {\n\treturn NewTChanKeyValueInheritedClient(\"KeyValue\", client)\n}\n\nfunc (c *tchanKeyValueClient) Get(ctx thrift.Context, key string) (string, error) {\n\tvar resp KeyValueGetResult\n\targs := KeyValueGetArgs{\n\t\tKey: key,\n\t}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"Get\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tcase resp.NotFound != nil:\n\t\t\terr = resp.NotFound\n\t\tcase resp.InvalidKey != nil:\n\t\t\terr = resp.InvalidKey\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for Get\")\n\t\t}\n\t}\n\n\treturn resp.GetSuccess(), err\n}\n\nfunc (c *tchanKeyValueClient) Set(ctx thrift.Context, key string, value string) error {\n\tvar resp KeyValueSetResult\n\targs := KeyValueSetArgs{\n\t\tKey:   key,\n\t\tValue: value,\n\t}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"Set\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tcase resp.InvalidKey != nil:\n\t\t\terr = resp.InvalidKey\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for Set\")\n\t\t}\n\t}\n\n\treturn err\n}\n\ntype tchanKeyValueServer struct {\n\tthrift.TChanServer\n\n\thandler TChanKeyValue\n}\n\n// NewTChanKeyValueServer wraps a handler for TChanKeyValue so it can be\n// registered with a thrift.Server.\nfunc NewTChanKeyValueServer(handler TChanKeyValue) thrift.TChanServer {\n\treturn &tchanKeyValueServer{\n\t\tNewTChanBaseServiceServer(handler),\n\t\thandler,\n\t}\n}\n\nfunc (s *tchanKeyValueServer) Service() string {\n\treturn \"KeyValue\"\n}\n\nfunc (s *tchanKeyValueServer) Methods() []string {\n\treturn []string{\n\t\t\"Get\",\n\t\t\"Set\",\n\n\t\t\"HealthCheck\",\n\t}\n}\n\nfunc (s *tchanKeyValueServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\tcase \"Get\":\n\t\treturn s.handleGet(ctx, protocol)\n\tcase \"Set\":\n\t\treturn s.handleSet(ctx, protocol)\n\n\tcase \"HealthCheck\":\n\t\treturn s.TChanServer.Handle(ctx, methodName, protocol)\n\tdefault:\n\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\nfunc (s *tchanKeyValueServer) handleGet(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req KeyValueGetArgs\n\tvar res KeyValueGetResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tr, err :=\n\t\ts.handler.Get(ctx, req.Key)\n\n\tif err != nil {\n\t\tswitch v := err.(type) {\n\t\tcase *KeyNotFound:\n\t\t\tif v == nil {\n\t\t\t\treturn false, nil, fmt.Errorf(\"Handler for notFound returned non-nil error type *KeyNotFound but nil value\")\n\t\t\t}\n\t\t\tres.NotFound = v\n\t\tcase *InvalidKey:\n\t\t\tif v == nil {\n\t\t\t\treturn false, nil, fmt.Errorf(\"Handler for invalidKey returned non-nil error type *InvalidKey but nil value\")\n\t\t\t}\n\t\t\tres.InvalidKey = v\n\t\tdefault:\n\t\t\treturn false, nil, err\n\t\t}\n\t} else {\n\t\tres.Success = &r\n\t}\n\n\treturn err == nil, &res, nil\n}\n\nfunc (s *tchanKeyValueServer) handleSet(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req KeyValueSetArgs\n\tvar res KeyValueSetResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\terr :=\n\t\ts.handler.Set(ctx, req.Key, req.Value)\n\n\tif err != nil {\n\t\tswitch v := err.(type) {\n\t\tcase *InvalidKey:\n\t\t\tif v == nil {\n\t\t\t\treturn false, nil, fmt.Errorf(\"Handler for invalidKey returned non-nil error type *InvalidKey but nil value\")\n\t\t\t}\n\t\t\tres.InvalidKey = v\n\t\tdefault:\n\t\t\treturn false, nil, err\n\t\t}\n\t} else {\n\t}\n\n\treturn err == nil, &res, nil\n}\n\ntype tchanBaseServiceClient struct {\n\tthriftService string\n\tclient        thrift.TChanClient\n}\n\nfunc NewTChanBaseServiceInheritedClient(thriftService string, client thrift.TChanClient) *tchanBaseServiceClient {\n\treturn &tchanBaseServiceClient{\n\t\tthriftService,\n\t\tclient,\n\t}\n}\n\n// NewTChanBaseServiceClient creates a client that can be used to make remote calls.\nfunc NewTChanBaseServiceClient(client thrift.TChanClient) TChanBaseService {\n\treturn NewTChanBaseServiceInheritedClient(\"baseService\", client)\n}\n\nfunc (c *tchanBaseServiceClient) HealthCheck(ctx thrift.Context) (string, error) {\n\tvar resp BaseServiceHealthCheckResult\n\targs := BaseServiceHealthCheckArgs{}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"HealthCheck\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for HealthCheck\")\n\t\t}\n\t}\n\n\treturn resp.GetSuccess(), err\n}\n\ntype tchanBaseServiceServer struct {\n\thandler TChanBaseService\n}\n\n// NewTChanBaseServiceServer wraps a handler for TChanBaseService so it can be\n// registered with a thrift.Server.\nfunc NewTChanBaseServiceServer(handler TChanBaseService) thrift.TChanServer {\n\treturn &tchanBaseServiceServer{\n\t\thandler,\n\t}\n}\n\nfunc (s *tchanBaseServiceServer) Service() string {\n\treturn \"baseService\"\n}\n\nfunc (s *tchanBaseServiceServer) Methods() []string {\n\treturn []string{\n\t\t\"HealthCheck\",\n\t}\n}\n\nfunc (s *tchanBaseServiceServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\tcase \"HealthCheck\":\n\t\treturn s.handleHealthCheck(ctx, protocol)\n\n\tdefault:\n\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\nfunc (s *tchanBaseServiceServer) handleHealthCheck(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req BaseServiceHealthCheckArgs\n\tvar res BaseServiceHealthCheckResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tr, err :=\n\t\ts.handler.HealthCheck(ctx)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t} else {\n\t\tres.Success = &r\n\t}\n\n\treturn err == nil, &res, nil\n}\n"
  },
  {
    "path": "examples/keyvalue/gen-go/keyvalue/ttypes.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage keyvalue\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\nvar GoUnusedProtection__ int\n\n// Attributes:\n//   - Key\ntype KeyNotFound struct {\n\tKey string `thrift:\"key,1\" db:\"key\" json:\"key\"`\n}\n\nfunc NewKeyNotFound() *KeyNotFound {\n\treturn &KeyNotFound{}\n}\n\nfunc (p *KeyNotFound) GetKey() string {\n\treturn p.Key\n}\nfunc (p *KeyNotFound) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyNotFound) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Key = v\n\t}\n\treturn nil\n}\n\nfunc (p *KeyNotFound) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"KeyNotFound\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *KeyNotFound) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"key\", thrift.STRING, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:key: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Key)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.key (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:key: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *KeyNotFound) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"KeyNotFound(%+v)\", *p)\n}\n\nfunc (p *KeyNotFound) Error() string {\n\treturn p.String()\n}\n\ntype InvalidKey struct {\n}\n\nfunc NewInvalidKey() *InvalidKey {\n\treturn &InvalidKey{}\n}\n\nfunc (p *InvalidKey) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *InvalidKey) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"InvalidKey\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *InvalidKey) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"InvalidKey(%+v)\", *p)\n}\n\nfunc (p *InvalidKey) Error() string {\n\treturn p.String()\n}\n\ntype NotAuthorized struct {\n}\n\nfunc NewNotAuthorized() *NotAuthorized {\n\treturn &NotAuthorized{}\n}\n\nfunc (p *NotAuthorized) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *NotAuthorized) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"NotAuthorized\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *NotAuthorized) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"NotAuthorized(%+v)\", *p)\n}\n\nfunc (p *NotAuthorized) Error() string {\n\treturn p.String()\n}\n"
  },
  {
    "path": "examples/keyvalue/keyvalue.thrift",
    "content": "service baseService {\n  string HealthCheck()\n}\n\nexception KeyNotFound {\n  1: string key\n}\n\nexception InvalidKey {}\n\nservice KeyValue extends baseService {\n  // If the key does not start with a letter, InvalidKey is returned.\n  // If the key does not exist, KeyNotFound is returned.\n  string Get(1: string key) throws (\n    1: KeyNotFound notFound\n    2: InvalidKey invalidKey)\n\n  // Set returns InvalidKey is an invalid key is sent.\n  void Set(1: string key, 2: string value) throws (\n    1: InvalidKey invalidKey\n  )\n}\n\n// Returned when the user is not authorized for the Admin service.\nexception NotAuthorized {}\n\nservice Admin extends baseService {\n  void clearAll() throws (1: NotAuthorized notAuthorized)\n}\n"
  },
  {
    "path": "examples/keyvalue/server/server.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"sync\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue\"\n\t\"github.com/uber/tchannel-go/hyperbahn\"\n\t\"github.com/uber/tchannel-go/pprof\"\n\t\"github.com/uber/tchannel-go/thrift\"\n)\n\nfunc main() {\n\t// Create a TChannel and register the Thrift handlers.\n\tch, err := tchannel.NewChannel(\"keyvalue\", nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to create tchannel: %v\", err)\n\t}\n\n\t// Register both the KeyValue and Admin services.\n\t// We can register multiple Thrift services on a single Hyperbahn service.\n\th := newKVHandler()\n\tserver := thrift.NewServer(ch)\n\tserver.Register(keyvalue.NewTChanKeyValueServer(h))\n\tserver.Register(keyvalue.NewTChanAdminServer(h))\n\tpprof.Register(ch)\n\n\t// Listen for connections on the external interface so we can receive connections.\n\tip, err := tchannel.ListenIP()\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to find IP to Listen on: %v\", err)\n\t}\n\t// We use port 0 which asks the OS to assign any available port.\n\t// Static port allocations are not necessary for services on Hyperbahn.\n\tch.ListenAndServe(fmt.Sprintf(\"%v:%v\", ip, 0))\n\n\t// Advertising registers this service instance with Hyperbahn so\n\t// that Hyperbahn can route requests for \"keyvalue\" to us.\n\tconfig := hyperbahn.Configuration{InitialNodes: os.Args[1:]}\n\tif len(config.InitialNodes) > 0 {\n\t\tclient, err := hyperbahn.NewClient(ch, config, nil)\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"hyperbahn.NewClient failed: %v\", err)\n\t\t}\n\t\tif err := client.Advertise(); err != nil {\n\t\t\tlog.Fatalf(\"Hyperbahn advertise failed: %v\", err)\n\t\t}\n\t}\n\n\t// The service is now started up, run it till we receive a ctrl-c.\n\tlog.Printf(\"KeyValue service has started on %v\", ch.PeerInfo().HostPort)\n\tselect {}\n}\n\ntype kvHandler struct {\n\tsync.RWMutex\n\tvals map[string]string\n}\n\n// NewKVHandler returns a new handler for the KeyValue service.\nfunc newKVHandler() *kvHandler {\n\treturn &kvHandler{vals: make(map[string]string)}\n}\n\n// Get returns the value stored for the given key.\nfunc (h *kvHandler) Get(ctx thrift.Context, key string) (string, error) {\n\tif err := isValidKey(key); err != nil {\n\t\treturn \"\", err\n\t}\n\n\th.RLock()\n\tdefer h.RUnlock()\n\n\tif val, ok := h.vals[key]; ok {\n\t\treturn val, nil\n\t}\n\n\treturn \"\", &keyvalue.KeyNotFound{Key: key}\n}\n\n// Set sets the value for a given key.\nfunc (h *kvHandler) Set(ctx thrift.Context, key, value string) error {\n\tif err := isValidKey(key); err != nil {\n\t\treturn err\n\t}\n\n\th.Lock()\n\tdefer h.Unlock()\n\n\th.vals[key] = value\n\t// Example of how to use response headers. Normally, these values should be passed via result structs.\n\tctx.SetResponseHeaders(map[string]string{\"count\": fmt.Sprint(len(h.vals))})\n\treturn nil\n}\n\n// HealthCheck return the health status of this process.\nfunc (h *kvHandler) HealthCheck(ctx thrift.Context) (string, error) {\n\treturn \"OK\", nil\n}\n\n// ClearAll clears all the keys.\nfunc (h *kvHandler) ClearAll(ctx thrift.Context) error {\n\tif !isAdmin(ctx) {\n\t\treturn &keyvalue.NotAuthorized{}\n\t}\n\n\th.Lock()\n\tdefer h.Unlock()\n\n\th.vals = make(map[string]string)\n\treturn nil\n}\n\nfunc isValidKey(key string) error {\n\tr, _ := utf8.DecodeRuneInString(key)\n\tif !unicode.IsLetter(r) {\n\t\treturn &keyvalue.InvalidKey{}\n\t}\n\treturn nil\n}\n\nfunc isAdmin(ctx thrift.Context) bool {\n\treturn ctx.Headers()[\"user\"] == \"admin\"\n}\n"
  },
  {
    "path": "examples/ping/README.md",
    "content": "# Ping-Pong\n\n```bash\n./build/examples/ping/pong\n```\n\nThis example creates a client and server channel.  The server channel registers\na `PingService` with a `ping` method, which takes request `Headers` and a `Ping` body\nand returns the same `Headers` along with a `Pong` body.  The client sends a ping\nrequest to the server.\n\nNote that every instance is bidirectional, so the same channel can be used for\nboth sending and receiving requests to peers.  New connections are initiated on\ndemand.\n"
  },
  {
    "path": "examples/ping/main.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"golang.org/x/net/context\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/json\"\n)\n\nvar log = tchannel.SimpleLogger\n\n// Ping is the ping request type.\ntype Ping struct {\n\tMessage string `json:\"message\"`\n}\n\n// Pong is the ping response type.\ntype Pong Ping\n\nfunc pingHandler(ctx json.Context, ping *Ping) (*Pong, error) {\n\treturn &Pong{\n\t\tMessage: fmt.Sprintf(\"ping %v\", ping),\n\t}, nil\n}\n\nfunc pingOtherHandler(ctx json.Context, ping *Ping) (*Pong, error) {\n\treturn &Pong{\n\t\tMessage: fmt.Sprintf(\"pingOther %v\", ping),\n\t}, nil\n}\n\nfunc onError(ctx context.Context, err error) {\n\tlog.WithFields(tchannel.ErrField(err)).Fatal(\"onError handler triggered.\")\n}\n\nfunc listenAndHandle(s *tchannel.Channel, hostPort string) {\n\tlog.Infof(\"Service %s\", hostPort)\n\n\t// If no error is returned, the listen was successful. Serving happens in the background.\n\tif err := s.ListenAndServe(hostPort); err != nil {\n\t\tlog.WithFields(\n\t\t\ttchannel.LogField{Key: \"hostPort\", Value: hostPort},\n\t\t\ttchannel.ErrField(err),\n\t\t).Fatal(\"Couldn't listen.\")\n\t}\n}\n\nfunc main() {\n\t// Create a new TChannel for handling requests\n\tch, err := tchannel.NewChannel(\"PingService\", &tchannel.ChannelOptions{Logger: tchannel.SimpleLogger})\n\tif err != nil {\n\t\tlog.WithFields(tchannel.ErrField(err)).Fatal(\"Couldn't create new channel.\")\n\t}\n\n\t// Register a handler for the ping message on the PingService\n\tjson.Register(ch, json.Handlers{\n\t\t\"ping\": pingHandler,\n\t}, onError)\n\n\t// Listen for incoming requests\n\tlistenAndHandle(ch, \"127.0.0.1:10500\")\n\n\t// Create a new TChannel for sending requests.\n\tclient, err := tchannel.NewChannel(\"ping-client\", nil)\n\tif err != nil {\n\t\tlog.WithFields(tchannel.ErrField(err)).Fatal(\"Couldn't create new client channel.\")\n\t}\n\n\t// Make a call to ourselves, with a timeout of 10s\n\tctx, cancel := json.NewContext(time.Second * 10)\n\tdefer cancel()\n\n\tpeer := client.Peers().Add(ch.PeerInfo().HostPort)\n\n\tvar pong Pong\n\tif err := json.CallPeer(ctx, peer, \"PingService\", \"ping\", &Ping{\"Hello World\"}, &pong); err != nil {\n\t\tlog.WithFields(tchannel.ErrField(err)).Fatal(\"json.Call failed.\")\n\t}\n\n\tlog.Infof(\"Received pong: %s\", pong.Message)\n\n\t// Create a new subchannel for the top-level channel\n\tsubCh := ch.GetSubChannel(\"PingServiceOther\")\n\n\t// Register a handler on the subchannel\n\tjson.Register(subCh, json.Handlers{\n\t\t\"pingOther\": pingOtherHandler,\n\t}, onError)\n\n\t// Try to send a message to the Service:Method pair for the subchannel\n\tif err := json.CallPeer(ctx, peer, \"PingServiceOther\", \"pingOther\", &Ping{\"Hello Other World\"}, &pong); err != nil {\n\t\tlog.WithFields(tchannel.ErrField(err)).Fatal(\"json.Call failed.\")\n\t}\n\n\tlog.Infof(\"Received pong: %s\", pong.Message)\n}\n"
  },
  {
    "path": "examples/test_server/server.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"golang.org/x/net/context\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n)\n\nvar (\n\tflagHost = flag.String(\"host\", \"localhost\", \"The hostname to serve on\")\n\tflagPort = flag.Int(\"port\", 0, \"The port to listen on\")\n)\n\ntype rawHandler struct{}\n\nfunc (rawHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\treturn &raw.Res{\n\t\tArg2: args.Arg2,\n\t\tArg3: args.Arg3,\n\t}, nil\n}\n\nfunc (rawHandler) OnError(ctx context.Context, err error) {\n\tlog.Fatalf(\"OnError: %v\", err)\n}\n\nfunc main() {\n\tflag.Parse()\n\n\tch, err := tchannel.NewChannel(\"test_as_raw\", nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"NewChannel failed: %v\", err)\n\t}\n\n\thandler := raw.Wrap(rawHandler{})\n\tch.Register(handler, \"echo\")\n\tch.Register(handler, \"streaming_echo\")\n\n\thostPort := fmt.Sprintf(\"%s:%v\", *flagHost, *flagPort)\n\tif err := ch.ListenAndServe(hostPort); err != nil {\n\t\tlog.Fatalf(\"ListenAndServe failed: %v\", err)\n\t}\n\n\tfmt.Println(\"listening on\", ch.PeerInfo().HostPort)\n\tselect {}\n}\n"
  },
  {
    "path": "examples/thrift/example.thrift",
    "content": "struct HealthCheckRes {\n  1: bool healthy,\n  2: string msg,\n}\n\nservice Base {\n  void BaseCall()\n}\n\nservice First extends Base {\n  string Echo(1:string msg)\n  HealthCheckRes Healthcheck()\n  void AppError()\n}\n\nservice Second {\n  void Test()\n}\n"
  },
  {
    "path": "examples/thrift/gen-go/example/base.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage example\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\ntype Base interface {\n\tBaseCall() (err error)\n}\n\ntype BaseClient struct {\n\tTransport       thrift.TTransport\n\tProtocolFactory thrift.TProtocolFactory\n\tInputProtocol   thrift.TProtocol\n\tOutputProtocol  thrift.TProtocol\n\tSeqId           int32\n}\n\nfunc NewBaseClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *BaseClient {\n\treturn &BaseClient{Transport: t,\n\t\tProtocolFactory: f,\n\t\tInputProtocol:   f.GetProtocol(t),\n\t\tOutputProtocol:  f.GetProtocol(t),\n\t\tSeqId:           0,\n\t}\n}\n\nfunc NewBaseClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *BaseClient {\n\treturn &BaseClient{Transport: t,\n\t\tProtocolFactory: nil,\n\t\tInputProtocol:   iprot,\n\t\tOutputProtocol:  oprot,\n\t\tSeqId:           0,\n\t}\n}\n\nfunc (p *BaseClient) BaseCall() (err error) {\n\tif err = p.sendBaseCall(); err != nil {\n\t\treturn\n\t}\n\treturn p.recvBaseCall()\n}\n\nfunc (p *BaseClient) sendBaseCall() (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"BaseCall\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := BaseBaseCallArgs{}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *BaseClient) recvBaseCall() (err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"BaseCall\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"BaseCall failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"BaseCall failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error1 error\n\t\terror1, err = error0.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error1\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"BaseCall failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := BaseBaseCallResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\ntype BaseProcessor struct {\n\tprocessorMap map[string]thrift.TProcessorFunction\n\thandler      Base\n}\n\nfunc (p *BaseProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {\n\tp.processorMap[key] = processor\n}\n\nfunc (p *BaseProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {\n\tprocessor, ok = p.processorMap[key]\n\treturn processor, ok\n}\n\nfunc (p *BaseProcessor) ProcessorMap() map[string]thrift.TProcessorFunction {\n\treturn p.processorMap\n}\n\nfunc NewBaseProcessor(handler Base) *BaseProcessor {\n\n\tself2 := &BaseProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)}\n\tself2.processorMap[\"BaseCall\"] = &baseProcessorBaseCall{handler: handler}\n\treturn self2\n}\n\nfunc (p *BaseProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\tname, _, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif processor, ok := p.GetProcessorFunction(name); ok {\n\t\treturn processor.Process(seqId, iprot, oprot)\n\t}\n\tiprot.Skip(thrift.STRUCT)\n\tiprot.ReadMessageEnd()\n\tx3 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function \"+name)\n\toprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)\n\tx3.Write(oprot)\n\toprot.WriteMessageEnd()\n\toprot.Flush()\n\treturn false, x3\n\n}\n\ntype baseProcessorBaseCall struct {\n\thandler Base\n}\n\nfunc (p *baseProcessorBaseCall) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := BaseBaseCallArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"BaseCall\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := BaseBaseCallResult{}\n\tvar err2 error\n\tif err2 = p.handler.BaseCall(); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing BaseCall: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"BaseCall\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"BaseCall\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\n// HELPER FUNCTIONS AND STRUCTURES\n\ntype BaseBaseCallArgs struct {\n}\n\nfunc NewBaseBaseCallArgs() *BaseBaseCallArgs {\n\treturn &BaseBaseCallArgs{}\n}\n\nfunc (p *BaseBaseCallArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *BaseBaseCallArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"BaseCall_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *BaseBaseCallArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"BaseBaseCallArgs(%+v)\", *p)\n}\n\ntype BaseBaseCallResult struct {\n}\n\nfunc NewBaseBaseCallResult() *BaseBaseCallResult {\n\treturn &BaseBaseCallResult{}\n}\n\nfunc (p *BaseBaseCallResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *BaseBaseCallResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"BaseCall_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *BaseBaseCallResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"BaseBaseCallResult(%+v)\", *p)\n}\n"
  },
  {
    "path": "examples/thrift/gen-go/example/constants.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage example\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\nfunc init() {\n}\n"
  },
  {
    "path": "examples/thrift/gen-go/example/first.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage example\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\ntype First interface {\n\tBase\n\n\t// Parameters:\n\t//  - Msg\n\tEcho(msg string) (r string, err error)\n\tHealthcheck() (r *HealthCheckRes, err error)\n\tAppError() (err error)\n}\n\ntype FirstClient struct {\n\t*BaseClient\n}\n\nfunc NewFirstClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *FirstClient {\n\treturn &FirstClient{BaseClient: NewBaseClientFactory(t, f)}\n}\n\nfunc NewFirstClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *FirstClient {\n\treturn &FirstClient{BaseClient: NewBaseClientProtocol(t, iprot, oprot)}\n}\n\n// Parameters:\n//   - Msg\nfunc (p *FirstClient) Echo(msg string) (r string, err error) {\n\tif err = p.sendEcho(msg); err != nil {\n\t\treturn\n\t}\n\treturn p.recvEcho()\n}\n\nfunc (p *FirstClient) sendEcho(msg string) (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"Echo\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := FirstEchoArgs{\n\t\tMsg: msg,\n\t}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *FirstClient) recvEcho() (value string, err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"Echo\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"Echo failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"Echo failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror4 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error5 error\n\t\terror5, err = error4.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error5\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"Echo failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := FirstEchoResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tvalue = result.GetSuccess()\n\treturn\n}\n\nfunc (p *FirstClient) Healthcheck() (r *HealthCheckRes, err error) {\n\tif err = p.sendHealthcheck(); err != nil {\n\t\treturn\n\t}\n\treturn p.recvHealthcheck()\n}\n\nfunc (p *FirstClient) sendHealthcheck() (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"Healthcheck\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := FirstHealthcheckArgs{}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *FirstClient) recvHealthcheck() (value *HealthCheckRes, err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"Healthcheck\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"Healthcheck failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"Healthcheck failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror6 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error7 error\n\t\terror7, err = error6.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error7\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"Healthcheck failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := FirstHealthcheckResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tvalue = result.GetSuccess()\n\treturn\n}\n\nfunc (p *FirstClient) AppError() (err error) {\n\tif err = p.sendAppError(); err != nil {\n\t\treturn\n\t}\n\treturn p.recvAppError()\n}\n\nfunc (p *FirstClient) sendAppError() (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"AppError\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := FirstAppErrorArgs{}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *FirstClient) recvAppError() (err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"AppError\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"AppError failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"AppError failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror8 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error9 error\n\t\terror9, err = error8.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error9\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"AppError failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := FirstAppErrorResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\ntype FirstProcessor struct {\n\t*BaseProcessor\n}\n\nfunc NewFirstProcessor(handler First) *FirstProcessor {\n\tself10 := &FirstProcessor{NewBaseProcessor(handler)}\n\tself10.AddToProcessorMap(\"Echo\", &firstProcessorEcho{handler: handler})\n\tself10.AddToProcessorMap(\"Healthcheck\", &firstProcessorHealthcheck{handler: handler})\n\tself10.AddToProcessorMap(\"AppError\", &firstProcessorAppError{handler: handler})\n\treturn self10\n}\n\ntype firstProcessorEcho struct {\n\thandler First\n}\n\nfunc (p *firstProcessorEcho) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := FirstEchoArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"Echo\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := FirstEchoResult{}\n\tvar retval string\n\tvar err2 error\n\tif retval, err2 = p.handler.Echo(args.Msg); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing Echo: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"Echo\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t} else {\n\t\tresult.Success = &retval\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"Echo\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\ntype firstProcessorHealthcheck struct {\n\thandler First\n}\n\nfunc (p *firstProcessorHealthcheck) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := FirstHealthcheckArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"Healthcheck\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := FirstHealthcheckResult{}\n\tvar retval *HealthCheckRes\n\tvar err2 error\n\tif retval, err2 = p.handler.Healthcheck(); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing Healthcheck: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"Healthcheck\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t} else {\n\t\tresult.Success = retval\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"Healthcheck\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\ntype firstProcessorAppError struct {\n\thandler First\n}\n\nfunc (p *firstProcessorAppError) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := FirstAppErrorArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"AppError\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := FirstAppErrorResult{}\n\tvar err2 error\n\tif err2 = p.handler.AppError(); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing AppError: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"AppError\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"AppError\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\n// HELPER FUNCTIONS AND STRUCTURES\n\n// Attributes:\n//   - Msg\ntype FirstEchoArgs struct {\n\tMsg string `thrift:\"msg,1\" db:\"msg\" json:\"msg\"`\n}\n\nfunc NewFirstEchoArgs() *FirstEchoArgs {\n\treturn &FirstEchoArgs{}\n}\n\nfunc (p *FirstEchoArgs) GetMsg() string {\n\treturn p.Msg\n}\nfunc (p *FirstEchoArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstEchoArgs) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Msg = v\n\t}\n\treturn nil\n}\n\nfunc (p *FirstEchoArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Echo_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstEchoArgs) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"msg\", thrift.STRING, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:msg: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Msg)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.msg (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:msg: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *FirstEchoArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"FirstEchoArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Success\ntype FirstEchoResult struct {\n\tSuccess *string `thrift:\"success,0\" db:\"success\" json:\"success,omitempty\"`\n}\n\nfunc NewFirstEchoResult() *FirstEchoResult {\n\treturn &FirstEchoResult{}\n}\n\nvar FirstEchoResult_Success_DEFAULT string\n\nfunc (p *FirstEchoResult) GetSuccess() string {\n\tif !p.IsSetSuccess() {\n\t\treturn FirstEchoResult_Success_DEFAULT\n\t}\n\treturn *p.Success\n}\nfunc (p *FirstEchoResult) IsSetSuccess() bool {\n\treturn p.Success != nil\n}\n\nfunc (p *FirstEchoResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 0:\n\t\t\tif err := p.ReadField0(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstEchoResult) ReadField0(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 0: \", err)\n\t} else {\n\t\tp.Success = &v\n\t}\n\treturn nil\n}\n\nfunc (p *FirstEchoResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Echo_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField0(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstEchoResult) writeField0(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSuccess() {\n\t\tif err := oprot.WriteFieldBegin(\"success\", thrift.STRING, 0); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 0:success: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteString(string(*p.Success)); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.success (0) field write error: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 0:success: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *FirstEchoResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"FirstEchoResult(%+v)\", *p)\n}\n\ntype FirstHealthcheckArgs struct {\n}\n\nfunc NewFirstHealthcheckArgs() *FirstHealthcheckArgs {\n\treturn &FirstHealthcheckArgs{}\n}\n\nfunc (p *FirstHealthcheckArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstHealthcheckArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Healthcheck_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstHealthcheckArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"FirstHealthcheckArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Success\ntype FirstHealthcheckResult struct {\n\tSuccess *HealthCheckRes `thrift:\"success,0\" db:\"success\" json:\"success,omitempty\"`\n}\n\nfunc NewFirstHealthcheckResult() *FirstHealthcheckResult {\n\treturn &FirstHealthcheckResult{}\n}\n\nvar FirstHealthcheckResult_Success_DEFAULT *HealthCheckRes\n\nfunc (p *FirstHealthcheckResult) GetSuccess() *HealthCheckRes {\n\tif !p.IsSetSuccess() {\n\t\treturn FirstHealthcheckResult_Success_DEFAULT\n\t}\n\treturn p.Success\n}\nfunc (p *FirstHealthcheckResult) IsSetSuccess() bool {\n\treturn p.Success != nil\n}\n\nfunc (p *FirstHealthcheckResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 0:\n\t\t\tif err := p.ReadField0(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstHealthcheckResult) ReadField0(iprot thrift.TProtocol) error {\n\tp.Success = &HealthCheckRes{}\n\tif err := p.Success.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.Success), err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstHealthcheckResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Healthcheck_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField0(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstHealthcheckResult) writeField0(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSuccess() {\n\t\tif err := oprot.WriteFieldBegin(\"success\", thrift.STRUCT, 0); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 0:success: \", p), err)\n\t\t}\n\t\tif err := p.Success.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.Success), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 0:success: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *FirstHealthcheckResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"FirstHealthcheckResult(%+v)\", *p)\n}\n\ntype FirstAppErrorArgs struct {\n}\n\nfunc NewFirstAppErrorArgs() *FirstAppErrorArgs {\n\treturn &FirstAppErrorArgs{}\n}\n\nfunc (p *FirstAppErrorArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstAppErrorArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"AppError_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstAppErrorArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"FirstAppErrorArgs(%+v)\", *p)\n}\n\ntype FirstAppErrorResult struct {\n}\n\nfunc NewFirstAppErrorResult() *FirstAppErrorResult {\n\treturn &FirstAppErrorResult{}\n}\n\nfunc (p *FirstAppErrorResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstAppErrorResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"AppError_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *FirstAppErrorResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"FirstAppErrorResult(%+v)\", *p)\n}\n"
  },
  {
    "path": "examples/thrift/gen-go/example/second.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage example\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\ntype Second interface {\n\tTest() (err error)\n}\n\ntype SecondClient struct {\n\tTransport       thrift.TTransport\n\tProtocolFactory thrift.TProtocolFactory\n\tInputProtocol   thrift.TProtocol\n\tOutputProtocol  thrift.TProtocol\n\tSeqId           int32\n}\n\nfunc NewSecondClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *SecondClient {\n\treturn &SecondClient{Transport: t,\n\t\tProtocolFactory: f,\n\t\tInputProtocol:   f.GetProtocol(t),\n\t\tOutputProtocol:  f.GetProtocol(t),\n\t\tSeqId:           0,\n\t}\n}\n\nfunc NewSecondClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *SecondClient {\n\treturn &SecondClient{Transport: t,\n\t\tProtocolFactory: nil,\n\t\tInputProtocol:   iprot,\n\t\tOutputProtocol:  oprot,\n\t\tSeqId:           0,\n\t}\n}\n\nfunc (p *SecondClient) Test() (err error) {\n\tif err = p.sendTest(); err != nil {\n\t\treturn\n\t}\n\treturn p.recvTest()\n}\n\nfunc (p *SecondClient) sendTest() (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"Test\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := SecondTestArgs{}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *SecondClient) recvTest() (err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"Test\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"Test failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"Test failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror12 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error13 error\n\t\terror13, err = error12.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error13\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"Test failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := SecondTestResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\ntype SecondProcessor struct {\n\tprocessorMap map[string]thrift.TProcessorFunction\n\thandler      Second\n}\n\nfunc (p *SecondProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {\n\tp.processorMap[key] = processor\n}\n\nfunc (p *SecondProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {\n\tprocessor, ok = p.processorMap[key]\n\treturn processor, ok\n}\n\nfunc (p *SecondProcessor) ProcessorMap() map[string]thrift.TProcessorFunction {\n\treturn p.processorMap\n}\n\nfunc NewSecondProcessor(handler Second) *SecondProcessor {\n\n\tself14 := &SecondProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)}\n\tself14.processorMap[\"Test\"] = &secondProcessorTest{handler: handler}\n\treturn self14\n}\n\nfunc (p *SecondProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\tname, _, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif processor, ok := p.GetProcessorFunction(name); ok {\n\t\treturn processor.Process(seqId, iprot, oprot)\n\t}\n\tiprot.Skip(thrift.STRUCT)\n\tiprot.ReadMessageEnd()\n\tx15 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function \"+name)\n\toprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)\n\tx15.Write(oprot)\n\toprot.WriteMessageEnd()\n\toprot.Flush()\n\treturn false, x15\n\n}\n\ntype secondProcessorTest struct {\n\thandler Second\n}\n\nfunc (p *secondProcessorTest) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := SecondTestArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"Test\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := SecondTestResult{}\n\tvar err2 error\n\tif err2 = p.handler.Test(); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing Test: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"Test\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"Test\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\n// HELPER FUNCTIONS AND STRUCTURES\n\ntype SecondTestArgs struct {\n}\n\nfunc NewSecondTestArgs() *SecondTestArgs {\n\treturn &SecondTestArgs{}\n}\n\nfunc (p *SecondTestArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SecondTestArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Test_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *SecondTestArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"SecondTestArgs(%+v)\", *p)\n}\n\ntype SecondTestResult struct {\n}\n\nfunc NewSecondTestResult() *SecondTestResult {\n\treturn &SecondTestResult{}\n}\n\nfunc (p *SecondTestResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SecondTestResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Test_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *SecondTestResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"SecondTestResult(%+v)\", *p)\n}\n"
  },
  {
    "path": "examples/thrift/gen-go/example/tchan-example.go",
    "content": "// @generated Code generated by thrift-gen. Do not modify.\n\n// Package example is generated code used to make or handle TChannel calls using Thrift.\npackage example\n\nimport (\n\t\"fmt\"\n\n\tathrift \"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n\t\"github.com/uber/tchannel-go/thrift\"\n)\n\n// Interfaces for the service and client for the services defined in the IDL.\n\n// TChanBase is the interface that defines the server handler and client interface.\ntype TChanBase interface {\n\tBaseCall(ctx thrift.Context) error\n}\n\n// TChanFirst is the interface that defines the server handler and client interface.\ntype TChanFirst interface {\n\tTChanBase\n\n\tAppError(ctx thrift.Context) error\n\tEcho(ctx thrift.Context, msg string) (string, error)\n\tHealthcheck(ctx thrift.Context) (*HealthCheckRes, error)\n}\n\n// TChanSecond is the interface that defines the server handler and client interface.\ntype TChanSecond interface {\n\tTest(ctx thrift.Context) error\n}\n\n// Implementation of a client and service handler.\n\ntype tchanBaseClient struct {\n\tthriftService string\n\tclient        thrift.TChanClient\n}\n\nfunc NewTChanBaseInheritedClient(thriftService string, client thrift.TChanClient) *tchanBaseClient {\n\treturn &tchanBaseClient{\n\t\tthriftService,\n\t\tclient,\n\t}\n}\n\n// NewTChanBaseClient creates a client that can be used to make remote calls.\nfunc NewTChanBaseClient(client thrift.TChanClient) TChanBase {\n\treturn NewTChanBaseInheritedClient(\"Base\", client)\n}\n\nfunc (c *tchanBaseClient) BaseCall(ctx thrift.Context) error {\n\tvar resp BaseBaseCallResult\n\targs := BaseBaseCallArgs{}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"BaseCall\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for BaseCall\")\n\t\t}\n\t}\n\n\treturn err\n}\n\ntype tchanBaseServer struct {\n\thandler TChanBase\n}\n\n// NewTChanBaseServer wraps a handler for TChanBase so it can be\n// registered with a thrift.Server.\nfunc NewTChanBaseServer(handler TChanBase) thrift.TChanServer {\n\treturn &tchanBaseServer{\n\t\thandler,\n\t}\n}\n\nfunc (s *tchanBaseServer) Service() string {\n\treturn \"Base\"\n}\n\nfunc (s *tchanBaseServer) Methods() []string {\n\treturn []string{\n\t\t\"BaseCall\",\n\t}\n}\n\nfunc (s *tchanBaseServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\tcase \"BaseCall\":\n\t\treturn s.handleBaseCall(ctx, protocol)\n\n\tdefault:\n\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\nfunc (s *tchanBaseServer) handleBaseCall(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req BaseBaseCallArgs\n\tvar res BaseBaseCallResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\terr :=\n\t\ts.handler.BaseCall(ctx)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t} else {\n\t}\n\n\treturn err == nil, &res, nil\n}\n\ntype tchanFirstClient struct {\n\tTChanBase\n\n\tthriftService string\n\tclient        thrift.TChanClient\n}\n\nfunc NewTChanFirstInheritedClient(thriftService string, client thrift.TChanClient) *tchanFirstClient {\n\treturn &tchanFirstClient{\n\t\tNewTChanBaseInheritedClient(thriftService, client),\n\t\tthriftService,\n\t\tclient,\n\t}\n}\n\n// NewTChanFirstClient creates a client that can be used to make remote calls.\nfunc NewTChanFirstClient(client thrift.TChanClient) TChanFirst {\n\treturn NewTChanFirstInheritedClient(\"First\", client)\n}\n\nfunc (c *tchanFirstClient) AppError(ctx thrift.Context) error {\n\tvar resp FirstAppErrorResult\n\targs := FirstAppErrorArgs{}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"AppError\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for AppError\")\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (c *tchanFirstClient) Echo(ctx thrift.Context, msg string) (string, error) {\n\tvar resp FirstEchoResult\n\targs := FirstEchoArgs{\n\t\tMsg: msg,\n\t}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"Echo\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for Echo\")\n\t\t}\n\t}\n\n\treturn resp.GetSuccess(), err\n}\n\nfunc (c *tchanFirstClient) Healthcheck(ctx thrift.Context) (*HealthCheckRes, error) {\n\tvar resp FirstHealthcheckResult\n\targs := FirstHealthcheckArgs{}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"Healthcheck\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for Healthcheck\")\n\t\t}\n\t}\n\n\treturn resp.GetSuccess(), err\n}\n\ntype tchanFirstServer struct {\n\tthrift.TChanServer\n\n\thandler TChanFirst\n}\n\n// NewTChanFirstServer wraps a handler for TChanFirst so it can be\n// registered with a thrift.Server.\nfunc NewTChanFirstServer(handler TChanFirst) thrift.TChanServer {\n\treturn &tchanFirstServer{\n\t\tNewTChanBaseServer(handler),\n\t\thandler,\n\t}\n}\n\nfunc (s *tchanFirstServer) Service() string {\n\treturn \"First\"\n}\n\nfunc (s *tchanFirstServer) Methods() []string {\n\treturn []string{\n\t\t\"AppError\",\n\t\t\"Echo\",\n\t\t\"Healthcheck\",\n\n\t\t\"BaseCall\",\n\t}\n}\n\nfunc (s *tchanFirstServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\tcase \"AppError\":\n\t\treturn s.handleAppError(ctx, protocol)\n\tcase \"Echo\":\n\t\treturn s.handleEcho(ctx, protocol)\n\tcase \"Healthcheck\":\n\t\treturn s.handleHealthcheck(ctx, protocol)\n\n\tcase \"BaseCall\":\n\t\treturn s.TChanServer.Handle(ctx, methodName, protocol)\n\tdefault:\n\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\nfunc (s *tchanFirstServer) handleAppError(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req FirstAppErrorArgs\n\tvar res FirstAppErrorResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\terr :=\n\t\ts.handler.AppError(ctx)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t} else {\n\t}\n\n\treturn err == nil, &res, nil\n}\n\nfunc (s *tchanFirstServer) handleEcho(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req FirstEchoArgs\n\tvar res FirstEchoResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tr, err :=\n\t\ts.handler.Echo(ctx, req.Msg)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t} else {\n\t\tres.Success = &r\n\t}\n\n\treturn err == nil, &res, nil\n}\n\nfunc (s *tchanFirstServer) handleHealthcheck(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req FirstHealthcheckArgs\n\tvar res FirstHealthcheckResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tr, err :=\n\t\ts.handler.Healthcheck(ctx)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t} else {\n\t\tres.Success = r\n\t}\n\n\treturn err == nil, &res, nil\n}\n\ntype tchanSecondClient struct {\n\tthriftService string\n\tclient        thrift.TChanClient\n}\n\nfunc NewTChanSecondInheritedClient(thriftService string, client thrift.TChanClient) *tchanSecondClient {\n\treturn &tchanSecondClient{\n\t\tthriftService,\n\t\tclient,\n\t}\n}\n\n// NewTChanSecondClient creates a client that can be used to make remote calls.\nfunc NewTChanSecondClient(client thrift.TChanClient) TChanSecond {\n\treturn NewTChanSecondInheritedClient(\"Second\", client)\n}\n\nfunc (c *tchanSecondClient) Test(ctx thrift.Context) error {\n\tvar resp SecondTestResult\n\targs := SecondTestArgs{}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"Test\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for Test\")\n\t\t}\n\t}\n\n\treturn err\n}\n\ntype tchanSecondServer struct {\n\thandler TChanSecond\n}\n\n// NewTChanSecondServer wraps a handler for TChanSecond so it can be\n// registered with a thrift.Server.\nfunc NewTChanSecondServer(handler TChanSecond) thrift.TChanServer {\n\treturn &tchanSecondServer{\n\t\thandler,\n\t}\n}\n\nfunc (s *tchanSecondServer) Service() string {\n\treturn \"Second\"\n}\n\nfunc (s *tchanSecondServer) Methods() []string {\n\treturn []string{\n\t\t\"Test\",\n\t}\n}\n\nfunc (s *tchanSecondServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\tcase \"Test\":\n\t\treturn s.handleTest(ctx, protocol)\n\n\tdefault:\n\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\nfunc (s *tchanSecondServer) handleTest(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req SecondTestArgs\n\tvar res SecondTestResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\terr :=\n\t\ts.handler.Test(ctx)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t} else {\n\t}\n\n\treturn err == nil, &res, nil\n}\n"
  },
  {
    "path": "examples/thrift/gen-go/example/ttypes.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage example\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\nvar GoUnusedProtection__ int\n\n// Attributes:\n//   - Healthy\n//   - Msg\ntype HealthCheckRes struct {\n\tHealthy bool   `thrift:\"healthy,1\" db:\"healthy\" json:\"healthy\"`\n\tMsg     string `thrift:\"msg,2\" db:\"msg\" json:\"msg\"`\n}\n\nfunc NewHealthCheckRes() *HealthCheckRes {\n\treturn &HealthCheckRes{}\n}\n\nfunc (p *HealthCheckRes) GetHealthy() bool {\n\treturn p.Healthy\n}\n\nfunc (p *HealthCheckRes) GetMsg() string {\n\treturn p.Msg\n}\nfunc (p *HealthCheckRes) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *HealthCheckRes) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadBool(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Healthy = v\n\t}\n\treturn nil\n}\n\nfunc (p *HealthCheckRes) ReadField2(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 2: \", err)\n\t} else {\n\t\tp.Msg = v\n\t}\n\treturn nil\n}\n\nfunc (p *HealthCheckRes) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"HealthCheckRes\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *HealthCheckRes) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"healthy\", thrift.BOOL, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:healthy: \", p), err)\n\t}\n\tif err := oprot.WriteBool(bool(p.Healthy)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.healthy (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:healthy: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *HealthCheckRes) writeField2(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"msg\", thrift.STRING, 2); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:msg: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Msg)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.msg (2) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:msg: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *HealthCheckRes) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"HealthCheckRes(%+v)\", *p)\n}\n"
  },
  {
    "path": "examples/thrift/main.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\ttchannel \"github.com/uber/tchannel-go\"\n\tgen \"github.com/uber/tchannel-go/examples/thrift/gen-go/example\"\n\t\"github.com/uber/tchannel-go/thrift\"\n)\n\nfunc main() {\n\tvar (\n\t\tlistener net.Listener\n\t\terr      error\n\t)\n\n\tif listener, err = setupServer(); err != nil {\n\t\tlog.Fatalf(\"setupServer failed: %v\", err)\n\t}\n\n\tif err := runClient1(\"server\", listener.Addr()); err != nil {\n\t\tlog.Fatalf(\"runClient1 failed: %v\", err)\n\t}\n\n\tif err := runClient2(\"server\", listener.Addr()); err != nil {\n\t\tlog.Fatalf(\"runClient2 failed: %v\", err)\n\t}\n\n\tgo listenConsole()\n\n\t// Run for 10 seconds, then stop\n\ttime.Sleep(time.Second * 10)\n}\n\nfunc setupServer() (net.Listener, error) {\n\ttchan, err := tchannel.NewChannel(\"server\", optsFor(\"server\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlistener, err := net.Listen(\"tcp\", \":0\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tserver := thrift.NewServer(tchan)\n\tserver.Register(gen.NewTChanFirstServer(&firstHandler{}))\n\tserver.Register(gen.NewTChanSecondServer(&secondHandler{}))\n\n\t// Serve will set the local peer info, and start accepting sockets in a separate goroutine.\n\ttchan.Serve(listener)\n\treturn listener, nil\n}\n\nfunc runClient1(hyperbahnService string, addr net.Addr) error {\n\ttchan, err := tchannel.NewChannel(\"client1\", optsFor(\"client1\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\ttchan.Peers().Add(addr.String())\n\ttclient := thrift.NewClient(tchan, hyperbahnService, nil)\n\tclient := gen.NewTChanFirstClient(tclient)\n\n\tgo func() {\n\t\tfor {\n\t\t\tctx, cancel := thrift.NewContext(time.Second)\n\t\t\tres, err := client.Echo(ctx, \"Hi\")\n\t\t\tlog.Println(\"Echo(Hi) = \", res, \", err: \", err)\n\t\t\tlog.Println(\"AppError() = \", client.AppError(ctx))\n\t\t\tlog.Println(\"BaseCall() = \", client.BaseCall(ctx))\n\t\t\tcancel()\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t}\n\t}()\n\treturn nil\n}\n\nfunc runClient2(hyperbahnService string, addr net.Addr) error {\n\ttchan, err := tchannel.NewChannel(\"client2\", optsFor(\"client2\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\ttchan.Peers().Add(addr.String())\n\ttclient := thrift.NewClient(tchan, hyperbahnService, nil)\n\tclient := gen.NewTChanSecondClient(tclient)\n\n\tgo func() {\n\t\tfor {\n\t\t\tctx, cancel := thrift.NewContext(time.Second)\n\t\t\tclient.Test(ctx)\n\t\t\tcancel()\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t}\n\t}()\n\treturn nil\n}\n\nfunc listenConsole() {\n\trdr := bufio.NewReader(os.Stdin)\n\tfor {\n\t\tline, _ := rdr.ReadString('\\n')\n\t\tswitch strings.TrimSpace(line) {\n\t\tcase \"s\":\n\t\t\tprintStack()\n\t\tdefault:\n\t\t\tfmt.Println(\"Unrecognized command:\", line)\n\t\t}\n\t}\n}\n\nfunc printStack() {\n\tbuf := make([]byte, 10000)\n\truntime.Stack(buf, true /* all */)\n\tfmt.Println(\"Stack:\\n\", string(buf))\n}\n\ntype firstHandler struct{}\n\nfunc (h *firstHandler) Healthcheck(ctx thrift.Context) (*gen.HealthCheckRes, error) {\n\tlog.Printf(\"first: HealthCheck()\\n\")\n\treturn &gen.HealthCheckRes{\n\t\tHealthy: true,\n\t\tMsg:     \"OK\"}, nil\n}\n\nfunc (h *firstHandler) BaseCall(ctx thrift.Context) error {\n\tlog.Printf(\"first: BaseCall()\\n\")\n\treturn nil\n}\n\nfunc (h *firstHandler) Echo(ctx thrift.Context, msg string) (r string, err error) {\n\tlog.Printf(\"first: Echo(%v)\\n\", msg)\n\treturn msg, nil\n}\n\nfunc (h *firstHandler) AppError(ctx thrift.Context) error {\n\tlog.Printf(\"first: AppError()\\n\")\n\treturn errors.New(\"app error\")\n}\n\nfunc (h *firstHandler) OneWay(ctx thrift.Context) error {\n\tlog.Printf(\"first: OneWay()\\n\")\n\treturn errors.New(\"OneWay error...won't be seen by client\")\n}\n\ntype secondHandler struct{}\n\nfunc (h *secondHandler) Test(ctx thrift.Context) error {\n\tlog.Println(\"secondHandler: Test()\")\n\treturn nil\n}\n\nfunc optsFor(processName string) *tchannel.ChannelOptions {\n\treturn &tchannel.ChannelOptions{\n\t\tProcessName: processName,\n\t\tLogger:      tchannel.NewLevelLogger(tchannel.SimpleLogger, tchannel.LogLevelWarn),\n\t}\n}\n"
  },
  {
    "path": "fragmentation_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nconst (\n\ttestFragmentHeaderSize = 1 /* flags */ + 1 /* checksum type */ + 4 /* CRC32 checksum */\n\n\ttestFragmentPayloadSize = 10 // enough room for a small payload\n\ttestFragmentSize        = testFragmentHeaderSize + testFragmentPayloadSize\n)\n\nfunc TestFragmentationEmptyArgs(t *testing.T) {\n\trunFragmentationTest(t, []string{\"\", \"\", \"\"}, buffers([][]byte{{\n\t\t0x0000,                                                  // flags\n\t\tbyte(ChecksumTypeCrc32), 0x0000, 0x0000, 0x0000, 0x0000, // empty checksum\n\t\t0x0000, 0x0000, // arg 1 (length no body)\n\t\t0x0000, 0x0000, // arg 2 (length no body)\n\t\t0x0000, 0x0000, // arg 3 (length no body)\n\t}}))\n}\n\nfunc TestFragmentationSingleFragment(t *testing.T) {\n\trunFragmentationTest(t, []string{\"A\", \"B\", \"C\"}, buffers([][]byte{{\n\t\t0x0000,                                         // flags\n\t\tbyte(ChecksumTypeCrc32), 0xa3, 0x83, 0x3, 0x48, // CRC32 checksum\n\t\t0x0000, 0x0001, 'A', // arg 1 (length single character body)\n\t\t0x0000, 0x0001, 'B', // arg 2 (length single character body)\n\t\t0x0000, 0x0001, 'C', // arg 3 (length single character body)\n\t}}))\n}\n\nfunc TestFragmentationMultipleFragments(t *testing.T) {\n\trunFragmentationTest(t, []string{\"ABCDEFHIJKLM\", \"NOPQRZTUWXYZ\", \"012345678\"}, buffers(\n\t\t[][]byte{{\n\t\t\t0x0001,                                          // has more fragments\n\t\t\tbyte(ChecksumTypeCrc32), 0x98, 0x43, 0x9a, 0x45, //  checksum\n\t\t\t0x0000, 0x0008, 'A', 'B', 'C', 'D', 'E', 'F', 'H', 'I'}}, // first 8 bytes of arg 1\n\t\t[][]byte{{\n\t\t\t0x0001,                                          // has more fragments\n\t\t\tbyte(ChecksumTypeCrc32), 0xaf, 0xb9, 0x9c, 0x98, //  checksum\n\t\t\t0x0000, 0x0004, 'J', 'K', 'L', 'M', // remaining 4 bytes of arg 1\n\t\t\t0x0000, 0x0002, 'N', 'O'}}, // all of arg 2 that fits (2 bytes)\n\t\t[][]byte{{\n\t\t\t0x0001,                                          // has more fragments\n\t\t\tbyte(ChecksumTypeCrc32), 0x23, 0xae, 0x2f, 0x37, //  checksum\n\t\t\t0x0000, 0x0008, 'P', 'Q', 'R', 'Z', 'T', 'U', 'W', 'X'}}, // more aarg 2\n\t\t[][]byte{{\n\t\t\t0x0001,                                          // has more fragments\n\t\t\tbyte(ChecksumTypeCrc32), 0xa2, 0x93, 0x74, 0xd8, //  checksum\n\t\t\t0x0000, 0x0002, 'Y', 'Z', // last parts of arg 2\n\t\t\t0x0000, 0x0004, '0', '1', '2', '3'}}, // first parts of arg 3\n\t\t[][]byte{{\n\t\t\t0x0000,                                          // no more fragments\n\t\t\tbyte(ChecksumTypeCrc32), 0xf3, 0x29, 0xbb, 0xd1, // checksum\n\t\t\t0x0000, 0x0005, '4', '5', '6', '7', '8'}},\n\t))\n}\n\nfunc TestFragmentationMiddleArgNearFragmentBoundary(t *testing.T) {\n\t// This covers the case where an argument in the middle ends near the\n\t// end of a fragment boundary, such that there is not enough room to\n\t// put another argument in the fragment.  In this case there should be\n\t// an empty chunk for that argument in the next fragment\n\trunFragmentationTest(t, []string{\"ABCDEF\", \"NOPQ\"}, buffers(\n\t\t[][]byte{{\n\t\t\t0x0001,                                          // has more fragments\n\t\t\tbyte(ChecksumTypeCrc32), 0xbb, 0x76, 0xfe, 0x69, // CRC32 checksum\n\t\t\t0x0000, 0x0006, 'A', 'B', 'C', 'D', 'E', 'F'}}, // all of arg 1\n\t\t[][]byte{{\n\t\t\t0x0000,                                          // no more fragments\n\t\t\tbyte(ChecksumTypeCrc32), 0x5b, 0x3c, 0x54, 0xfe, // CRC32 checksum\n\t\t\t0x0000, 0x0000, // empty chunk indicating the end of arg 1\n\t\t\t0x0000, 0x0004, 'N', 'O', 'P', 'Q'}}, // all of arg 2\n\t))\n}\n\nfunc TestFragmentationMiddleArgOnExactFragmentBoundary(t *testing.T) {\n\t// This covers the case where an argument in the middle ends exactly at the end of a fragment.\n\t// Again, there should be an empty chunk for that argument in the next fragment\n\trunFragmentationTest(t, []string{\"ABCDEFGH\", \"NOPQ\"}, buffers(\n\t\t[][]byte{{\n\t\t\t0x0001,                                          // has more fragments\n\t\t\tbyte(ChecksumTypeCrc32), 0x68, 0xdc, 0xb6, 0x1c, // CRC32 checksum\n\t\t\t0x0000, 0x0008, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'}}, // all of arg 1\n\t\t[][]byte{{\n\t\t\t0x0000,                                         // no more fragments\n\t\t\tbyte(ChecksumTypeCrc32), 0x32, 0x66, 0xf, 0x25, // CRC32 checksum\n\t\t\t0x0000, 0x0000, // empty chunk indicating the end of arg 1\n\t\t\t0x0000, 0x0004, 'N', 'O', 'P', 'Q'}}, // all of arg 2\n\t))\n}\n\nfunc TestFragmentationLastArgOnNearFragmentBoundary(t *testing.T) {\n\t// Covers the case where the last argument ends near a fragment\n\t// boundary.  No new fragments should get created\n\trunFragmentationTest(t, []string{\"ABCDEF\"}, buffers(\n\t\t[][]byte{{\n\t\t\t0x0000,                                          // has more fragments\n\t\t\tbyte(ChecksumTypeCrc32), 0xbb, 0x76, 0xfe, 0x69, // CRC32 checksum\n\t\t\t0x0000, 0x0006, 'A', 'B', 'C', 'D', 'E', 'F'}}, // all of arg 1\n\t))\n}\n\nfunc TestFragmentationLastArgOnExactFragmentBoundary(t *testing.T) {\n\t// Covers the case where the last argument ends exactly on a fragment\n\t// boundary.  No new fragments should get created\n\trunFragmentationTest(t, []string{\"ABCDEFGH\"}, buffers(\n\t\t[][]byte{{\n\t\t\t0x0000,                                          // has more fragments\n\t\t\tbyte(ChecksumTypeCrc32), 0x68, 0xdc, 0xb6, 0x1c, // CRC32 checksum\n\t\t\t0x0000, 0x0008, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'}}, // all of arg 1\n\t))\n}\n\nfunc TestFragmentationWriterErrors(t *testing.T) {\n\trunFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) {\n\t\t// Write without starting argument\n\t\t_, err := w.Write([]byte(\"foo\"))\n\t\tassert.Error(t, err)\n\t})\n\n\trunFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) {\n\t\t// BeginArgument twice without starting argument\n\t\tassert.NoError(t, w.BeginArgument(false /* last */))\n\t\tassert.Error(t, w.BeginArgument(false /* last */))\n\t})\n\n\trunFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) {\n\t\t// BeginArgument after writing final argument\n\t\twriter, err := w.ArgWriter(true /* last */)\n\t\tassert.NoError(t, err)\n\n\t\tassert.NoError(t, NewArgWriter(writer, nil).Write([]byte(\"hello\")))\n\t\tassert.Error(t, w.BeginArgument(false /* last */))\n\t})\n\n\trunFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) {\n\t\t// Close without beginning argument\n\t\tassert.Error(t, w.Close())\n\t})\n}\n\nfunc TestFragmentationReaderErrors(t *testing.T) {\n\trunFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) {\n\t\t// Read without starting argument\n\t\tb := make([]byte, 10)\n\t\t_, err := r.Read(b)\n\t\tassert.Error(t, err)\n\t})\n\n\trunFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) {\n\t\t// Close without beginning argument\n\t\tassert.Error(t, r.Close())\n\t})\n\n\trunFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) {\n\t\t// BeginArgument after reading final argument\n\t\twriter, err := w.ArgWriter(true /* last */)\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, NewArgWriter(writer, nil).Write([]byte(\"hello\")))\n\n\t\treader, err := r.ArgReader(true /* last */)\n\t\tassert.NoError(t, err)\n\n\t\tvar arg []byte\n\t\tassert.NoError(t, NewArgReader(reader, nil).Read(&arg))\n\t\tassert.Equal(t, \"hello\", string(arg))\n\t\tassert.Error(t, r.BeginArgument(false /* last */))\n\t})\n\n\trunFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) {\n\t\t// Sender sent final argument, but receiver thinks there is more\n\t\twriter, err := w.ArgWriter(true /* last */)\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, NewArgWriter(writer, nil).Write([]byte(\"hello\")))\n\n\t\treader, err := r.ArgReader(false /* last */)\n\t\tassert.NoError(t, err)\n\n\t\tvar arg []byte\n\t\tassert.Error(t, NewArgReader(reader, nil).Read(&arg))\n\t})\n\n\trunFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) {\n\t\t// Close without receiving all data in chunk\n\t\twriter, err := w.ArgWriter(true /* last */)\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, NewArgWriter(writer, nil).Write([]byte(\"hello\")))\n\n\t\tassert.NoError(t, r.BeginArgument(true /* last */))\n\t\tb := make([]byte, 3)\n\t\t_, err = r.Read(b)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"hel\", string(b))\n\t\tassert.Error(t, r.Close())\n\t})\n\n\trunFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) {\n\t\t// Close without receiving all fragments\n\t\twriter, err := w.ArgWriter(true /* last */)\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, NewArgWriter(writer, nil).Write([]byte(\"hello world what's up\")))\n\n\t\tassert.NoError(t, r.BeginArgument(true /* last */))\n\t\tb := make([]byte, 8)\n\t\t_, err = r.Read(b)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"hello wo\", string(b))\n\t\tassert.Error(t, r.Close())\n\t})\n\n\trunFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) {\n\t\t// BeginArgument while argument is in process\n\t\twriter, err := w.ArgWriter(true /* last */)\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, NewArgWriter(writer, nil).Write([]byte(\"hello world what's up\")))\n\t\tassert.NoError(t, r.BeginArgument(false /* last */))\n\t\tassert.Error(t, r.BeginArgument(false /* last */))\n\t})\n}\n\nfunc TestFragmentationChecksumTypeErrors(t *testing.T) {\n\tsendCh := make(fragmentChannel, 10)\n\trecvCh := make(fragmentChannel, 10)\n\tw := newFragmentingWriter(NullLogger, sendCh, ChecksumTypeCrc32.New())\n\tr := newFragmentingReader(NullLogger, recvCh)\n\n\t// Write two fragments out\n\twriter, err := w.ArgWriter(true /* last */)\n\tassert.NoError(t, err)\n\tassert.NoError(t, NewArgWriter(writer, nil).Write([]byte(\"hello world what's up\")))\n\n\t// Intercept and change the checksum type between the first and second fragment\n\tfirst := <-sendCh\n\trecvCh <- first\n\n\tsecond := <-sendCh\n\tsecond[1] = byte(ChecksumTypeCrc32C)\n\trecvCh <- second\n\n\t// Attempt to read, should fail\n\treader, err := r.ArgReader(true /* last */)\n\tassert.NoError(t, err)\n\n\tvar arg []byte\n\tassert.Error(t, NewArgReader(reader, nil).Read(&arg))\n}\n\nfunc TestFragmentationChecksumMismatch(t *testing.T) {\n\tsendCh := make(fragmentChannel, 10)\n\trecvCh := make(fragmentChannel, 10)\n\tw := newFragmentingWriter(NullLogger, sendCh, ChecksumTypeCrc32.New())\n\tr := newFragmentingReader(NullLogger, recvCh)\n\n\t// Write two fragments out\n\twriter, err := w.ArgWriter(true /* last */)\n\tassert.NoError(t, err)\n\tassert.NoError(t, NewArgWriter(writer, nil).Write([]byte(\"hello world this is two\")))\n\n\t// Intercept and change the checksum value in the second fragment\n\tfirst := <-sendCh\n\trecvCh <- first\n\n\tsecond := <-sendCh\n\tsecond[2], second[3], second[4], second[5] = 0x01, 0x02, 0x03, 0x04\n\trecvCh <- second\n\n\t// Attempt to read, should fail due to mismatch between local checksum and peer supplied checksum\n\treader, err := r.ArgReader(true /* last */)\n\tassert.NoError(t, err)\n\n\t_, err = io.Copy(ioutil.Discard, reader)\n\tassert.Equal(t, errMismatchedChecksums, err)\n}\n\nfunc runFragmentationErrorTest(f func(w *fragmentingWriter, r *fragmentingReader)) {\n\tch := make(fragmentChannel, 10)\n\tw := newFragmentingWriter(NullLogger, ch, ChecksumTypeCrc32.New())\n\tr := newFragmentingReader(NullLogger, ch)\n\tf(w, r)\n}\n\nfunc runFragmentationTest(t *testing.T, args []string, expectedFragments [][]byte) {\n\tsendCh := make(fragmentChannel, 10)\n\trecvCh := make(fragmentChannel, 10)\n\n\tw := newFragmentingWriter(NullLogger, sendCh, ChecksumTypeCrc32.New())\n\tr := newFragmentingReader(NullLogger, recvCh)\n\n\tvar fragments [][]byte\n\tvar actualArgs []string\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor fragment := range sendCh {\n\t\t\tfragments = append(fragments, fragment)\n\t\t\trecvCh <- fragment\n\t\t}\n\t}()\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\n\t\tfor i := 0; i < len(args)-1; i++ {\n\t\t\treader, err := r.ArgReader(false /* last */)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tvar arg []byte\n\t\t\trequire.NoError(t, NewArgReader(reader, nil).Read(&arg))\n\t\t\tactualArgs = append(actualArgs, string(arg))\n\t\t}\n\n\t\treader, err := r.ArgReader(true /* last */)\n\t\trequire.NoError(t, err)\n\n\t\tvar arg []byte\n\t\trequire.NoError(t, NewArgReader(reader, nil).Read(&arg))\n\t\tactualArgs = append(actualArgs, string(arg))\n\t}()\n\n\tfor i := 0; i < len(args)-1; i++ {\n\t\twriter, err := w.ArgWriter(false /* last */)\n\t\tassert.NoError(t, err)\n\t\trequire.NoError(t, NewArgWriter(writer, nil).Write([]byte(args[i])))\n\t}\n\twriter, err := w.ArgWriter(true /* last */)\n\tassert.NoError(t, err)\n\trequire.NoError(t, NewArgWriter(writer, nil).Write([]byte(args[len(args)-1])))\n\tclose(sendCh)\n\n\twg.Wait()\n\n\tassert.Equal(t, args, actualArgs)\n\tassert.Equal(t, len(expectedFragments), len(fragments), \"incorrect number of fragments\")\n\tfor i := 0; i < len(expectedFragments); i++ {\n\t\texpectedFragment, fragment := expectedFragments[i], fragments[i]\n\t\tassert.Equal(t, expectedFragment, fragment, \"incorrect fragment %d\", i)\n\t}\n}\n\ntype fragmentChannel chan []byte\n\nfunc (ch fragmentChannel) newFragment(initial bool, checksum Checksum) (*writableFragment, error) {\n\twbuf := typed.NewWriteBuffer(make([]byte, testFragmentSize))\n\tfragment := new(writableFragment)\n\tfragment.flagsRef = wbuf.DeferByte()\n\twbuf.WriteSingleByte(byte(checksum.TypeCode()))\n\tfragment.checksumRef = wbuf.DeferBytes(checksum.Size())\n\tfragment.checksum = checksum\n\tfragment.contents = wbuf\n\treturn fragment, wbuf.Err()\n}\n\nfunc (ch fragmentChannel) flushFragment(fragment *writableFragment) error {\n\tvar buf bytes.Buffer\n\tfragment.contents.FlushTo(&buf)\n\tch <- buf.Bytes()\n\treturn nil\n}\n\nfunc (ch fragmentChannel) recvNextFragment(initial bool) (*readableFragment, error) {\n\trbuf := typed.NewReadBuffer(<-ch)\n\tfragment := new(readableFragment)\n\tfragment.onDone = func() {}\n\tfragment.flags = rbuf.ReadSingleByte()\n\tfragment.checksumType = ChecksumType(rbuf.ReadSingleByte())\n\tfragment.checksum = rbuf.ReadBytes(fragment.checksumType.ChecksumSize())\n\tfragment.contents = rbuf\n\treturn fragment, rbuf.Err()\n}\n\nfunc (ch fragmentChannel) doneReading(unexpected error) {}\nfunc (ch fragmentChannel) doneSending()                 {}\n\nfunc buffers(elements ...[][]byte) [][]byte {\n\tvar buffers [][]byte\n\tfor i := range elements {\n\t\tbuffers = append(buffers, bytes.Join(elements[i], []byte{}))\n\t}\n\n\treturn buffers\n}\n"
  },
  {
    "path": "fragmenting_reader.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\nvar (\n\terrMismatchedChecksumTypes  = errors.New(\"peer returned different checksum types between fragments\")\n\terrMismatchedChecksums      = errors.New(\"different checksums between peer and local\")\n\terrChunkExceedsFragmentSize = errors.New(\"peer chunk size exceeds remaining data in fragment\")\n\terrAlreadyReadingArgument   = errors.New(\"already reading argument\")\n\terrNotReadingArgument       = errors.New(\"not reading argument\")\n\terrMoreDataInArgument       = errors.New(\"closed argument reader when there is more data available to read\")\n\terrExpectedMoreArguments    = errors.New(\"closed argument reader when there may be more data available to read\")\n\terrNoMoreFragments          = errors.New(\"no more fragments\")\n)\n\ntype readableFragment struct {\n\tisDone       bool\n\tflags        byte\n\tchecksumType ChecksumType\n\tchecksum     []byte\n\tcontents     *typed.ReadBuffer\n\tonDone       func()\n}\n\nfunc (f *readableFragment) done() {\n\tif f.isDone {\n\t\treturn\n\t}\n\tf.onDone()\n\tf.isDone = true\n}\n\ntype fragmentReceiver interface {\n\t// recvNextFragment returns the next received fragment, blocking until\n\t// it's available or a deadline/cancel occurs\n\trecvNextFragment(intial bool) (*readableFragment, error)\n\n\t// doneReading is called when the fragment receiver is finished reading all fragments.\n\t// If an error frame is the last received frame, then doneReading is called with an error.\n\tdoneReading(unexpectedErr error)\n}\n\ntype fragmentingReadState int\n\nconst (\n\tfragmentingReadStart fragmentingReadState = iota\n\tfragmentingReadInArgument\n\tfragmentingReadInLastArgument\n\tfragmentingReadWaitingForArgument\n\tfragmentingReadComplete\n)\n\nfunc (s fragmentingReadState) isReadingArgument() bool {\n\treturn s == fragmentingReadInArgument || s == fragmentingReadInLastArgument\n}\n\ntype fragmentingReader struct {\n\tlogger           Logger\n\tstate            fragmentingReadState\n\tremainingChunks  [][]byte\n\tcurChunk         []byte\n\thasMoreFragments bool\n\treceiver         fragmentReceiver\n\tcurFragment      *readableFragment\n\tchecksum         Checksum\n\terr              error\n}\n\nfunc newFragmentingReader(logger Logger, receiver fragmentReceiver) *fragmentingReader {\n\treturn &fragmentingReader{\n\t\tlogger:           logger,\n\t\treceiver:         receiver,\n\t\thasMoreFragments: true,\n\t}\n}\n\n// The ArgReader will handle fragmentation as needed. Once the argument has\n// been read, the ArgReader must be closed.\nfunc (r *fragmentingReader) ArgReader(last bool) (ArgReader, error) {\n\tif err := r.BeginArgument(last); err != nil {\n\t\treturn nil, err\n\t}\n\treturn r, nil\n}\n\nfunc (r *fragmentingReader) BeginArgument(last bool) error {\n\tif r.err != nil {\n\t\treturn r.err\n\t}\n\n\tswitch {\n\tcase r.state.isReadingArgument():\n\t\tr.err = errAlreadyReadingArgument\n\t\treturn r.err\n\tcase r.state == fragmentingReadComplete:\n\t\tr.err = errComplete\n\t\treturn r.err\n\t}\n\n\t// We're guaranteed that either this is the first argument (in which\n\t// case we need to get the first fragment and chunk) or that we have a\n\t// valid curChunk (populated via Close)\n\tif r.state == fragmentingReadStart {\n\t\tif r.err = r.recvAndParseNextFragment(true); r.err != nil {\n\t\t\treturn r.err\n\t\t}\n\t}\n\n\tr.state = fragmentingReadInArgument\n\tif last {\n\t\tr.state = fragmentingReadInLastArgument\n\t}\n\treturn nil\n}\n\nfunc (r *fragmentingReader) Read(b []byte) (int, error) {\n\tif r.err != nil {\n\t\treturn 0, r.err\n\t}\n\n\tif !r.state.isReadingArgument() {\n\t\tr.err = errNotReadingArgument\n\t\treturn 0, r.err\n\t}\n\n\ttotalRead := 0\n\tfor {\n\t\t// Copy as much data as we can from the current chunk\n\t\tn := copy(b, r.curChunk)\n\t\ttotalRead += n\n\t\tr.curChunk = r.curChunk[n:]\n\t\tb = b[n:]\n\n\t\tif len(b) == 0 {\n\t\t\t// There was enough data in the current chunk to\n\t\t\t// satisfy the read.  Advance our place in the current\n\t\t\t// chunk and be done\n\t\t\treturn totalRead, nil\n\t\t}\n\n\t\t// There wasn't enough data in the current chunk to satisfy the\n\t\t// current read.  If there are more chunks in the current\n\t\t// fragment, then we've reach the end of this argument.  Return\n\t\t// an io.EOF so functions like ioutil.ReadFully know to finish\n\t\tif len(r.remainingChunks) > 0 {\n\t\t\treturn totalRead, io.EOF\n\t\t}\n\n\t\t// Try to fetch more fragments.  If there are no more\n\t\t// fragments, then we've reached the end of the argument\n\t\tif !r.hasMoreFragments {\n\t\t\treturn totalRead, io.EOF\n\t\t}\n\n\t\tif r.err = r.recvAndParseNextFragment(false); r.err != nil {\n\t\t\treturn totalRead, r.err\n\t\t}\n\t}\n}\n\nfunc (r *fragmentingReader) Close() error {\n\tlast := r.state == fragmentingReadInLastArgument\n\tif r.err != nil {\n\t\treturn r.err\n\t}\n\n\tif !r.state.isReadingArgument() {\n\t\tr.err = errNotReadingArgument\n\t\treturn r.err\n\t}\n\n\tif len(r.curChunk) > 0 {\n\t\t// There was more data remaining in the chunk\n\t\tr.err = errMoreDataInArgument\n\t\treturn r.err\n\t}\n\n\t// Several possibilities here:\n\t// 1. The caller thinks this is the last argument, but there are chunks in the current\n\t//    fragment or more fragments in this message\n\t//       - give them an error\n\t// 2. The caller thinks this is the last argument, and there are no more chunks and no more\n\t//    fragments\n\t//       - the stream is complete\n\t// 3. The caller thinks there are more arguments, and there are more chunks in this fragment\n\t//       - advance to the next chunk, this is the first chunk for the next argument\n\t// 4. The caller thinks there are more arguments, and there are no more chunks in this fragment,\n\t//    but there are more fragments in the message\n\t//       - retrieve the next fragment, confirm it has an empty chunk (indicating the end of the\n\t//         current argument), advance to the next check (which is the first chunk for the next arg)\n\t// 5. The caller thinks there are more arguments, but there are no more chunks or fragments available\n\t//      - give them an err\n\tif last {\n\t\tif len(r.remainingChunks) > 0 || r.hasMoreFragments {\n\t\t\t// We expect more arguments\n\t\t\tr.err = errExpectedMoreArguments\n\t\t\treturn r.err\n\t\t}\n\n\t\tr.doneReading(nil)\n\t\tr.curFragment.done()\n\t\tr.curChunk = nil\n\t\tr.state = fragmentingReadComplete\n\t\treturn nil\n\t}\n\n\tr.state = fragmentingReadWaitingForArgument\n\n\t// If there are more chunks in this fragment, advance to the next chunk.  This is the first chunk\n\t// for the next argument\n\tif len(r.remainingChunks) > 0 {\n\t\tr.curChunk, r.remainingChunks = r.remainingChunks[0], r.remainingChunks[1:]\n\t\treturn nil\n\t}\n\n\t// If there are no more chunks in this fragment, and no more fragments, we have an issue\n\tif !r.hasMoreFragments {\n\t\tr.err = errNoMoreFragments\n\t\treturn r.err\n\t}\n\n\t// There are no more chunks in this fragments, but more fragments - get the next fragment\n\tif r.err = r.recvAndParseNextFragment(false); r.err != nil {\n\t\treturn r.err\n\t}\n\n\treturn nil\n}\n\nfunc (r *fragmentingReader) recvAndParseNextFragment(initial bool) error {\n\tif r.err != nil {\n\t\treturn r.err\n\t}\n\n\tif r.curFragment != nil {\n\t\tr.curFragment.done()\n\t}\n\n\tr.curFragment, r.err = r.receiver.recvNextFragment(initial)\n\tif r.err != nil {\n\t\tif err, ok := r.err.(errorMessage); ok {\n\t\t\t// Serialized system errors are still reported (e.g. latency, trace reporting).\n\t\t\tr.err = err.AsSystemError()\n\t\t\tr.doneReading(r.err)\n\t\t}\n\t\treturn r.err\n\t}\n\n\t// Set checksum, or confirm new checksum is the same type as the prior checksum\n\tif r.checksum == nil {\n\t\tr.checksum = r.curFragment.checksumType.New()\n\t} else if r.checksum.TypeCode() != r.curFragment.checksumType {\n\t\treturn errMismatchedChecksumTypes\n\t}\n\n\t// Split fragment into underlying chunks\n\tr.hasMoreFragments = (r.curFragment.flags & hasMoreFragmentsFlag) == hasMoreFragmentsFlag\n\tr.remainingChunks = nil\n\tfor r.curFragment.contents.BytesRemaining() > 0 && r.curFragment.contents.Err() == nil {\n\t\tchunkSize := r.curFragment.contents.ReadUint16()\n\t\tif chunkSize > uint16(r.curFragment.contents.BytesRemaining()) {\n\t\t\treturn errChunkExceedsFragmentSize\n\t\t}\n\t\tchunkData := r.curFragment.contents.ReadBytes(int(chunkSize))\n\t\tr.remainingChunks = append(r.remainingChunks, chunkData)\n\t\tr.checksum.Add(chunkData)\n\t}\n\n\tif r.curFragment.contents.Err() != nil {\n\t\treturn r.curFragment.contents.Err()\n\t}\n\n\t// Validate checksums\n\tlocalChecksum := r.checksum.Sum()\n\tif bytes.Compare(r.curFragment.checksum, localChecksum) != 0 {\n\t\tr.err = errMismatchedChecksums\n\t\treturn r.err\n\t}\n\n\t// Pull out the first chunk to act as the current chunk\n\tr.curChunk, r.remainingChunks = r.remainingChunks[0], r.remainingChunks[1:]\n\treturn nil\n}\n\nfunc (r *fragmentingReader) doneReading(err error) {\n\tif r.checksum != nil {\n\t\tr.checksum.Release()\n\t}\n\tr.receiver.doneReading(err)\n}\n"
  },
  {
    "path": "fragmenting_writer.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\nvar (\n\terrAlreadyWritingArgument = errors.New(\"already writing argument\")\n\terrNotWritingArgument     = errors.New(\"not writing argument\")\n\terrComplete               = errors.New(\"last argument already sent\")\n)\n\nconst (\n\tchunkHeaderSize      = 2    // each chunk is a uint16\n\thasMoreFragmentsFlag = 0x01 // flags indicating there are more fragments coming\n)\n\n// A writableFragment is a fragment that can be written to, containing a buffer\n// for contents, a running checksum, and placeholders for the fragment flags\n// and final checksum value\ntype writableFragment struct {\n\tflagsRef    typed.ByteRef\n\tchecksumRef typed.BytesRef\n\tchecksum    Checksum\n\tcontents    *typed.WriteBuffer\n\tframe       *Frame\n}\n\n// finish finishes the fragment, updating the final checksum and fragment flags\nfunc (f *writableFragment) finish(hasMoreFragments bool) {\n\tf.checksumRef.Update(f.checksum.Sum())\n\tif hasMoreFragments {\n\t\t// Important: hasMoreFragmentsFlag is set if there are more fragments, but NOT CLEARED if there aren't.\n\t\t// This allows for callReqContinue frames to follow a fragmented callReq frame e.g. when arg2 is modified\n\t\t// by the relayer\n\t\tf.flagsRef.Update(hasMoreFragmentsFlag)\n\t} else {\n\t\tf.checksum.Release()\n\t}\n}\n\n// A writableChunk is a chunk of data within a fragment, representing the\n// contents of an argument within that fragment\ntype writableChunk struct {\n\tsize     uint16\n\tsizeRef  typed.Uint16Ref\n\tchecksum Checksum\n\tcontents *typed.WriteBuffer\n}\n\n// newWritableChunk creates a new writable chunk around a checksum and a buffer to hold data\nfunc newWritableChunk(checksum Checksum, contents *typed.WriteBuffer) *writableChunk {\n\treturn &writableChunk{\n\t\tsize:     0,\n\t\tsizeRef:  contents.DeferUint16(),\n\t\tchecksum: checksum,\n\t\tcontents: contents,\n\t}\n}\n\n// writeAsFits writes as many bytes from the given slice as fits into the chunk\nfunc (c *writableChunk) writeAsFits(b []byte) int {\n\tif len(b) > c.contents.BytesRemaining() {\n\t\tb = b[:c.contents.BytesRemaining()]\n\t}\n\n\tc.checksum.Add(b)\n\tc.contents.WriteBytes(b)\n\n\twritten := len(b)\n\tc.size += uint16(written)\n\treturn written\n}\n\n// finish finishes the chunk, updating its chunk size\nfunc (c *writableChunk) finish() {\n\tc.sizeRef.Update(c.size)\n}\n\n// A fragmentSender allocates and sends outbound fragments to a target\ntype fragmentSender interface {\n\t// newFragment allocates a new fragment\n\tnewFragment(initial bool, checksum Checksum) (*writableFragment, error)\n\n\t// flushFragment flushes the given fragment\n\tflushFragment(f *writableFragment) error\n\n\t// doneSending is called when the fragment receiver is finished sending all fragments.\n\tdoneSending()\n}\n\ntype fragmentingWriterState int\n\nconst (\n\tfragmentingWriteStart fragmentingWriterState = iota\n\tfragmentingWriteInArgument\n\tfragmentingWriteInLastArgument\n\tfragmentingWriteWaitingForArgument\n\tfragmentingWriteComplete\n)\n\nfunc (s fragmentingWriterState) isWritingArgument() bool {\n\treturn s == fragmentingWriteInArgument || s == fragmentingWriteInLastArgument\n}\n\n// A fragmentingWriter writes one or more arguments to an underlying stream,\n// breaking them into fragments as needed, and applying an overarching\n// checksum.  It relies on an underlying fragmentSender, which creates and\n// flushes the fragments as needed\ntype fragmentingWriter struct {\n\tlogger      Logger\n\tsender      fragmentSender\n\tchecksum    Checksum\n\tcurFragment *writableFragment\n\tcurChunk    *writableChunk\n\tstate       fragmentingWriterState\n\terr         error\n}\n\n// newFragmentingWriter creates a new fragmenting writer\nfunc newFragmentingWriter(logger Logger, sender fragmentSender, checksum Checksum) *fragmentingWriter {\n\treturn &fragmentingWriter{\n\t\tlogger:   logger,\n\t\tsender:   sender,\n\t\tchecksum: checksum,\n\t\tstate:    fragmentingWriteStart,\n\t}\n}\n\n// ArgWriter returns an ArgWriter to write an argument. The ArgWriter will handle\n// fragmentation as needed. Once the argument is written, the ArgWriter must be closed.\nfunc (w *fragmentingWriter) ArgWriter(last bool) (ArgWriter, error) {\n\tif err := w.BeginArgument(last); err != nil {\n\t\treturn nil, err\n\t}\n\treturn w, nil\n}\n\n// BeginArgument tells the writer that the caller is starting a new argument.\n// Must not be called while an existing argument is in place\nfunc (w *fragmentingWriter) BeginArgument(last bool) error {\n\tif w.err != nil {\n\t\treturn w.err\n\t}\n\n\tswitch {\n\tcase w.state == fragmentingWriteComplete:\n\t\tw.err = errComplete\n\t\treturn w.err\n\tcase w.state.isWritingArgument():\n\t\tw.err = errAlreadyWritingArgument\n\t\treturn w.err\n\t}\n\n\t// If we don't have a fragment, request one\n\tif w.curFragment == nil {\n\t\tinitial := w.state == fragmentingWriteStart\n\t\tif w.curFragment, w.err = w.sender.newFragment(initial, w.checksum); w.err != nil {\n\t\t\treturn w.err\n\t\t}\n\t}\n\n\t// If there's no room in the current fragment, freak out.  This will\n\t// only happen due to an implementation error in the TChannel stack\n\t// itself\n\tif w.curFragment.contents.BytesRemaining() <= chunkHeaderSize {\n\t\tpanic(fmt.Errorf(\"attempting to begin an argument in a fragment with only %d bytes available\",\n\t\t\tw.curFragment.contents.BytesRemaining()))\n\t}\n\n\tw.curChunk = newWritableChunk(w.checksum, w.curFragment.contents)\n\tw.state = fragmentingWriteInArgument\n\tif last {\n\t\tw.state = fragmentingWriteInLastArgument\n\t}\n\treturn nil\n}\n\n// Write writes argument data, breaking it into fragments as needed\nfunc (w *fragmentingWriter) Write(b []byte) (int, error) {\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\n\tif !w.state.isWritingArgument() {\n\t\tw.err = errNotWritingArgument\n\t\treturn 0, w.err\n\t}\n\n\ttotalWritten := 0\n\tfor {\n\t\tbytesWritten := w.curChunk.writeAsFits(b)\n\t\ttotalWritten += bytesWritten\n\t\tif bytesWritten == len(b) {\n\t\t\t// The whole thing fit, we're done\n\t\t\treturn totalWritten, nil\n\t\t}\n\n\t\t// There was more data than fit into the fragment, so flush the current fragment,\n\t\t// start a new fragment and chunk, and continue writing\n\t\tif w.err = w.Flush(); w.err != nil {\n\t\t\treturn totalWritten, w.err\n\t\t}\n\n\t\tb = b[bytesWritten:]\n\t}\n}\n\n// Flush flushes the current fragment, and starts a new fragment and chunk.\nfunc (w *fragmentingWriter) Flush() error {\n\tw.curChunk.finish()\n\tw.curFragment.finish(true)\n\tif w.err = w.sender.flushFragment(w.curFragment); w.err != nil {\n\t\treturn w.err\n\t}\n\n\tif w.curFragment, w.err = w.sender.newFragment(false, w.checksum); w.err != nil {\n\t\treturn w.err\n\t}\n\n\tw.curChunk = newWritableChunk(w.checksum, w.curFragment.contents)\n\treturn nil\n}\n\n// Close ends the current argument.\nfunc (w *fragmentingWriter) Close() error {\n\tlast := w.state == fragmentingWriteInLastArgument\n\tif w.err != nil {\n\t\treturn w.err\n\t}\n\n\tif !w.state.isWritingArgument() {\n\t\tw.err = errNotWritingArgument\n\t\treturn w.err\n\t}\n\n\tw.curChunk.finish()\n\n\t// There are three possibilities here:\n\t// 1. There are no more arguments\n\t//      flush with more_fragments=false, mark the stream as complete\n\t// 2. There are more arguments, but we can't fit more data into this fragment\n\t//      flush with more_fragments=true, start new fragment, write empty chunk to indicate\n\t//      the current argument is complete\n\t// 3. There are more arguments, and we can fit more data into this fragment\n\t//      update the chunk but leave the current fragment open\n\tif last {\n\t\t// No more arguments - flush this final fragment and mark ourselves complete\n\t\tw.state = fragmentingWriteComplete\n\t\tw.curFragment.finish(false)\n\t\tw.err = w.sender.flushFragment(w.curFragment)\n\t\tw.sender.doneSending()\n\t\treturn w.err\n\t}\n\n\tw.state = fragmentingWriteWaitingForArgument\n\tif w.curFragment.contents.BytesRemaining() > chunkHeaderSize {\n\t\t// There's enough room in this fragment for the next argument's\n\t\t// initial chunk, so we're done here\n\t\treturn nil\n\t}\n\n\t// This fragment is full - flush and prepare for another argument\n\tw.curFragment.finish(true)\n\tif w.err = w.sender.flushFragment(w.curFragment); w.err != nil {\n\t\treturn w.err\n\t}\n\n\tif w.curFragment, w.err = w.sender.newFragment(false, w.checksum); w.err != nil {\n\t\treturn w.err\n\t}\n\n\t// Write an empty chunk to indicate this argument has ended\n\tw.curFragment.contents.WriteUint16(0)\n\treturn nil\n}\n"
  },
  {
    "path": "frame.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\nconst (\n\t// MaxFrameSize is the total maximum size for a frame\n\tMaxFrameSize = math.MaxUint16\n\n\t// FrameHeaderSize is the size of the header element for a frame\n\tFrameHeaderSize = 16\n\n\t// MaxFramePayloadSize is the maximum size of the payload for a single frame\n\tMaxFramePayloadSize = MaxFrameSize - FrameHeaderSize\n)\n\n// FrameHeader is the header for a frame, containing the MessageType and size\ntype FrameHeader struct {\n\t// The size of the frame including the header\n\tsize uint16\n\n\t// The type of message represented by the frame\n\tmessageType messageType\n\n\t// Left empty\n\treserved1 byte\n\n\t// The id of the message represented by the frame\n\tID uint32\n\n\t// Left empty\n\treserved [8]byte\n}\n\n// SetPayloadSize sets the size of the frame payload\nfunc (fh *FrameHeader) SetPayloadSize(size uint16) {\n\tfh.size = size + FrameHeaderSize\n}\n\n// PayloadSize returns the size of the frame payload\nfunc (fh FrameHeader) PayloadSize() uint16 {\n\treturn fh.size - FrameHeaderSize\n}\n\n// FrameSize returns the total size of the frame\nfunc (fh FrameHeader) FrameSize() uint16 {\n\treturn fh.size\n}\n\n// MessageType returns the type of the message\nfunc (fh FrameHeader) MessageType() byte {\n\treturn byte(fh.messageType)\n}\n\nfunc (fh FrameHeader) String() string { return fmt.Sprintf(\"%v[%d]\", fh.messageType, fh.ID) }\n\n// MarshalJSON returns a `{\"id\":NNN, \"msgType\":MMM, \"size\":SSS}` representation\nfunc (fh FrameHeader) MarshalJSON() ([]byte, error) {\n\ts := struct {\n\t\tID      uint32      `json:\"id\"`\n\t\tMsgType messageType `json:\"msgType\"`\n\t\tSize    uint16      `json:\"size\"`\n\t}{fh.ID, fh.messageType, fh.size}\n\treturn json.Marshal(s)\n}\n\nfunc (fh *FrameHeader) read(r *typed.ReadBuffer) error {\n\tfh.size = r.ReadUint16()\n\tfh.messageType = messageType(r.ReadSingleByte())\n\tfh.reserved1 = r.ReadSingleByte()\n\tfh.ID = r.ReadUint32()\n\tr.ReadBytes(len(fh.reserved))\n\treturn r.Err()\n}\n\nfunc (fh *FrameHeader) write(w *typed.WriteBuffer) error {\n\tw.WriteUint16(fh.size)\n\tw.WriteSingleByte(byte(fh.messageType))\n\tw.WriteSingleByte(fh.reserved1)\n\tw.WriteUint32(fh.ID)\n\tw.WriteBytes(fh.reserved[:])\n\treturn w.Err()\n}\n\n// A Frame is a header and payload\ntype Frame struct {\n\tbuffer       []byte // full buffer, including payload and header\n\theaderBuffer []byte // slice referencing just the header\n\n\t// The header for the frame\n\tHeader FrameHeader\n\n\t// The payload for the frame\n\tPayload []byte\n}\n\n// NewFrame allocates a new frame with the given payload capacity\nfunc NewFrame(payloadCapacity int) *Frame {\n\tf := &Frame{}\n\tf.buffer = make([]byte, payloadCapacity+FrameHeaderSize)\n\tf.Payload = f.buffer[FrameHeaderSize:]\n\tf.headerBuffer = f.buffer[:FrameHeaderSize]\n\treturn f\n}\n\n// ReadBody takes in a previously read frame header, and only reads in the body\n// based on the size specified in the header. This allows callers to defer\n// the frame allocation till the body needs to be read.\nfunc (f *Frame) ReadBody(header []byte, r io.Reader) error {\n\t// Copy the header into the underlying buffer so we have an assembled frame\n\t// that can be directly forwarded.\n\tcopy(f.buffer, header)\n\n\t// Parse the header into our typed struct.\n\tif err := f.Header.read(typed.NewReadBuffer(header)); err != nil {\n\t\treturn err\n\t}\n\n\tswitch payloadSize := f.Header.PayloadSize(); {\n\tcase payloadSize > MaxFramePayloadSize:\n\t\treturn fmt.Errorf(\"invalid frame size %v\", f.Header.size)\n\tcase payloadSize > 0:\n\t\t_, err := io.ReadFull(r, f.SizedPayload())\n\t\treturn err\n\tdefault:\n\t\t// No payload to read\n\t\treturn nil\n\t}\n}\n\n// ReadIn reads the frame from the given io.Reader.\n// Deprecated: Only maintained for backwards compatibility. Callers should\n// use ReadBody instead.\nfunc (f *Frame) ReadIn(r io.Reader) error {\n\theader := make([]byte, FrameHeaderSize)\n\tif _, err := io.ReadFull(r, header); err != nil {\n\t\treturn err\n\t}\n\n\treturn f.ReadBody(header, r)\n}\n\n// WriteOut writes the frame to the given io.Writer\nfunc (f *Frame) WriteOut(w io.Writer) error {\n\tvar wbuf typed.WriteBuffer\n\twbuf.Wrap(f.headerBuffer)\n\n\tif err := f.Header.write(&wbuf); err != nil {\n\t\treturn err\n\t}\n\n\tfullFrame := f.buffer[:f.Header.FrameSize()]\n\tif _, err := w.Write(fullFrame); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// SizedPayload returns the slice of the payload actually used, as defined by the header\nfunc (f *Frame) SizedPayload() []byte {\n\treturn f.Payload[:f.Header.PayloadSize()]\n}\n\n// messageType returns the message type.\nfunc (f *Frame) messageType() messageType {\n\treturn f.Header.messageType\n}\n\nfunc (f *Frame) write(msg message) error {\n\tvar wbuf typed.WriteBuffer\n\twbuf.Wrap(f.Payload[:])\n\tif err := msg.write(&wbuf); err != nil {\n\t\treturn err\n\t}\n\n\tf.Header.ID = msg.ID()\n\tf.Header.reserved1 = 0\n\tf.Header.messageType = msg.messageType()\n\tf.Header.SetPayloadSize(uint16(wbuf.BytesWritten()))\n\treturn nil\n}\n\nfunc (f *Frame) read(msg message) error {\n\tvar rbuf typed.ReadBuffer\n\trbuf.Wrap(f.SizedPayload())\n\treturn msg.read(&rbuf)\n}\n"
  },
  {
    "path": "frame_pool.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport \"sync\"\n\n// A FramePool is a pool for managing and re-using frames\ntype FramePool interface {\n\t// Retrieves a new frame from the pool\n\tGet() *Frame\n\n\t// Releases a frame back to the pool\n\tRelease(f *Frame)\n}\n\n// DefaultFramePool uses the SyncFramePool.\nvar DefaultFramePool = NewSyncFramePool()\n\n// DisabledFramePool is a pool that uses the heap and relies on GC.\nvar DisabledFramePool = disabledFramePool{}\n\ntype disabledFramePool struct{}\n\nfunc (p disabledFramePool) Get() *Frame      { return NewFrame(MaxFramePayloadSize) }\nfunc (p disabledFramePool) Release(f *Frame) {}\n\ntype syncFramePool struct {\n\tpool *sync.Pool\n}\n\n// NewSyncFramePool returns a frame pool that uses a sync.Pool.\nfunc NewSyncFramePool() FramePool {\n\treturn &syncFramePool{\n\t\tpool: &sync.Pool{New: func() interface{} { return NewFrame(MaxFramePayloadSize) }},\n\t}\n}\n\nfunc (p syncFramePool) Get() *Frame {\n\treturn p.pool.Get().(*Frame)\n}\n\nfunc (p syncFramePool) Release(f *Frame) {\n\tp.pool.Put(f)\n}\n\ntype channelFramePool chan *Frame\n\n// NewChannelFramePool returns a frame pool backed by a channel that has a max capacity.\nfunc NewChannelFramePool(capacity int) FramePool {\n\treturn channelFramePool(make(chan *Frame, capacity))\n}\n\nfunc (c channelFramePool) Get() *Frame {\n\tselect {\n\tcase frame := <-c:\n\t\treturn frame\n\tdefault:\n\t\treturn NewFrame(MaxFramePayloadSize)\n\t}\n}\n\nfunc (c channelFramePool) Release(f *Frame) {\n\tselect {\n\tcase c <- f:\n\tdefault:\n\t\t// Too many frames in the channel, discard it.\n\t}\n}\n"
  },
  {
    "path": "frame_pool_b_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"math/rand\"\n\t\"sync\"\n\t\"testing\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"go.uber.org/atomic\"\n)\n\nfunc benchmarkUsing(b *testing.B, pool FramePool) {\n\tconst numGoroutines = 1000\n\tconst maxHoldFrames = 1000\n\n\tvar gotFrames atomic.Uint64\n\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < numGoroutines; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\n\t\t\tfor {\n\t\t\t\tif gotFrames.Load() > uint64(b.N) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tframesToHold := rand.Intn(maxHoldFrames)\n\t\t\t\tgotFrames.Add(uint64(framesToHold))\n\n\t\t\t\tframes := make([]*Frame, framesToHold)\n\t\t\t\tfor i := 0; i < framesToHold; i++ {\n\t\t\t\t\tframes[i] = pool.Get()\n\t\t\t\t}\n\n\t\t\t\tfor i := 0; i < framesToHold; i++ {\n\t\t\t\t\tpool.Release(frames[i])\n\t\t\t\t}\n\t\t\t}\n\n\t\t\twg.Done()\n\t\t}()\n\t}\n\n\twg.Wait()\n}\n\nfunc BenchmarkFramePoolDisabled(b *testing.B) {\n\tbenchmarkUsing(b, DisabledFramePool)\n}\n\nfunc BenchmarkFramePoolSync(b *testing.B) {\n\tbenchmarkUsing(b, NewSyncFramePool())\n}\n\nfunc BenchmarkFramePoolChannel1000(b *testing.B) {\n\tbenchmarkUsing(b, NewChannelFramePool(1000))\n}\n\nfunc BenchmarkFramePoolChannel10000(b *testing.B) {\n\tbenchmarkUsing(b, NewChannelFramePool(10000))\n}\n"
  },
  {
    "path": "frame_pool_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\n// This file contains functions for tests to access internal tchannel state.\n// Since it has a _test.go suffix, it is only compiled with tests in this package.\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"math/rand\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\t\"github.com/uber/tchannel-go/testutils/testreader\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\ntype swapper struct {\n\tt testing.TB\n}\n\nfunc (s *swapper) OnError(ctx context.Context, err error) {\n\ts.t.Errorf(\"OnError: %v\", err)\n}\n\nfunc (*swapper) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\treturn &raw.Res{\n\t\tArg2: args.Arg3,\n\t\tArg3: args.Arg2,\n\t}, nil\n}\n\nfunc doPingAndCall(t testing.TB, clientCh *Channel, hostPort string) {\n\tctx, cancel := NewContext(time.Second * 5)\n\tdefer cancel()\n\n\trequire.NoError(t, clientCh.Ping(ctx, hostPort))\n\n\tconst maxRandArg = 512 * 1024\n\n\targ2 := testutils.RandBytes(rand.Intn(maxRandArg))\n\targ3 := testutils.RandBytes(rand.Intn(maxRandArg))\n\tresArg2, resArg3, _, err := raw.Call(ctx, clientCh, hostPort, \"swap-server\", \"swap\", arg2, arg3)\n\tif !assert.NoError(t, err, \"error during sendRecv\") {\n\t\treturn\n\t}\n\n\t// We expect the arguments to be swapped.\n\tif bytes.Compare(arg3, resArg2) != 0 {\n\t\tt.Errorf(\"returned arg2 does not match expected:\\n  got %v\\n want %v\", resArg2, arg3)\n\t}\n\tif bytes.Compare(arg2, resArg3) != 0 {\n\t\tt.Errorf(\"returned arg2 does not match expected:\\n  got %v\\n want %v\", resArg3, arg2)\n\t}\n}\n\nfunc doErrorCall(t testing.TB, clientCh *Channel, hostPort string) {\n\tctx, cancel := NewContext(time.Second * 5)\n\tdefer cancel()\n\n\t_, _, _, err := raw.Call(ctx, clientCh, hostPort, \"swap-server\", \"non-existent\", nil, nil)\n\tassert.Error(t, err, \"Call to non-existent endpoint should fail\")\n\tassert.Equal(t, ErrCodeBadRequest, GetSystemErrorCode(err), \"Error code mismatch\")\n}\n\nfunc TestFramesReleased(t *testing.T) {\n\tCheckStress(t)\n\n\tdefer testutils.SetTimeout(t, 30*time.Second)()\n\tconst (\n\t\trequestsPerGoroutine = 10\n\t\tnumGoroutines        = 10\n\t)\n\n\tpool := NewCheckedFramePoolForTest()\n\topts := testutils.NewOpts().\n\t\tSetServiceName(\"swap-server\").\n\t\tSetFramePool(pool).\n\t\tAddLogFilter(\"Couldn't find handler.\", 2*numGoroutines*requestsPerGoroutine)\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(raw.Wrap(&swapper{t}), \"swap\")\n\n\t\tclientOpts := testutils.NewOpts().SetFramePool(pool)\n\t\tclientCh := ts.NewClient(clientOpts)\n\n\t\t// Create an active connection that can be shared by the goroutines by calling Ping.\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\t\trequire.NoError(t, clientCh.Ping(ctx, ts.HostPort()))\n\n\t\tvar wg sync.WaitGroup\n\t\tfor i := 0; i < numGoroutines; i++ {\n\t\t\twg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\tfor i := 0; i < requestsPerGoroutine; i++ {\n\t\t\t\t\tdoPingAndCall(t, clientCh, ts.HostPort())\n\t\t\t\t\tdoErrorCall(t, clientCh, ts.HostPort())\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\twg.Wait()\n\t})\n\n\tCheckFramePoolIsEmpty(t, pool)\n}\n\ntype dirtyFramePool struct{}\n\nfunc (p dirtyFramePool) Get() *Frame {\n\tf := NewFrame(MaxFramePayloadSize)\n\treader := testreader.Looper([]byte{^byte(0)})\n\tio.ReadFull(reader, f.Payload)\n\treturn f\n}\n\nfunc (p dirtyFramePool) Release(f *Frame) {}\n\nfunc TestDirtyFrameRequests(t *testing.T) {\n\targSizes := []int{25000, 50000, 75000}\n\n\t// Create the largest required random cache.\n\ttestutils.RandBytes(argSizes[len(argSizes)-1])\n\n\topts := testutils.NewOpts().\n\t\tSetServiceName(\"swap-server\").\n\t\tSetFramePool(dirtyFramePool{})\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(raw.Wrap(&swapper{t}), \"swap\")\n\n\t\tfor _, argSize := range argSizes {\n\t\t\tctx, cancel := NewContext(time.Second)\n\t\t\tdefer cancel()\n\n\t\t\targ2, arg3 := testutils.RandBytes(argSize), testutils.RandBytes(argSize)\n\t\t\tres2, res3, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.Server().ServiceName(), \"swap\", arg2, arg3)\n\t\t\tif assert.NoError(t, err, \"Call failed\") {\n\t\t\t\tassert.Equal(t, arg2, res3, \"Result arg3 wrong\")\n\t\t\t\tassert.Equal(t, arg3, res2, \"Result arg3 wrong\")\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "frame_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"math\"\n\t\"testing\"\n\t\"testing/iotest\"\n\t\"testing/quick\"\n\n\t\"github.com/uber/tchannel-go/testutils/testreader\"\n\t\"github.com/uber/tchannel-go/typed\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc fakeHeader(t messageType) FrameHeader {\n\treturn FrameHeader{\n\t\tsize:        uint16(0xFF34),\n\t\tmessageType: t,\n\t\tID:          0xDEADBEEF,\n\t}\n}\n\nfunc TestFrameHeaderJSON(t *testing.T) {\n\tfh := fakeHeader(messageTypeCallReq)\n\tlogged, err := json.Marshal(fh)\n\tassert.NoError(t, err, \"FrameHeader can't be marshalled to JSON\")\n\tassert.Equal(\n\t\tt,\n\t\tstring(logged),\n\t\t`{\"id\":3735928559,\"msgType\":3,\"size\":65332}`,\n\t\t\"FrameHeader didn't marshal to JSON as expected\",\n\t)\n}\n\nfunc TestFraming(t *testing.T) {\n\tfh := fakeHeader(messageTypeCallReq)\n\twbuf := typed.NewWriteBufferWithSize(1024)\n\trequire.Nil(t, fh.write(wbuf))\n\n\tvar b bytes.Buffer\n\tif _, err := wbuf.FlushTo(&b); err != nil {\n\t\trequire.Nil(t, err)\n\t}\n\n\trbuf := typed.NewReadBuffer(b.Bytes())\n\n\tvar fh2 FrameHeader\n\trequire.Nil(t, fh2.read(rbuf))\n\n\tassert.Equal(t, fh, fh2)\n}\n\nfunc TestPartialRead(t *testing.T) {\n\tf := NewFrame(MaxFramePayloadSize)\n\tf.Header.size = FrameHeaderSize + 2134\n\tf.Header.messageType = messageTypeCallReq\n\tf.Header.ID = 0xDEADBEED\n\n\t// We set the full payload but only the first 2134 bytes should be written.\n\tfor i := 0; i < len(f.Payload); i++ {\n\t\tval := (i * 37) % 256\n\t\tf.Payload[i] = byte(val)\n\t}\n\tbuf := &bytes.Buffer{}\n\trequire.NoError(t, f.WriteOut(buf))\n\tassert.Equal(t, f.Header.size, uint16(buf.Len()), \"frame size should match written bytes\")\n\n\t// Read the data back, from a reader that fragments.\n\tf2 := NewFrame(MaxFramePayloadSize)\n\trequire.NoError(t, f2.ReadIn(iotest.OneByteReader(buf)))\n\n\t// Ensure header and payload are the same.\n\trequire.Equal(t, f.Header, f2.Header, \"frame headers don't match\")\n\trequire.Equal(t, f.SizedPayload(), f2.SizedPayload(), \"payload does not match\")\n}\n\nfunc TestFrameReadShortFrame(t *testing.T) {\n\theaderFull := make([]byte, FrameHeaderSize)\n\theaderFull[1] = FrameHeaderSize + 1 // give the frame a non-zero size.\n\tbody := []byte{1}\n\n\tf := NewFrame(MaxFramePayloadSize)\n\terr := f.ReadBody(headerFull, bytes.NewReader(body))\n\trequire.NoError(t, err, \"Should not fail to read full frame header\")\n\n\tfor i := 0; i < FrameHeaderSize; i++ {\n\t\tpartialHeader := headerFull[:i]\n\n\t\tf := NewFrame(MaxFramePayloadSize)\n\t\terr := f.ReadBody(partialHeader, bytes.NewReader(body))\n\t\tassert.Equal(t, typed.ErrEOF, err, \"Expected short header to fail\")\n\t}\n}\n\nfunc TestEmptyPayload(t *testing.T) {\n\tf := NewFrame(MaxFramePayloadSize)\n\tm := &pingRes{id: 1}\n\trequire.NoError(t, f.write(m))\n\n\t// Write out the frame.\n\tbuf := &bytes.Buffer{}\n\trequire.NoError(t, f.WriteOut(buf))\n\tassert.Equal(t, FrameHeaderSize, buf.Len())\n\n\t// Read the frame from the buffer.\n\t// net.Conn returns io.EOF if you try to read 0 bytes at the end.\n\t// This is also simulated by the LimitedReader so we use that here.\n\trequire.NoError(t, f.ReadIn(&io.LimitedReader{R: buf, N: FrameHeaderSize}))\n}\n\nfunc TestReservedBytes(t *testing.T) {\n\t// Set up a frame with non-zero values\n\tf := NewFrame(MaxFramePayloadSize)\n\treader := testreader.Looper([]byte{^byte(0)})\n\tio.ReadFull(reader, f.Payload)\n\tf.Header.read(typed.NewReadBuffer(f.Payload))\n\n\tm := &pingRes{id: 1}\n\tf.write(m)\n\n\tbuf := &bytes.Buffer{}\n\tf.WriteOut(buf)\n\tassert.Equal(t,\n\t\t[]byte{\n\t\t\t0x0, 0x10, // size\n\t\t\t0xd1,               // type\n\t\t\t0x0,                // reserved should always be 0\n\t\t\t0x0, 0x0, 0x0, 0x1, // id\n\t\t\t0x0, 0x0, 0x0, 0x0, // reserved should always be 0\n\t\t\t0x0, 0x0, 0x0, 0x0, // reserved should always be 0\n\t\t},\n\t\tbuf.Bytes(), \"Unexpected bytes\")\n}\n\nfunc TestMessageType(t *testing.T) {\n\tframe := NewFrame(MaxFramePayloadSize)\n\terr := frame.write(&callReq{Service: \"foo\"})\n\trequire.NoError(t, err, \"Error writing message to frame.\")\n\tassert.Equal(t, messageTypeCallReq, frame.messageType(), \"Failed to read message type from frame.\")\n}\n\nfunc TestFrameReadIn(t *testing.T) {\n\tmaxPayload := bytes.Repeat([]byte{1}, MaxFramePayloadSize)\n\ttests := []struct {\n\t\tmsg              string\n\t\tbs               []byte\n\t\twantFrameHeader  FrameHeader\n\t\twantFramePayload []byte\n\t\twantErr          string\n\t}{\n\t\t{\n\t\t\tmsg: \"frame with no payload\",\n\t\t\tbs: []byte{\n\t\t\t\t0, 16 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */\n\t\t\t\t9, 8, 7, 6, 5, 4, 3, 2, // reserved\n\t\t\t},\n\t\t\twantFrameHeader: FrameHeader{\n\t\t\t\tsize:        16,\n\t\t\t\tmessageType: 1,\n\t\t\t\treserved1:   2,\n\t\t\t\tID:          3,\n\t\t\t\t// reserved:    [8]byte{9, 8, 7, 6, 5, 4, 3, 2}, // currently ignored.\n\t\t\t},\n\t\t\twantFramePayload: []byte{},\n\t\t},\n\t\t{\n\t\t\tmsg: \"frame with small payload\",\n\t\t\tbs: []byte{\n\t\t\t\t0, 18 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */\n\t\t\t\t9, 8, 7, 6, 5, 4, 3, 2, // reserved\n\t\t\t\t100, 200, // payload\n\t\t\t},\n\t\t\twantFrameHeader: FrameHeader{\n\t\t\t\tsize:        18,\n\t\t\t\tmessageType: 1,\n\t\t\t\treserved1:   2,\n\t\t\t\tID:          3,\n\t\t\t\t// reserved:    [8]byte{9, 8, 7, 6, 5, 4, 3, 2}, // currently ignored.\n\t\t\t},\n\t\t\twantFramePayload: []byte{100, 200},\n\t\t},\n\t\t{\n\t\t\tmsg: \"frame with max size\",\n\t\t\tbs: append([]byte{\n\t\t\t\tmath.MaxUint8, math.MaxUint8 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */\n\t\t\t\t9, 8, 7, 6, 5, 4, 3, 2, // reserved\n\t\t\t}, maxPayload...),\n\t\t\twantFrameHeader: FrameHeader{\n\t\t\t\tsize:        math.MaxUint16,\n\t\t\t\tmessageType: 1,\n\t\t\t\treserved1:   2,\n\t\t\t\tID:          3,\n\t\t\t\t// currently ignored.\n\t\t\t\t// reserved:    [8]byte{9, 8, 7, 6, 5, 4, 3, 2},\n\t\t\t},\n\t\t\twantFramePayload: maxPayload,\n\t\t},\n\t\t{\n\t\t\tmsg: \"frame with 0 size\",\n\t\t\tbs: []byte{\n\t\t\t\t0, 0 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */\n\t\t\t\t9, 8, 7, 6, 5, 4, 3, 2, // reserved\n\t\t\t},\n\t\t\twantErr: \"invalid frame size 0\",\n\t\t},\n\t\t{\n\t\t\tmsg: \"frame with size < HeaderSize\",\n\t\t\tbs: []byte{\n\t\t\t\t0, 15 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */\n\t\t\t\t9, 8, 7, 6, 5, 4, 3, 2, // reserved\n\t\t\t},\n\t\t\twantErr: \"invalid frame size 15\",\n\t\t},\n\t\t{\n\t\t\tmsg: \"frame with partial header\",\n\t\t\tbs: []byte{\n\t\t\t\t0, 16 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */\n\t\t\t\t// missing reserved bytes\n\t\t\t},\n\t\t\twantErr: \"unexpected EOF\",\n\t\t},\n\t\t{\n\t\t\tmsg: \"frame with partial payload\",\n\t\t\tbs: []byte{\n\t\t\t\t0, 24 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */\n\t\t\t\t9, 8, 7, 6, 5, 4, 3, 2, // reserved\n\t\t\t\t1, 2, // partial payload\n\t\t\t},\n\t\t\twantErr: \"unexpected EOF\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tf := DefaultFramePool.Get()\n\t\tr := bytes.NewReader(tt.bs)\n\t\terr := f.ReadIn(r)\n\t\tif tt.wantErr != \"\" {\n\t\t\trequire.Error(t, err, tt.msg)\n\t\t\tassert.Contains(t, err.Error(), tt.wantErr, tt.msg)\n\t\t\tcontinue\n\t\t}\n\n\t\trequire.NoError(t, err, tt.msg)\n\t\tassert.Equal(t, tt.wantFrameHeader, f.Header, \"%v: header mismatch\", tt.msg)\n\t\tassert.Equal(t, tt.wantFramePayload, f.SizedPayload(), \"%v: unexpected payload\")\n\t}\n}\n\nfunc frameReadIn(bs []byte) (decoded bool) {\n\tframe := DefaultFramePool.Get()\n\tdefer DefaultFramePool.Release(frame)\n\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tdecoded = false\n\t\t}\n\t}()\n\tframe.ReadIn(bytes.NewReader(bs))\n\treturn true\n}\n\nfunc TestQuickFrameReadIn(t *testing.T) {\n\t// Try to read any set of bytes as a frame.\n\terr := quick.Check(frameReadIn, &quick.Config{MaxCount: 10000})\n\trequire.NoError(t, err, \"Failed to fuzz test ReadIn\")\n\n\t// Limit the search space to just headers.\n\terr = quick.Check(func(size uint16, t byte, id uint32) bool {\n\t\tbs := make([]byte, FrameHeaderSize)\n\t\tbinary.BigEndian.PutUint16(bs[0:2], size)\n\t\tbs[2] = t\n\t\tbinary.BigEndian.PutUint32(bs[4:8], id)\n\t\treturn frameReadIn(bs)\n\t}, &quick.Config{MaxCount: 10000})\n\trequire.NoError(t, err, \"Failed to fuzz test ReadIn\")\n}\n"
  },
  {
    "path": "frame_utils_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"unsafe\"\n\n\t\"github.com/prashantv/protectmem\"\n)\n\ntype protectMemAllocs struct {\n\tframeAlloc  *protectmem.Allocation\n\tbufferAlloc *protectmem.Allocation\n}\n\ntype ProtectMemFramePool struct {\n\tsync.Mutex\n\n\tallocations map[*Frame]protectMemAllocs\n}\n\n// NewProtectMemFramePool creates a frame pool that ensures that released frames\n// are not reused by removing all access to a frame once it's been released.\nfunc NewProtectMemFramePool() FramePool {\n\treturn &ProtectMemFramePool{\n\t\tallocations: make(map[*Frame]protectMemAllocs),\n\t}\n}\nfunc (p *ProtectMemFramePool) Get() *Frame {\n\tframeAlloc := protectmem.Allocate(unsafe.Sizeof(Frame{}))\n\tf := (*Frame)(frameAlloc.Ptr())\n\n\tbufferAlloc := protectmem.AllocateSlice(&f.buffer, MaxFramePayloadSize)\n\tf.buffer = f.buffer[:MaxFramePayloadSize]\n\tf.Payload = f.buffer[FrameHeaderSize:]\n\tf.headerBuffer = f.buffer[:FrameHeaderSize]\n\n\tp.Lock()\n\tp.allocations[f] = protectMemAllocs{\n\t\tframeAlloc:  frameAlloc,\n\t\tbufferAlloc: bufferAlloc,\n\t}\n\tp.Unlock()\n\n\treturn f\n}\n\nfunc (p *ProtectMemFramePool) Release(f *Frame) {\n\tp.Lock()\n\tallocs, ok := p.allocations[f]\n\tdelete(p.allocations, f)\n\tp.Unlock()\n\n\tif !ok {\n\t\tpanic(fmt.Errorf(\"released frame that was not allocated by pool: %v\", f.Header))\n\t}\n\n\tallocs.bufferAlloc.Protect(protectmem.None)\n\tallocs.frameAlloc.Protect(protectmem.None)\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/uber/tchannel-go\n\ngo 1.21\n\nrequire (\n\tgithub.com/HdrHistogram/hdrhistogram-go v0.9.0 // indirect\n\tgithub.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b\n\tgithub.com/cactus/go-statsd-client/statsd v0.0.0-20190922033735-5ca90424ceb7\n\tgithub.com/crossdock/crossdock-go v0.0.0-20160816171116-049aabb0122b\n\tgithub.com/jessevdk/go-flags v1.4.0\n\tgithub.com/opentracing/opentracing-go v1.1.0\n\tgithub.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a\n\tgithub.com/samuel/go-thrift v0.0.0-20190219015601-e8b6b52668fe\n\tgithub.com/streadway/quantile v0.0.0-20220407130108-4246515d968d\n\tgithub.com/stretchr/testify v1.5.1\n\tgithub.com/uber-go/tally v3.3.15+incompatible\n\tgithub.com/uber/jaeger-client-go v2.22.1+incompatible\n\tgo.uber.org/atomic v1.6.0\n\tgo.uber.org/multierr v1.2.0\n\tgolang.org/x/net v0.14.0\n\tgolang.org/x/sys v0.11.0\n\tgopkg.in/yaml.v2 v2.4.0\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/stretchr/objx v0.3.0 // indirect\n\tgithub.com/uber/jaeger-lib v2.4.1+incompatible // indirect\n\tgolang.org/x/tools v0.1.12 // indirect\n\tgopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/HdrHistogram/hdrhistogram-go v0.9.0 h1:dpujRju0R4M/QZzcnR1LH1qm+TVG3UzkWdp5tH1WMcg=\ngithub.com/HdrHistogram/hdrhistogram-go v0.9.0/go.mod h1:nxrse8/Tzg2tg3DZcZjm6qEclQKK70g0KxO61gFFZD4=\ngithub.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE=\ngithub.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=\ngithub.com/cactus/go-statsd-client/statsd v0.0.0-20190922033735-5ca90424ceb7 h1:QjgH6kpBzpFeQKXnpa6cdfg4F2heAG2sP3CZG+fGS+8=\ngithub.com/cactus/go-statsd-client/statsd v0.0.0-20190922033735-5ca90424ceb7/go.mod h1:D4RDtP0MffJ3+R36OkGul0LwJLIN8nRb0Ac6jZmJCmo=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/crossdock/crossdock-go v0.0.0-20160816171116-049aabb0122b h1:WR1qVJzbvrVywhAk4kMQKRPx09AZVI0NdEdYs59iHcA=\ngithub.com/crossdock/crossdock-go v0.0.0-20160816171116-049aabb0122b/go.mod h1:v9FBN7gdVTpiD/+LZ7Po0UKvROyT87uLVxTHVky/dlQ=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=\ngithub.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=\ngithub.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a h1:AA9vgIBDjMHPC2McaGPojgV2dcI78ZC0TLNhYCXEKH8=\ngithub.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a/go.mod h1:lzZQ3Noex5pfAy7mkAeCjcBDteYU85uWWnJ/y6gKU8k=\ngithub.com/samuel/go-thrift v0.0.0-20190219015601-e8b6b52668fe h1:gD4vkYmuoWVgdV6UwI3tPo9MtMfVoIRY+Xn9919SJBg=\ngithub.com/samuel/go-thrift v0.0.0-20190219015601-e8b6b52668fe/go.mod h1:Vrkh1pnjV9Bl8c3P9zH0/D4NlOHWP5d4/hF4YTULaec=\ngithub.com/streadway/quantile v0.0.0-20220407130108-4246515d968d h1:X4+kt6zM/OVO6gbJdAfJR60MGPsqCzbtXNnjoGqdfAs=\ngithub.com/streadway/quantile v0.0.0-20220407130108-4246515d968d/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=\ngithub.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/uber-go/tally v3.3.15+incompatible h1:9hLSgNBP28CjIaDmAuRTq9qV+UZY+9PcvAkXO4nNMwg=\ngithub.com/uber-go/tally v3.3.15+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU=\ngithub.com/uber/jaeger-client-go v2.22.1+incompatible h1:NHcubEkVbahf9t3p75TOCR83gdUHXjRJvjoBh1yACsM=\ngithub.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=\ngithub.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=\ngithub.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.2.0 h1:6I+W7f5VwC5SV9dNrZ3qXrDB9mD0dyGOi/ZJmYw03T4=\ngo.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=\ngolang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=\ngolang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\n"
  },
  {
    "path": "guide/Thrift_Hyperbahn.md",
    "content": "# Set up a Go + Thrift + Hyperbahn Service\n\nThe code matching this guide is [here](../examples/keyvalue).\n\nThe TChannel+Thrift integration for Go uses code generated by thrift-gen.\n\n## Dependencies\n\nMake sure your [GOPATH is set up](http://golang.org/doc/code.html) before following this guide.\n\nYou'll need to `go get` the following:\n* github.com/uber/tchannel-go\n* github.com/uber/tchannel-go/hyperbahn\n* github.com/uber/tchannel-go/thrift\n* github.com/uber/tchannel-go/thrift/thrift-gen\n\nUse [Godep](https://github.com/tools/godep) to manage dependencies, as the API is still in development and will change.\n\nThis example will assume that the service is created in the following directory:\n`$GOPATH/src/github.com/uber/tchannel-go/examples/keyvalue`\n\nYou should use your own path and update your import paths accordingly.\n\n## Thrift service\n\nCreate a [Thrift](https://thrift.apache.org/) file to define your service. For this guide, we'll use:\n\n`keyvalue.thrift`:\n```thrift\nservice baseService {\n  string HealthCheck()\n}\n\nexception KeyNotFound {\n  1: string key\n}\n\nexception InvalidKey {}\n\nservice KeyValue extends baseService {\n  // If the key does not start with a letter, InvalidKey is returned.\n  // If the key does not exist, KeyNotFound is returned.\n  string Get(1: string key) throws (\n    1: KeyNotFound notFound\n    2: InvalidKey invalidKey)\n\n  // Set returns InvalidKey is an invalid key is sent.\n  void Set(1: string key, 2: string value)\n}\n\n// Returned when the user is not authorized for the Admin service.\nexception NotAuthorized {}\n\nservice Admin extends baseService {\n  void clearAll() throws (1: NotAuthorized notAuthorized)\n}\n```\n\nThis Thrift specification defines two services:\n * `KeyValue`: A simple string key-value store.\n * `Admin`: Management for the key-value store.\n\nBoth of these services inherit `baseService` and so inherit `HealthCheck`.\n\nThe methods may return exceptions instead of the expected result, which are\nalso defined in the specification.\n\nOnce you have defined your service, you should generate the Thrift service and\nclient libraries by running the following:\n\n```bash\ncd $GOPATH/src/github.com/uber/tchannel-go/examples/keyvalue\nthrift-gen --generateThrift --inputFile keyvalue.thrift\n```\n\nThis runs the Thrift compiler, and then generates the service and client bindings.\nYou can run the commands manually as well:\n\n```bash\n# Generate serialization/deserialization logic.\nthrift -r --gen go:thrift_import=github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift keyvalue.thrift \n\n# Generate TChannel service interfaces in the same directory where Thrift generates code.\nthrift-gen --inputFile \"$THRIFTFILE\" --outputFile \"THRIFT_FILE_FOLDER/gen-go/thriftName/tchan-keyvalue.go\"\n```\n\n## Go server\n\nTo get the server ready, the following needs to be done:\n\n1. Create the TChannel which is the network layer protocol.\n2. Create a handler to handle the methods defined in the Thrift definition, and register it with tchannel/thrift.\n3. Create a Hyperbahn client and advertise your service with Hyperbahn.\n\n### Create a TChannel\nCreate a channel using [tchannel.NewChannel](http://godoc.org/github.com/uber/tchannel-go#NewChannel) and listen using [Channel.ListenAndServe](http://godoc.org/github.com/uber/tchannel-go#Channel.ListenAndServe).\n\nThe address passed to Listen should be a remote IP that can be used for incoming connections from other machines. You can use [tchannel.ListenIP](http://godoc.org/github.com/uber/tchannel-go#ListenIP) which uses heuristics to determine a good remote IP.\n\nWhen creating a channel, you can pass additional [options](http://godoc.org/github.com/uber/tchannel-go#ChannelOptions).\n\n### Create and register Thrift handler\n\nCreate a custom type with methods required by the Thrift generated interface. You can examine this interface by looking in `gen-go/keyvalue/tchan-keyvalue.go`. For example, the interface for our definition file looks like:\n```go\ntype TChanAdmin interface {\n\tHealthCheck(ctx thrift.Context) (string, error)\n\tClearAll(ctx thrift.Context) error\n}\n\ntype TChanKeyValue interface {\n\tGet(ctx thrift.Context, key string) (string, error)\n\tHealthCheck(ctx thrift.Context) (string, error)\n\tSet(ctx thrift.Context, key string, value string) error\n}\n```\nCreate an instance of your handler type, and then create a [thrift.Server](http://godoc.org/github.com/uber/tchannel-go/thrift#NewServer) and [register](http://godoc.org/github.com/uber/tchannel-go/thrift#Server.Register) your Thrift handler. You can register multiple Thrift services on the same `thrift.Server`.\n\nEach handler method is run in a new goroutine and so must be thread-safe.\nYour handler methods can return two types of errors:\n * Errors declared in the Thrift file (e.g. `KeyNotFound`).\n * Unexpected errors.\n\nIf you return an unexpected error, an error frame is sent over Thrift with the message. If there are known error cases, it is better to declare them in the Thrift file and return those explicitly, e.g.:\n\n```go\n  if value, ok := map[key]; ok {\n    return value, \"\"\n  }\n  // Return a Thrift exception if the key is not found.\n  return \"\", &keyvalue.KeyNotFound{Key: key}\n```\n\n### Advertise with Hyperbahn\n\nCreate a Hyperbahn client using [hyperbahn.NewClient](http://godoc.org/github.com/uber/tchannel-go/hyperbahn#NewClient) which requires a Hyperbahn configuration object that should be loaded from a configuration file for the current environment. You can also pass more [options](http://godoc.org/github.com/uber/tchannel-go/hyperbahn#ClientOptions) when creating the client.\n\nCall [Advertise](http://godoc.org/github.com/uber/tchannel-go/hyperbahn#Client.Advertise) to advertise the service with Hyperbahn.\n\n### Serving\n\nYour service is now serving over Hyperbahn! You can test this by making a call using [tcurl](https://github.com/uber/tcurl):\n\n```\nnode tcurl.js -p [HYPERBAHN-HOSTPORT] -t [DIR-TO-THRIFT] keyvalue KeyValue::Set -3 '{\"key\": \"hello\", \"value\": \"world\"}'\nnode tcurl.js -p [HYPERBAHN-HOSTPORT] -t [DIR-TO-THRIFT] keyvalue KeyValue::Get -3 '{\"key\": \"hello\"}'\n```\n\nReplace `[HYPERBAHN-HOSTPORT]` with the host:port of a Hyperbahn node, and `[DIR-TO-THRIFT]` with the directory where the .thrift file is stored.\n\nYour service can now be accessed from any language over Hyperbahn + TChannel!\n\n## Go client\n\nNote: The client implementation is still in active development.\n\nTo make a client that talks, you need to:\n\n1. Create a TChannel (or re-use an existing TChannel)\n2. Set up Hyperbahn\n3. Create a Thrift+TChannel client.\n4. Make remote calls using the Thrift client.\n\n### Create a TChannel\n\nTChannels are bi-directional and so the client uses the same method as the server code (tchannel.NewChannel) to create a TChannel. You do not need to call ListenAndServe on the channel. Even though the channel does not host a service, a serviceName is required\nfor TChannel. This serviceName should be unique to identify this client.\n\nYou can use an existing TChannel which hosts a service to make client calls.\n\n### Set up Hyperbahn\n\nSimilar to the server code, create a new Hyperbahn client using hyperbahn.NewClient. You do not\nneed to call Advertise, as the client does not have any services to advertise over Hyperbahn.\n\nIf you have already set up an existing client for use with a server, then you do not\nneed to do anything further.\n\n### Create a Thrift client\n\nThe Thrift client has two parts:\n\n1. The `thrift.TChanClient` which is configured to hit a specific Hyperbahn service.\n2. A generated client which uses an underlying `thrift.TChanClient` to call methods for a specific Thrift service.\n\nTo create a `thrift.TChanClient`, use `thrift.NewClient`. This client can then be used to create a generated client:\n```go\nthriftClient := thrift.NewClient(ch, \"keyvalue\", nil)\nclient := keyvalue.NewTChanKeyValueClient(thriftClient)\nadminClient := keyvalue.NewTChanAdminClient(thriftClient)\n```\n\n### Make remote calls\n\nMethod calls on the client make remote calls over TChannel. E.g.\n```go\nerr := client.Set(ctx, \"hello\", \"world\")\nval, err := client.Get(ctx, \"hello\")\n// val = \"world\"\n```\n\nYou must pass a context when making method calls which passes the deadline, tracing information, and application headers. A simple root context is:\n```go\nctx, cancel := thrift.NewContext(time.Second)\n```\n\nAll calls over TChannel are required to have a timeout, and tracing information. NewContext should only be used by edges, all other nodes should pass through the incoming Context. When you pass through a Context, you pass along the deadline, tracing information, and the headers.\n\nNote: Trace spans are automatically generated by TChannel, and the parent is set automatically from the current context's tracing span.\n\n## Headers\n\nThrift + TChannel allows clients to send headers (a list of string key/value pairs) and servers can add response headers to any response.\n\nIn Go, headers are attached to a context\nbefore a call is made using [WithHeaders](http://godoc.org/github.com/uber/tchannel-go/thrift#WithHeaders):\n```go\nheaders := map[string]string{\"user\": \"prashant\"}\n\nctx, cancel := thrift.NewContext(time.Second)\nctx = thrift.WithHeaders(ctx)\n```\n\nThe server can read these headers using [Headers](http://godoc.org/github.com/uber/tchannel-go/thrift#Context) and can set additional response headers using `SetResponseHeaders`:\n```go\nfunc (h *kvHandler) ClearAll(ctx thrift.Context) {\n  headers := ctx.Headers()\n  // Application logic\n  respHeaders := map[string]string{\n    \"count\": 10,\n  }\n  ctx.SetResponseHeaders(respHeaders)\n}\n```\n\nThe client can read the response headers by calling `ctx.ResponseHeaders()` on the same context that was passed when making the call:\n\n```go\nctx := thrift.WithHeaders(thrift.NewContext(time.Second), headers)\nerr := adminClient.ClearAll()\n// check error\nresponseHeaders := ctx.ResponseHeaders()\n```\n\nHeaders should not be used to pass arguments to the method - the Thrift request/response structs should be used for this.\n\n## Limitations & Upcoming Changes\n\nTChannel's peer selection does not yet have a detailed health model for nodes, and selection\ndoes not balance load across nodes.\n\nThe thrift-gen autogenerated code is new, and may not support all Thrift features (E.g. annotations, includes, multiple files)\n"
  },
  {
    "path": "handlers.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"reflect\"\n\t\"runtime\"\n\t\"sync\"\n\n\t\"golang.org/x/net/context\"\n)\n\n// A Handler is an object that can be registered with a Channel to process\n// incoming calls for a given service and method\ntype Handler interface {\n\t// Handles an incoming call for service\n\tHandle(ctx context.Context, call *InboundCall)\n}\n\n// registrar is a subset of the Registrar interface, only containing Register.\ntype registrar interface {\n\tRegister(h Handler, methodName string)\n}\n\n// A HandlerFunc is an adapter to allow the use of ordinary functions as\n// Channel handlers.  If f is a function with the appropriate signature, then\n// HandlerFunc(f) is a Handler object that calls f.\ntype HandlerFunc func(ctx context.Context, call *InboundCall)\n\n// Handle calls f(ctx, call)\nfunc (f HandlerFunc) Handle(ctx context.Context, call *InboundCall) { f(ctx, call) }\n\n// An ErrorHandlerFunc is an adapter to allow the use of ordinary functions as\n// Channel handlers, with error handling convenience.  If f is a function with\n// the appropriate signature, then ErrorHandlerFunc(f) is a Handler object that\n// calls f.\ntype ErrorHandlerFunc func(ctx context.Context, call *InboundCall) error\n\n// Handle calls f(ctx, call)\nfunc (f ErrorHandlerFunc) Handle(ctx context.Context, call *InboundCall) {\n\tif err := f(ctx, call); err != nil {\n\t\tif GetSystemErrorCode(err) == ErrCodeUnexpected {\n\t\t\tcall.log.WithFields(f.getLogFields()...).WithFields(ErrField(err)).Error(\"Unexpected handler error\")\n\t\t}\n\t\tcall.Response().SendSystemError(err)\n\t}\n}\n\nfunc (f ErrorHandlerFunc) getLogFields() LogFields {\n\tptr := reflect.ValueOf(f).Pointer()\n\thandlerFunc := runtime.FuncForPC(ptr) // can't be nil\n\tfileName, fileLine := handlerFunc.FileLine(ptr)\n\treturn LogFields{\n\t\t{\"handlerFuncName\", handlerFunc.Name()},\n\t\t{\"handlerFuncFileName\", fileName},\n\t\t{\"handlerFuncFileLine\", fileLine},\n\t}\n}\n\n// Manages handlers\ntype handlerMap struct {\n\tsync.RWMutex\n\n\thandlers map[string]Handler\n}\n\n// Register implements registrar.\nfunc (hmap *handlerMap) Register(h Handler, method string) {\n\thmap.Lock()\n\tdefer hmap.Unlock()\n\n\tif hmap.handlers == nil {\n\t\thmap.handlers = make(map[string]Handler)\n\t}\n\n\thmap.handlers[method] = h\n}\n\n// Finds the handler matching the given service and method.  See https://github.com/golang/go/issues/3512\n// for the reason that method is []byte instead of a string\nfunc (hmap *handlerMap) find(method []byte) Handler {\n\thmap.RLock()\n\thandler := hmap.handlers[string(method)]\n\thmap.RUnlock()\n\n\treturn handler\n}\n\nfunc (hmap *handlerMap) Handle(ctx context.Context, call *InboundCall) {\n\tc := call.conn\n\th := hmap.find(call.Method())\n\tif h == nil {\n\t\tc.log.WithFields(\n\t\t\tLogField{\"serviceName\", call.ServiceName()},\n\t\t\tLogField{\"method\", call.MethodString()},\n\t\t).Error(\"Couldn't find handler.\")\n\t\tcall.Response().SendSystemError(\n\t\t\tNewSystemError(ErrCodeBadRequest, \"no handler for service %q and method %q\", call.ServiceName(), call.Method()))\n\t\treturn\n\t}\n\n\tif c.log.Enabled(LogLevelDebug) {\n\t\tc.log.Debugf(\"Dispatching %s:%s from %s\", call.ServiceName(), call.Method(), c.remotePeerInfo)\n\t}\n\th.Handle(ctx, call)\n}\n\n// channelHandler is a Handler that wraps a Channel and delegates requests\n// to SubChannels based on the inbound call's service name.\ntype channelHandler struct{ ch *Channel }\n\nfunc (c channelHandler) Handle(ctx context.Context, call *InboundCall) {\n\tc.ch.GetSubChannel(call.ServiceName()).handler.Handle(ctx, call)\n}\n\n// Register registers the handler on the channel's default service name.\nfunc (c channelHandler) Register(h Handler, methodName string) {\n\tc.ch.GetSubChannel(c.ch.PeerInfo().ServiceName).Register(h, methodName)\n}\n\n// userHandlerWithSkip is a Handler that wraps a localHandler backed by the channel.\n// and a user provided handler.\n// The inbound call will be handled by user handler, unless the call's\n// method name is configured to be handled by localHandler from ignore.\ntype userHandlerWithSkip struct {\n\tlocalHandler      channelHandler\n\tignoreUserHandler map[string]struct{} // key is service::method\n\tuserHandler       Handler\n}\n\nfunc (u userHandlerWithSkip) Handle(ctx context.Context, call *InboundCall) {\n\tif _, ok := u.ignoreUserHandler[call.MethodString()]; ok {\n\t\tu.localHandler.Handle(ctx, call)\n\t\treturn\n\t}\n\tu.userHandler.Handle(ctx, call)\n}\n\nfunc (u userHandlerWithSkip) Register(h Handler, methodName string) {\n\tu.localHandler.Register(h, methodName)\n}\n"
  },
  {
    "path": "handlers_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"golang.org/x/net/context\"\n)\n\ntype dummyHandler struct{}\n\nfunc (dummyHandler) Handle(ctx context.Context, call *InboundCall) {}\n\nfunc TestHandlers(t *testing.T) {\n\tconst (\n\t\tm1 = \"m1\"\n\t\tm2 = \"m2\"\n\t)\n\tvar (\n\t\thmap = &handlerMap{}\n\n\t\th1 = &dummyHandler{}\n\t\th2 = &dummyHandler{}\n\n\t\tm1b = []byte(m1)\n\t\tm2b = []byte(m2)\n\t)\n\n\tassert.Nil(t, hmap.find(m1b))\n\tassert.Nil(t, hmap.find(m2b))\n\n\thmap.Register(h1, m1)\n\tassert.Equal(t, h1, hmap.find(m1b))\n\tassert.Nil(t, hmap.find(m2b))\n\n\thmap.Register(h2, m2)\n\tassert.Equal(t, h1, hmap.find(m1b))\n\tassert.Equal(t, h2, hmap.find(m2b))\n}\n"
  },
  {
    "path": "handlers_with_skip_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/uber/tchannel-go\"\n\t. \"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\nfunc procedure(svc, method string) string {\n\treturn fmt.Sprintf(\"%s::%s\", svc, method)\n}\n\nfunc TestUserHandlerWithSkip(t *testing.T) {\n\tconst (\n\t\tsvc                  = \"svc\"\n\t\tuserHandleMethod     = \"method\"\n\t\tuserHandleSkipMethod = \"skipMethod\"\n\t\thandleRuns           = 3\n\t\thandleSkipRuns       = 5\n\t)\n\n\tuserCounter, channelCounter := &recordHandler{}, &recordHandler{}\n\n\topts := testutils.NewOpts().NoRelay()\n\topts.ServiceName = svc\n\topts.ChannelOptions = ChannelOptions{\n\t\tHandler:            userCounter,\n\t\tSkipHandlerMethods: []string{procedure(svc, userHandleSkipMethod)},\n\t}\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t// channel should be able to handle user ignored methods\n\t\tts.Register(channelCounter, procedure(svc, userHandleSkipMethod))\n\n\t\tclient := ts.NewClient(nil)\n\n\t\tfor i := 0; i < handleRuns; i++ {\n\t\t\tctx, cancel := tchannel.NewContext(testutils.Timeout(300 * time.Millisecond))\n\t\t\tdefer cancel()\n\t\t\traw.Call(ctx, client, ts.HostPort(), svc, procedure(svc, userHandleMethod), nil, nil)\n\t\t}\n\t\tassert.Equal(t, uint32(handleRuns), userCounter.c.Load(), \"user provided handler not invoked correct amount of times\")\n\n\t\tfor i := 0; i < handleSkipRuns; i++ {\n\t\t\tctx, cancel := tchannel.NewContext(testutils.Timeout(300 * time.Millisecond))\n\t\t\tdefer cancel()\n\t\t\traw.Call(ctx, client, ts.HostPort(), svc, procedure(svc, userHandleSkipMethod), nil, nil)\n\t\t}\n\t\tassert.Equal(t, uint32(handleSkipRuns), channelCounter.c.Load(), \"user provided handler not invoked correct amount of times\")\n\t})\n}\n\nfunc TestUserHandlerWithSkipInvalidInput(t *testing.T) {\n\topts := &ChannelOptions{\n\t\tHandler:            &recordHandler{},\n\t\tSkipHandlerMethods: []string{\"notDelimitedByDoubleColons\"},\n\t}\n\t_, err := NewChannel(\"svc\", opts)\n\tassert.EqualError(t, err, `each \"SkipHandlerMethods\" value should be of service::Method format but got \"notDelimitedByDoubleColons\"`)\n}\n\ntype recordHandler struct{ c atomic.Uint32 }\n\nfunc (r *recordHandler) Handle(ctx context.Context, call *InboundCall) {\n\tr.c.Inc()\n}\n"
  },
  {
    "path": "health.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\t\"golang.org/x/net/context\"\n)\n\nconst (\n\t_defaultHealthCheckTimeout         = time.Second\n\t_defaultHealthCheckFailuresToClose = 5\n\n\t_healthHistorySize = 256\n)\n\n// HealthCheckOptions are the parameters to configure active TChannel health\n// checks. These are not intended to check application level health, but\n// TCP connection health (similar to TCP keep-alives). The health checks use\n// TChannel ping messages.\ntype HealthCheckOptions struct {\n\t// The period between health checks. If this is zeor, active health checks\n\t// are disabled.\n\tInterval time.Duration\n\n\t// The timeout to use for a health check.\n\t// If no value is specified, it defaults to time.Second.\n\tTimeout time.Duration\n\n\t// FailuresToClose is the number of consecutive health check failures that\n\t// will cause this connection to be closed.\n\t// If no value is specified, it defaults to 5.\n\tFailuresToClose int\n}\n\ntype healthHistory struct {\n\tsync.RWMutex\n\n\tstates []bool\n\n\tinsertAt int\n\ttotal    int\n}\n\nfunc newHealthHistory() *healthHistory {\n\treturn &healthHistory{\n\t\tstates: make([]bool, _healthHistorySize),\n\t}\n}\n\nfunc (hh *healthHistory) add(b bool) {\n\thh.Lock()\n\tdefer hh.Unlock()\n\n\thh.states[hh.insertAt] = b\n\thh.insertAt = (hh.insertAt + 1) % _healthHistorySize\n\thh.total++\n}\n\nfunc (hh *healthHistory) asBools() []bool {\n\thh.RLock()\n\tdefer hh.RUnlock()\n\n\tif hh.total < _healthHistorySize {\n\t\treturn append([]bool(nil), hh.states[:hh.total]...)\n\t}\n\n\tstates := hh.states\n\tcopyStates := make([]bool, 0, _healthHistorySize)\n\tcopyStates = append(copyStates, states[hh.insertAt:]...)\n\tcopyStates = append(copyStates, states[:hh.insertAt]...)\n\treturn copyStates\n}\n\nfunc (hco HealthCheckOptions) enabled() bool {\n\treturn hco.Interval > 0\n}\n\nfunc (hco HealthCheckOptions) withDefaults() HealthCheckOptions {\n\tif hco.Timeout == 0 {\n\t\thco.Timeout = _defaultHealthCheckTimeout\n\t}\n\tif hco.FailuresToClose == 0 {\n\t\thco.FailuresToClose = _defaultHealthCheckFailuresToClose\n\t}\n\treturn hco\n}\n\n// healthCheck will do periodic pings on the connection to check the state of the connection.\n// We accept connID on the stack so can more easily debug panics or leaked goroutines.\nfunc (c *Connection) healthCheck(connID uint32) {\n\tdefer close(c.healthCheckDone)\n\n\topts := c.opts.HealthChecks\n\n\tticker := c.timeTicker(opts.Interval)\n\tdefer ticker.Stop()\n\n\tconsecutiveFailures := 0\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\tcase <-c.healthCheckCtx.Done():\n\t\t\treturn\n\t\t}\n\n\t\tctx, cancel := context.WithTimeout(c.healthCheckCtx, opts.Timeout)\n\t\terr := c.ping(ctx)\n\t\tcancel()\n\t\tc.healthCheckHistory.add(err == nil)\n\t\tif err == nil {\n\t\t\tif c.log.Enabled(LogLevelDebug) {\n\t\t\t\tc.log.Debug(\"Performed successful active health check.\")\n\t\t\t}\n\t\t\tconsecutiveFailures = 0\n\t\t\tcontinue\n\t\t}\n\n\t\t// If the health check failed because the connection closed or health\n\t\t// checks were stopped, we don't need to log or close the connection.\n\t\tif GetSystemErrorCode(err) == ErrCodeCancelled || err == ErrInvalidConnectionState {\n\t\t\tc.log.WithFields(ErrField(err)).Debug(\"Health checker stopped.\")\n\t\t\treturn\n\t\t}\n\n\t\tconsecutiveFailures++\n\t\tc.log.WithFields(LogFields{\n\t\t\t{\"consecutiveFailures\", consecutiveFailures},\n\t\t\tErrField(err),\n\t\t\t{\"failuresToClose\", opts.FailuresToClose},\n\t\t}...).Warn(\"Failed active health check.\")\n\n\t\tif consecutiveFailures >= opts.FailuresToClose {\n\t\t\tc.close(LogFields{\n\t\t\t\t{\"reason\", \"health check failure\"},\n\t\t\t\tErrField(err),\n\t\t\t}...)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (c *Connection) stopHealthCheck() {\n\t// Health checks are not enabled.\n\tif c.healthCheckDone == nil {\n\t\treturn\n\t}\n\n\t// Best effort check to see if health checks were stopped.\n\tif c.healthCheckCtx.Err() != nil {\n\t\treturn\n\t}\n\tc.log.Debug(\"Stopping health checks.\")\n\tc.healthCheckQuit()\n\t<-c.healthCheckDone\n}\n"
  },
  {
    "path": "health_ext_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestHealthCheckStopBeforeStart(t *testing.T) {\n\topts := testutils.NewOpts().NoRelay()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\n\t\tvar pingCount int\n\t\tframeRelay, cancel := testutils.FrameRelay(t, ts.HostPort(), func(outgoing bool, f *Frame) *Frame {\n\t\t\tif strings.Contains(f.Header.String(), \"PingRes\") {\n\t\t\t\tpingCount++\n\t\t\t}\n\t\t\treturn f\n\t\t})\n\t\tdefer cancel()\n\n\t\tft := testutils.NewFakeTicker()\n\t\topts := testutils.NewOpts().\n\t\t\tSetTimeTicker(ft.New).\n\t\t\tSetHealthChecks(HealthCheckOptions{Interval: time.Second})\n\t\tclient := ts.NewClient(opts)\n\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\tconn, err := client.RootPeers().GetOrAdd(frameRelay).GetConnection(ctx)\n\t\trequire.NoError(t, err, \"Failed to get connection\")\n\n\t\tconn.StopHealthCheck()\n\n\t\t// Should be no ping messages sent.\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tft.TryTick()\n\t\t}\n\t\tassert.Equal(t, 0, pingCount, \"No pings when health check is stopped\")\n\t})\n}\n\nfunc TestHealthCheckStopNoError(t *testing.T) {\n\topts := testutils.NewOpts().NoRelay()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\n\t\tvar pingCount int\n\t\tframeRelay, cancel := testutils.FrameRelay(t, ts.HostPort(), func(outgoing bool, f *Frame) *Frame {\n\t\t\tif strings.Contains(f.Header.String(), \"PingRes\") {\n\t\t\t\tpingCount++\n\t\t\t}\n\t\t\treturn f\n\t\t})\n\t\tdefer cancel()\n\n\t\tft := testutils.NewFakeTicker()\n\t\topts := testutils.NewOpts().\n\t\t\tSetTimeTicker(ft.New).\n\t\t\tSetHealthChecks(HealthCheckOptions{Interval: time.Second}).\n\t\t\tAddLogFilter(\"Unexpected ping response.\", 1)\n\t\tclient := ts.NewClient(opts)\n\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\tconn, err := client.RootPeers().GetOrAdd(frameRelay).GetConnection(ctx)\n\t\trequire.NoError(t, err, \"Failed to get connection\")\n\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tft.Tick()\n\t\t\twaitForNHealthChecks(t, conn, i+1)\n\t\t}\n\t\tconn.StopHealthCheck()\n\n\t\t// We stop the health check, so the ticks channel is no longer read, so\n\t\t// we can't use the synchronous tick here.\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tft.TryTick()\n\t\t}\n\n\t\tassert.Equal(t, 10, pingCount, \"Pings should stop after health check is stopped\")\n\t})\n}\n\nfunc TestHealthCheckIntegration(t *testing.T) {\n\ttests := []struct {\n\t\tmsg                 string\n\t\tdisable             bool\n\t\tfailuresToClose     int\n\t\tpingResponses       []bool\n\t\twantActive          bool\n\t\twantHealthCheckLogs int\n\t}{\n\t\t{\n\t\t\tmsg:             \"no failures with failuresToClose=0\",\n\t\t\tfailuresToClose: 1,\n\t\t\tpingResponses:   []bool{true, true, true, true},\n\t\t\twantActive:      true,\n\t\t},\n\t\t{\n\t\t\tmsg:                 \"single failure with failuresToClose=1\",\n\t\t\tfailuresToClose:     1,\n\t\t\tpingResponses:       []bool{true, false},\n\t\t\twantActive:          false,\n\t\t\twantHealthCheckLogs: 1,\n\t\t},\n\t\t{\n\t\t\tmsg:                 \"single failure with failuresToClose=2\",\n\t\t\tfailuresToClose:     2,\n\t\t\tpingResponses:       []bool{true, false, true, false, true},\n\t\t\twantActive:          true,\n\t\t\twantHealthCheckLogs: 2,\n\t\t},\n\t\t{\n\t\t\tmsg:                 \"up to 2 consecutive failures with failuresToClose=3\",\n\t\t\tfailuresToClose:     3,\n\t\t\tpingResponses:       []bool{true, false, true, false, true, false, false, true, false, false, true},\n\t\t\twantActive:          true,\n\t\t\twantHealthCheckLogs: 6,\n\t\t},\n\t\t{\n\t\t\tmsg:                 \"3 consecutive failures with failuresToClose=3\",\n\t\t\tfailuresToClose:     3,\n\t\t\tpingResponses:       []bool{true, false, true, false, true, false, false, true, false, false, false},\n\t\t\twantActive:          false,\n\t\t\twantHealthCheckLogs: 7,\n\t\t},\n\t}\n\n\terrFrame := getErrorFrame(t)\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\topts := testutils.NewOpts().NoRelay()\n\t\t\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\t\tvar pingCount int\n\t\t\t\tframeRelay, cancel := testutils.FrameRelay(t, ts.HostPort(), func(outgoing bool, f *Frame) *Frame {\n\t\t\t\t\tif strings.Contains(f.Header.String(), \"PingRes\") {\n\t\t\t\t\t\tsuccess := tt.pingResponses[pingCount]\n\t\t\t\t\t\tpingCount++\n\t\t\t\t\t\tif !success {\n\t\t\t\t\t\t\terrFrame.Header.ID = f.Header.ID\n\t\t\t\t\t\t\tf = errFrame\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn f\n\t\t\t\t})\n\t\t\t\tdefer cancel()\n\n\t\t\t\tft := testutils.NewFakeTicker()\n\t\t\t\topts := testutils.NewOpts().\n\t\t\t\t\tSetTimeTicker(ft.New).\n\t\t\t\t\tSetHealthChecks(HealthCheckOptions{Interval: time.Second, FailuresToClose: tt.failuresToClose}).\n\t\t\t\t\tAddLogFilter(\"Failed active health check.\", uint(tt.wantHealthCheckLogs)).\n\t\t\t\t\tAddLogFilter(\"Unexpected ping response.\", 1)\n\t\t\t\tclient := ts.NewClient(opts)\n\n\t\t\t\tctx, cancel := NewContext(time.Second)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tconn, err := client.RootPeers().GetOrAdd(frameRelay).GetConnection(ctx)\n\t\t\t\trequire.NoError(t, err, \"Failed to get connection\")\n\n\t\t\t\tfor i := 0; i < len(tt.pingResponses); i++ {\n\t\t\t\t\tft.TryTick()\n\n\t\t\t\t\twaitForNHealthChecks(t, conn, i+1)\n\t\t\t\t\tassert.Equal(t, tt.pingResponses[:i+1], introspectConn(conn).HealthChecks, \"Unexpectd health check history\")\n\t\t\t\t}\n\n\t\t\t\t// Once the health check is done, we trigger a Close, it's possible we are still\n\t\t\t\t// waiting for the connection to close.\n\t\t\t\tif tt.wantActive == false {\n\t\t\t\t\ttestutils.WaitFor(time.Second, func() bool { return !conn.IsActive() })\n\t\t\t\t}\n\t\t\t\tassert.Equal(t, tt.wantActive, conn.IsActive(), \"Connection active mismatch\")\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc waitForNHealthChecks(t testing.TB, conn *Connection, n int) {\n\trequire.True(t, testutils.WaitFor(time.Second, func() bool {\n\t\treturn len(introspectConn(conn).HealthChecks) >= n\n\t}), \"Failed while waiting for %v health checks\", n)\n}\n\nfunc introspectConn(c *Connection) ConnectionRuntimeState {\n\treturn c.IntrospectState(&IntrospectionOptions{})\n}\n"
  },
  {
    "path": "health_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"math/rand\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestHealthCheckEnabled(t *testing.T) {\n\thc := HealthCheckOptions{}\n\tassert.False(t, hc.enabled(), \"Default struct should not have health checks enabled\")\n\n\thc.Interval = time.Second\n\tassert.True(t, hc.enabled(), \"Setting interval should enable health checks\")\n}\n\nfunc TestHealthCheckOptionsDefaults(t *testing.T) {\n\ttests := []struct {\n\t\topts HealthCheckOptions\n\t\twant HealthCheckOptions\n\t}{\n\t\t{\n\t\t\topts: HealthCheckOptions{},\n\t\t\twant: HealthCheckOptions{Timeout: _defaultHealthCheckTimeout, FailuresToClose: _defaultHealthCheckFailuresToClose},\n\t\t},\n\t\t{\n\t\t\topts: HealthCheckOptions{Timeout: 2 * time.Second},\n\t\t\twant: HealthCheckOptions{Timeout: 2 * time.Second, FailuresToClose: _defaultHealthCheckFailuresToClose},\n\t\t},\n\t\t{\n\t\t\topts: HealthCheckOptions{FailuresToClose: 3},\n\t\t\twant: HealthCheckOptions{Timeout: _defaultHealthCheckTimeout, FailuresToClose: 3},\n\t\t},\n\t\t{\n\t\t\topts: HealthCheckOptions{Timeout: 2 * time.Second, FailuresToClose: 3},\n\t\t\twant: HealthCheckOptions{Timeout: 2 * time.Second, FailuresToClose: 3},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tgot := tt.opts.withDefaults()\n\t\tassert.Equal(t, tt.want, got, \"Unexpected defaults for %+v\", tt.opts)\n\t}\n}\n\nfunc TestHealthHistory(t *testing.T) {\n\thh := newHealthHistory()\n\tvar want []bool\n\tfor i := 0; i < 1000; i++ {\n\t\tassert.Equal(t, want, hh.asBools())\n\t\tb := rand.Intn(3) > 0\n\t\thh.add(b)\n\t\twant = append(want, b)\n\t\tif len(want) > _healthHistorySize {\n\t\t\twant = want[1:]\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "http/buf.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage http\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\nfunc writeHeaders(wb *typed.WriteBuffer, form http.Header) {\n\tnumHeadersDeferred := wb.DeferUint16()\n\tnumHeaders := uint16(0)\n\tfor k, values := range form {\n\t\tfor _, v := range values {\n\t\t\twb.WriteLen16String(k)\n\t\t\twb.WriteLen16String(v)\n\t\t\tnumHeaders++\n\t\t}\n\t}\n\tnumHeadersDeferred.Update(numHeaders)\n}\n\nfunc readHeaders(rb *typed.ReadBuffer, form http.Header) {\n\tnumHeaders := rb.ReadUint16()\n\tfor i := 0; i < int(numHeaders); i++ {\n\t\tk := rb.ReadLen16String()\n\t\tv := rb.ReadLen16String()\n\t\tform[k] = append(form[k], v)\n\t}\n}\n\nfunc readVarintString(rb *typed.ReadBuffer) string {\n\tlength := rb.ReadUvarint()\n\treturn rb.ReadString(int(length))\n}\n\nfunc writeVarintString(wb *typed.WriteBuffer, s string) {\n\twb.WriteUvarint(uint64(len(s)))\n\twb.WriteString(s)\n}\n"
  },
  {
    "path": "http/buf_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage http\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\nfunc TestHeaders(t *testing.T) {\n\ttests := []http.Header{\n\t\t{},\n\t\t{\n\t\t\t\"K1\": []string{\"K1V1\", \"K1V2\", \"K1V3\"},\n\t\t\t\"K2\": []string{\"K2V2\", \"K2V2\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tbuf := make([]byte, 1000)\n\t\twb := typed.NewWriteBuffer(buf)\n\t\twriteHeaders(wb, tt)\n\n\t\tnewHeaders := make(http.Header)\n\t\trb := typed.NewReadBuffer(buf)\n\t\treadHeaders(rb, newHeaders)\n\t\tassert.Equal(t, tt, newHeaders, \"Headers mismatch\")\n\t}\n}\n\nfunc TestVarintString(t *testing.T) {\n\ttests := []string{\n\t\t\"\",\n\t\t\"short string\",\n\t\ttestutils.RandString(1000),\n\t}\n\n\tfor _, tt := range tests {\n\t\tbuf := make([]byte, 2000)\n\t\twb := typed.NewWriteBuffer(buf)\n\t\twriteVarintString(wb, tt)\n\n\t\trb := typed.NewReadBuffer(buf)\n\t\tgot := readVarintString(rb)\n\t\tassert.Equal(t, tt, got, \"Varint string mismatch\")\n\t}\n}\n"
  },
  {
    "path": "http/http_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage http\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\nfunc dumpHandler(w http.ResponseWriter, r *http.Request) {\n\tr.URL.Host = \"test.local\"\n\tr.URL.Scheme = \"http\"\n\n\t// We cannot use httputil.DumpRequestOut as it prints the chunked encoding\n\t// while we only care about the data that the reader would see.\n\tdump := &bytes.Buffer{}\n\tdump.WriteString(r.Method)\n\tdump.WriteString(r.URL.String())\n\tdump.WriteString(\"\\n\")\n\n\tdump.WriteString(\"Headers: \")\n\tdump.WriteString(fmt.Sprint(r.Form))\n\tdump.WriteString(\"\\n\")\n\n\tdump.WriteString(\"Body: \")\n\tio.Copy(dump, r.Body)\n\tdump.WriteString(\"\\n\")\n\n\tw.Header().Add(\"My-Header-1\", \"V1\")\n\tw.Header().Add(\"My-Header-1\", \"V2\")\n\tw.Header().Add(\"My-Header-2\", \"V3\")\n\n\tw.Write([]byte(\"Dumped request:\\n\"))\n\tw.Write(dump.Bytes())\n}\n\nfunc setupHTTP(t *testing.T, serveMux *http.ServeMux) (string, func()) {\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\trequire.NoError(t, err, \"net.Listen failed\")\n\n\tgo http.Serve(ln, serveMux)\n\thttpAddr := ln.Addr().String()\n\treturn httpAddr, func() { ln.Close() }\n}\n\nfunc setupTChan(t *testing.T, mux *http.ServeMux) (string, func()) {\n\tch := testutils.NewServer(t, testutils.NewOpts().SetServiceName(\"test\"))\n\thandler := func(ctx context.Context, call *tchannel.InboundCall) {\n\t\treq, err := ReadRequest(call)\n\t\tif !assert.NoError(t, err, \"ReadRequest failed\") {\n\t\t\treturn\n\t\t}\n\n\t\t// Make the HTTP call using the default mux.\n\t\twriter, finish := ResponseWriter(call.Response())\n\t\tmux.ServeHTTP(writer, req)\n\t\tfinish()\n\t}\n\tch.Register(tchannel.HandlerFunc(handler), \"http\")\n\treturn ch.PeerInfo().HostPort, func() { ch.Close() }\n}\n\nfunc setupProxy(t *testing.T, tchanAddr string) (string, func()) {\n\tmux := http.NewServeMux()\n\tmux.Handle(\"/\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t// You get /proxy/host:port/rest/of/the/path\n\t\tparts := strings.SplitN(r.URL.Path, \"/\", 4)\n\t\tr.URL.Host = parts[2]\n\t\tr.URL.Scheme = \"http\"\n\t\tr.URL.Path = parts[3]\n\n\t\tch := testutils.NewClient(t, nil)\n\t\tctx, cancel := tchannel.NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\tcall, err := ch.BeginCall(ctx, tchanAddr, \"test\", \"http\", nil)\n\t\trequire.NoError(t, err, \"BeginCall failed\")\n\n\t\trequire.NoError(t, WriteRequest(call, r), \"WriteRequest failed\")\n\t\tresp, err := ReadResponse(call.Response())\n\t\trequire.NoError(t, err, \"Read response failed\")\n\n\t\tfor k, vs := range resp.Header {\n\t\t\tfor _, v := range vs {\n\t\t\t\tw.Header().Add(k, v)\n\t\t\t}\n\t\t}\n\t\tw.WriteHeader(resp.StatusCode)\n\n\t\t_, err = io.Copy(w, resp.Body)\n\t\tassert.NoError(t, err, \"io.Copy failed\")\n\t\terr = resp.Body.Close()\n\t\tassert.NoError(t, err, \"Close Response Body failed\")\n\t}))\n\treturn setupHTTP(t, mux)\n}\n\n// setupServer sets up a HTTP handler and a TChannel handler .\nfunc setupServer(t *testing.T) (string, string, func()) {\n\tmux := http.NewServeMux()\n\tmux.Handle(\"/\", http.HandlerFunc(dumpHandler))\n\n\thttpAddr, httpClose := setupHTTP(t, mux)\n\ttchanAddr, tchanClose := setupTChan(t, mux)\n\n\tclose := func() {\n\t\thttpClose()\n\t\ttchanClose()\n\t}\n\treturn httpAddr, tchanAddr, close\n}\n\nfunc makeHTTPCall(t *testing.T, req *http.Request) *http.Response {\n\tresp, err := http.DefaultClient.Do(req)\n\trequire.NoError(t, err, \"HTTP request failed\")\n\treturn resp\n}\n\nfunc makeTChanCall(t *testing.T, tchanAddr string, req *http.Request) *http.Response {\n\tch := testutils.NewClient(t, nil)\n\tctx, cancel := tchannel.NewContext(time.Second)\n\tdefer cancel()\n\n\tcall, err := ch.BeginCall(ctx, tchanAddr, \"test\", \"http\", nil)\n\trequire.NoError(t, err, \"BeginCall failed\")\n\n\trequire.NoError(t, WriteRequest(call, req), \"WriteRequest failed\")\n\tresp, err := ReadResponse(call.Response())\n\trequire.NoError(t, err, \"Read response failed\")\n\n\treturn resp\n}\n\nfunc compareResponseBasic(t *testing.T, testName string, resp1, resp2 *http.Response) {\n\tresp1Body, err := ioutil.ReadAll(resp1.Body)\n\trequire.NoError(t, err, \"Read response failed\")\n\tresp2Body, err := ioutil.ReadAll(resp2.Body)\n\trequire.NoError(t, err, \"Read response failed\")\n\n\tassert.Equal(t, resp1.Status, resp2.Status, \"%v: Response status mismatch\", testName)\n\tassert.Equal(t, resp1.StatusCode, resp2.StatusCode, \"%v: Response status code mismatch\", testName)\n\tassert.Equal(t, string(resp1Body), string(resp2Body), \"%v: Response body mismatch\", testName)\n}\n\nfunc compareResponses(t *testing.T, testName string, resp1, resp2 *http.Response) {\n\tresp1Bs, err := httputil.DumpResponse(resp1, true)\n\trequire.NoError(t, err, \"Dump response\")\n\tresp2Bs, err := httputil.DumpResponse(resp2, true)\n\trequire.NoError(t, err, \"Dump response\")\n\tassert.Equal(t, string(resp1Bs), string(resp2Bs), \"%v: Response mismatch\", testName)\n}\n\ntype requestTest struct {\n\tname string\n\tf    func(string) *http.Request\n}\n\nfunc getRequestTests(t *testing.T) []requestTest {\n\trandBytes := testutils.RandBytes(40000)\n\treturn []requestTest{\n\t\t{\n\t\t\tname: \"get simple\",\n\t\t\tf: func(httpAddr string) *http.Request {\n\t\t\t\treq, err := http.NewRequest(\"GET\", fmt.Sprintf(\"http://%v/this/is/my?req=1&v=2&v&a&a\", httpAddr), nil)\n\t\t\t\trequire.NoError(t, err, \"NewRequest failed\")\n\t\t\t\treturn req\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"post simple\",\n\t\t\tf: func(httpAddr string) *http.Request {\n\t\t\t\tbody := strings.NewReader(\"This is a simple POST body\")\n\t\t\t\treq, err := http.NewRequest(\"POST\", fmt.Sprintf(\"http://%v/post/path?v=1&b=3\", httpAddr), body)\n\t\t\t\trequire.NoError(t, err, \"NewRequest failed\")\n\t\t\t\treturn req\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"post random bytes\",\n\t\t\tf: func(httpAddr string) *http.Request {\n\t\t\t\tbody := bytes.NewReader(randBytes)\n\t\t\t\treq, err := http.NewRequest(\"POST\", fmt.Sprintf(\"http://%v/post/path?v=1&b=3\", httpAddr), body)\n\t\t\t\trequire.NoError(t, err, \"NewRequest failed\")\n\t\t\t\treturn req\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc TestDirectRequests(t *testing.T) {\n\thttpAddr, tchanAddr, finish := setupServer(t)\n\tdefer finish()\n\n\ttests := getRequestTests(t)\n\tfor _, tt := range tests {\n\t\tresp1 := makeHTTPCall(t, tt.f(httpAddr))\n\t\tresp2 := makeTChanCall(t, tchanAddr, tt.f(httpAddr))\n\t\tcompareResponseBasic(t, tt.name, resp1, resp2)\n\t}\n}\n\nfunc TestProxyRequests(t *testing.T) {\n\thttpAddr, tchanAddr, finish := setupServer(t)\n\tdefer finish()\n\tproxyAddr, finish := setupProxy(t, tchanAddr)\n\tdefer finish()\n\n\ttests := getRequestTests(t)\n\tfor _, tt := range tests {\n\t\tresp1 := makeHTTPCall(t, tt.f(httpAddr))\n\t\tresp2 := makeHTTPCall(t, tt.f(proxyAddr+\"/proxy/\"+httpAddr))\n\n\t\t// Delete the Date header since the calls are made at different times.\n\t\tresp1.Header.Del(\"Date\")\n\t\tresp2.Header.Del(\"Date\")\n\t\tcompareResponses(t, tt.name, resp1, resp2)\n\t}\n}\n"
  },
  {
    "path": "http/request.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage http\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\n// WriteRequest writes a http.Request to the given writers.\nfunc WriteRequest(call tchannel.ArgWritable, req *http.Request) error {\n\t// TODO(prashant): Allow creating write buffers that let you grow the buffer underneath.\n\twb := typed.NewWriteBufferWithSize(10000)\n\twb.WriteLen8String(req.Method)\n\twriteVarintString(wb, req.URL.String())\n\twriteHeaders(wb, req.Header)\n\n\targ2Writer, err := call.Arg2Writer()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err := wb.FlushTo(arg2Writer); err != nil {\n\t\treturn err\n\t}\n\tif err := arg2Writer.Close(); err != nil {\n\t\treturn err\n\t}\n\n\targ3Writer, err := call.Arg3Writer()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif req.Body != nil {\n\t\tif _, err = io.Copy(arg3Writer, req.Body); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn arg3Writer.Close()\n}\n\n// ReadRequest reads a http.Request from the given readers.\nfunc ReadRequest(call tchannel.ArgReadable) (*http.Request, error) {\n\tvar arg2 []byte\n\tif err := tchannel.NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil {\n\t\treturn nil, err\n\t}\n\trb := typed.NewReadBuffer(arg2)\n\tmethod := rb.ReadLen8String()\n\turl := readVarintString(rb)\n\n\tr, err := http.NewRequest(method, url, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treadHeaders(rb, r.Header)\n\n\tif err := rb.Err(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tr.Body, err = call.Arg3Reader()\n\treturn r, err\n}\n"
  },
  {
    "path": "http/response.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage http\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\n// ReadResponse reads a http.Response from the given readers.\nfunc ReadResponse(call tchannel.ArgReadable) (*http.Response, error) {\n\tvar arg2 []byte\n\tif err := tchannel.NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil {\n\t\treturn nil, err\n\t}\n\n\trb := typed.NewReadBuffer(arg2)\n\tstatusCode := rb.ReadUint16()\n\tmessage := readVarintString(rb)\n\n\tresponse := &http.Response{\n\t\tStatusCode: int(statusCode),\n\t\tStatus:     fmt.Sprintf(\"%v %v\", statusCode, message),\n\t\tProto:      \"HTTP/1.1\",\n\t\tProtoMajor: 1,\n\t\tProtoMinor: 1,\n\t\tHeader:     make(http.Header),\n\t}\n\treadHeaders(rb, response.Header)\n\tif err := rb.Err(); err != nil {\n\t\treturn nil, err\n\t}\n\n\targ3Reader, err := call.Arg3Reader()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresponse.Body = arg3Reader\n\treturn response, nil\n}\n\ntype tchanResponseWriter struct {\n\theaders    http.Header\n\tstatusCode int\n\tresponse   tchannel.ArgWritable\n\targ3Writer io.WriteCloser\n\terr        error\n}\n\nfunc newTChanResponseWriter(response tchannel.ArgWritable) *tchanResponseWriter {\n\treturn &tchanResponseWriter{\n\t\theaders:    make(http.Header),\n\t\tstatusCode: http.StatusOK,\n\t\tresponse:   response,\n\t}\n}\n\nfunc (w *tchanResponseWriter) Header() http.Header {\n\treturn w.headers\n}\n\nfunc (w *tchanResponseWriter) WriteHeader(statusCode int) {\n\tw.statusCode = statusCode\n}\n\n// writeHeaders writes out the HTTP headers as arg2, and creates the arg3 writer.\nfunc (w *tchanResponseWriter) writeHeaders() {\n\t// TODO(prashant): Allow creating write buffers that let you grow the buffer underneath.\n\twb := typed.NewWriteBufferWithSize(10000)\n\twb.WriteUint16(uint16(w.statusCode))\n\twriteVarintString(wb, http.StatusText(w.statusCode))\n\twriteHeaders(wb, w.headers)\n\n\targ2Writer, err := w.response.Arg2Writer()\n\tif err != nil {\n\t\tw.err = err\n\t\treturn\n\t}\n\tif _, w.err = wb.FlushTo(arg2Writer); w.err != nil {\n\t\treturn\n\t}\n\tif w.err = arg2Writer.Close(); w.err != nil {\n\t\treturn\n\t}\n\n\tw.arg3Writer, w.err = w.response.Arg3Writer()\n}\n\nfunc (w *tchanResponseWriter) Write(bs []byte) (int, error) {\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\n\tif w.arg3Writer == nil {\n\t\tw.writeHeaders()\n\t}\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\n\treturn w.arg3Writer.Write(bs)\n}\n\nfunc (w *tchanResponseWriter) finish() error {\n\tif w.arg3Writer == nil || w.err != nil {\n\t\treturn w.err\n\t}\n\treturn w.arg3Writer.Close()\n}\n\n// ResponseWriter returns a http.ResponseWriter that will write to an underlying writer.\n// It also returns a function that should be called once the handler has completed.\nfunc ResponseWriter(response tchannel.ArgWritable) (http.ResponseWriter, func() error) {\n\tresponseWriter := newTChanResponseWriter(response)\n\treturn responseWriter, responseWriter.finish\n}\n"
  },
  {
    "path": "hyperbahn/advertise.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage hyperbahn\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n)\n\nconst (\n\t// maxAdvertiseFailures is the number of consecutive advertise failures after\n\t// which we give up and trigger an OnError event.\n\tmaxAdvertiseFailures = 5\n\t// advertiseInterval is the base time interval between advertisements.\n\tadvertiseInterval = 50 * time.Second\n\t// advertiseFuzzInterval is the maximum fuzz period to add to advertiseInterval.\n\tadvertiseFuzzInterval = 20 * time.Second\n\t// advertiseRetryInterval is the unfuzzed base duration to wait before retry on the first\n\t// advertise failure. Successive retries will use 2 * previous base duration.\n\tadvertiseRetryInterval = 1 * time.Second\n)\n\n// ErrAdvertiseFailed is triggered when advertise fails.\ntype ErrAdvertiseFailed struct {\n\t// WillRetry is set to true if advertise will be retried.\n\tWillRetry bool\n\t// Cause is the underlying error returned from the advertise call.\n\tCause error\n}\n\nfunc (e ErrAdvertiseFailed) Error() string {\n\treturn fmt.Sprintf(\"advertise failed, retry: %v, cause: %v\", e.WillRetry, e.Cause)\n}\n\n// fuzzInterval returns a fuzzed version of the interval based on FullJitter as described here:\n// http://www.awsarchitectureblog.com/2015/03/backoff.html\nfunc fuzzInterval(interval time.Duration) time.Duration {\n\treturn time.Duration(rand.Int63n(int64(interval)))\n}\n\n// fuzzedAdvertiseInterval returns the time to sleep between successful advertisements.\nfunc (c *Client) fuzzedAdvertiseInterval() time.Duration {\n\treturn advertiseInterval + fuzzInterval(advertiseFuzzInterval)\n}\n\n// logFailedRegistrationRetry logs either a warning or info depending on the number of\n// consecutiveFailures. If consecutiveFailures > maxAdvertiseFailures, then we log a warning.\nfunc (c *Client) logFailedRegistrationRetry(errLogger tchannel.Logger, consecutiveFailures uint) {\n\tlogFn := errLogger.Info\n\tif consecutiveFailures > maxAdvertiseFailures {\n\t\tlogFn = errLogger.Warn\n\t}\n\n\tlogFn(\"Hyperbahn client registration failed, will retry.\")\n}\n\n// advertiseLoop readvertises the service approximately every minute (with some fuzzing).\nfunc (c *Client) advertiseLoop() {\n\tsleepFor := c.fuzzedAdvertiseInterval()\n\tconsecutiveFailures := uint(0)\n\n\tfor {\n\t\tc.sleep(sleepFor)\n\t\tif c.IsClosed() {\n\t\t\tc.tchan.Logger().Infof(\"Hyperbahn client closed\")\n\t\t\treturn\n\t\t}\n\n\t\tif err := c.sendAdvertise(); err != nil {\n\t\t\tconsecutiveFailures++\n\t\t\terrLogger := c.tchan.Logger().WithFields(tchannel.ErrField(err))\n\t\t\tif consecutiveFailures >= maxAdvertiseFailures && c.opts.FailStrategy == FailStrategyFatal {\n\t\t\t\tc.opts.Handler.OnError(ErrAdvertiseFailed{Cause: err, WillRetry: false})\n\t\t\t\terrLogger.Fatal(\"Hyperbahn client registration failed.\")\n\t\t\t}\n\n\t\t\tc.logFailedRegistrationRetry(errLogger, consecutiveFailures)\n\t\t\tc.opts.Handler.OnError(ErrAdvertiseFailed{Cause: err, WillRetry: true})\n\n\t\t\t// Even after many failures, cap backoff.\n\t\t\tif consecutiveFailures < maxAdvertiseFailures {\n\t\t\t\tsleepFor = fuzzInterval(advertiseRetryInterval * time.Duration(1<<consecutiveFailures))\n\t\t\t}\n\t\t} else {\n\t\t\tc.opts.Handler.On(Readvertised)\n\t\t\tsleepFor = c.fuzzedAdvertiseInterval()\n\t\t\tconsecutiveFailures = 0\n\t\t}\n\t}\n}\n\n// initialAdvertise will do the initial Advertise call to Hyperbahn with additional\n// retries on top of the built-in TChannel retries. It will use exponential backoff\n// between each of the call attempts.\nfunc (c *Client) initialAdvertise() error {\n\tvar err error\n\tfor attempt := uint(0); attempt < maxAdvertiseFailures; attempt++ {\n\t\terr = c.sendAdvertise()\n\t\tif err == nil || err == errEphemeralPeer {\n\t\t\tbreak\n\t\t}\n\n\t\tc.tchan.Logger().WithFields(tchannel.ErrField(err)).Info(\n\t\t\t\"Hyperbahn client initial registration failure, will retry\")\n\n\t\t// Back off for a while.\n\t\tsleepFor := fuzzInterval(advertiseRetryInterval * time.Duration(1<<attempt))\n\t\tc.sleep(sleepFor)\n\t}\n\treturn err\n}\n\nfunc (c *Client) sleep(d time.Duration) {\n\tc.opts.TimeSleep(d)\n}\n"
  },
  {
    "path": "hyperbahn/advertise_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage hyperbahn\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/json\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestInitialAdvertiseFailedRetryBackoff(t *testing.T) {\n\tdefer testutils.SetTimeout(t, 2*time.Second)()\n\n\tclientOpts := stubbedSleep()\n\tsleepArgs, sleepBlock, sleepClose := testutils.SleepStub(&clientOpts.TimeSleep)\n\n\t// We expect to retry 5 times,\n\tgo func() {\n\t\tfor attempt := uint(0); attempt < 5; attempt++ {\n\t\t\tmaxSleepFor := advertiseRetryInterval * time.Duration(1<<attempt)\n\t\t\tgot := <-sleepArgs\n\t\t\tassert.True(t, got <= maxSleepFor,\n\t\t\t\t\"Initial advertise attempt %v expected sleep %v < %v\", attempt, got, maxSleepFor)\n\t\t\tsleepBlock <- struct{}{}\n\t\t}\n\t\tsleepClose()\n\t}()\n\n\twithSetup(t, func(hypCh *tchannel.Channel, hostPort string) {\n\t\tserverCh := testutils.NewServer(t, nil)\n\t\tdefer serverCh.Close()\n\n\t\tclient, err := NewClient(serverCh, configFor(hostPort), clientOpts)\n\t\trequire.NoError(t, err, \"NewClient\")\n\t\tdefer client.Close()\n\t\tassert.Error(t, client.Advertise(), \"Advertise without handler should fail\")\n\t})\n}\n\nfunc TestInitialAdvertiseFailedRetryTimeout(t *testing.T) {\n\twithSetup(t, func(hypCh *tchannel.Channel, hyperbahnHostPort string) {\n\t\tstarted := time.Now()\n\t\tcount := 0\n\t\tadHandler := func(ctx json.Context, req *AdRequest) (*AdResponse, error) {\n\t\t\tcount++\n\n\t\t\tdeadline, ok := ctx.Deadline()\n\t\t\tif assert.True(t, ok, \"context is missing Deadline\") {\n\t\t\t\tassert.True(t, deadline.Sub(started) <= 2*time.Second,\n\t\t\t\t\t\"Timeout per attempt should be 1 second. Started: %v Deadline: %v\", started, deadline)\n\t\t\t}\n\n\t\t\treturn nil, tchannel.NewSystemError(tchannel.ErrCodeUnexpected, \"unexpected\")\n\t\t}\n\t\tjson.Register(hypCh, json.Handlers{\"ad\": adHandler}, nil)\n\n\t\tch := testutils.NewServer(t, nil)\n\t\tclient, err := NewClient(ch, configFor(hyperbahnHostPort), stubbedSleep())\n\t\tassert.NoError(t, err, \"hyperbahn NewClient failed\")\n\t\tdefer client.Close()\n\n\t\tassert.Error(t, client.Advertise(), \"Advertise should not succeed\")\n\t\t// We expect 5 retries by TChannel and we attempt 5 to advertise 5 times.\n\t\tassert.Equal(t, 5*5, count, \"adHandler not retried correct number of times\")\n\t})\n}\n\nfunc TestNotListeningChannel(t *testing.T) {\n\twithSetup(t, func(hypCh *tchannel.Channel, hyperbahnHostPort string) {\n\t\tadHandler := func(ctx json.Context, req *AdRequest) (*AdResponse, error) {\n\t\t\treturn &AdResponse{1}, nil\n\t\t}\n\t\tjson.Register(hypCh, json.Handlers{\"ad\": adHandler}, nil)\n\n\t\tch := testutils.NewClient(t, nil)\n\t\tclient, err := NewClient(ch, configFor(hyperbahnHostPort), stubbedSleep())\n\t\tassert.NoError(t, err, \"hyperbahn NewClient failed\")\n\t\tdefer client.Close()\n\n\t\tassert.Equal(t, errEphemeralPeer, client.Advertise(), \"Advertise without Listen should fail\")\n\t})\n}\n\ntype retryTest struct {\n\t// channel used to control the response to an 'ad' call.\n\trespCh chan int\n\t// reqCh contains the AdRequests sent to the adHandler.\n\treqCh chan *AdRequest\n\n\t// sleep stub channels.\n\tsleepArgs  <-chan time.Duration\n\tsleepBlock chan<- struct{}\n\tsleepClose func()\n\ttimeSleep  func(time.Duration)\n\n\tch     *tchannel.Channel\n\tclient *Client\n\tmock   mock.Mock\n}\n\nfunc (r *retryTest) On(event Event) {\n\tr.mock.Called(event)\n}\nfunc (r *retryTest) OnError(err error) {\n\tr.mock.Called(err)\n}\n\nfunc (r *retryTest) adHandler(ctx json.Context, req *AdRequest) (*AdResponse, error) {\n\tr.reqCh <- req\n\tv := <-r.respCh\n\tif v == 0 {\n\t\treturn nil, errors.New(\"failed\")\n\t}\n\treturn &AdResponse{v}, nil\n}\n\nfunc (r *retryTest) setup() {\n\tr.respCh = make(chan int, 1)\n\tr.reqCh = make(chan *AdRequest, 1)\n\tr.sleepArgs, r.sleepBlock, r.sleepClose = testutils.SleepStub(&r.timeSleep)\n}\n\nfunc (r *retryTest) setAdvertiseSuccess() {\n\tr.respCh <- 1\n}\n\nfunc (r *retryTest) setAdvertiseFailure() {\n\tr.respCh <- 0\n}\n\nfunc runRetryTest(t *testing.T, f func(r *retryTest)) {\n\tr := &retryTest{}\n\tdefer testutils.SetTimeout(t, 2*time.Second)()\n\tr.setup()\n\n\twithSetup(t, func(hypCh *tchannel.Channel, hostPort string) {\n\t\tjson.Register(hypCh, json.Handlers{\"ad\": r.adHandler}, nil)\n\n\t\t// Advertise failures cause warning log messages.\n\t\topts := testutils.NewOpts().\n\t\t\tSetServiceName(\"my-client\").\n\t\t\tAddLogFilter(\"Hyperbahn client registration failed\", 10)\n\t\tserverCh := testutils.NewServer(t, opts)\n\t\tdefer serverCh.Close()\n\n\t\tvar err error\n\t\tr.ch = serverCh\n\t\tr.client, err = NewClient(serverCh, configFor(hostPort), &ClientOptions{\n\t\t\tHandler:      r,\n\t\t\tFailStrategy: FailStrategyIgnore,\n\t\t\tTimeSleep:    r.timeSleep,\n\t\t})\n\t\trequire.NoError(t, err, \"NewClient\")\n\t\tdefer r.client.Close()\n\t\tf(r)\n\t\tr.mock.AssertExpectations(t)\n\t})\n}\n\nfunc TestAdvertiseSuccess(t *testing.T) {\n\trunRetryTest(t, func(r *retryTest) {\n\t\tr.mock.On(\"On\", SendAdvertise).Return().\n\t\t\tTimes(1 /* initial */ + 10 /* successful retries */)\n\t\tr.mock.On(\"On\", Advertised).Return().Once()\n\t\tr.setAdvertiseSuccess()\n\t\trequire.NoError(t, r.client.Advertise())\n\n\t\t// Verify that the arguments passed to 'ad' are correct.\n\t\texpectedRequest := &AdRequest{[]service{{Name: \"my-client\", Cost: 0}}}\n\t\trequire.Equal(t, expectedRequest, <-r.reqCh)\n\n\t\t// Verify readvertise happen after sleeping for ~advertiseInterval.\n\t\tr.mock.On(\"On\", Readvertised).Return().Times(10)\n\t\tfor i := 0; i < 10; i++ {\n\t\t\ts1 := <-r.sleepArgs\n\t\t\tcheckAdvertiseInterval(t, s1)\n\t\t\tr.sleepBlock <- struct{}{}\n\n\t\t\tr.setAdvertiseSuccess()\n\t\t\trequire.Equal(t, expectedRequest, <-r.reqCh)\n\t\t}\n\n\t\t// Block till the last advertise completes.\n\t\t<-r.sleepArgs\n\t})\n}\n\nfunc TestMutlipleAdvertise(t *testing.T) {\n\trunRetryTest(t, func(r *retryTest) {\n\t\tr.mock.On(\"On\", SendAdvertise).Return().\n\t\t\tTimes(1 /* initial */ + 10 /* successful retries */)\n\t\tr.mock.On(\"On\", Advertised).Return().Once()\n\t\tr.setAdvertiseSuccess()\n\n\t\tsc2, sc3 := r.ch.GetSubChannel(\"svc-2\"), r.ch.GetSubChannel(\"svc-3\")\n\t\trequire.NoError(t, r.client.Advertise(sc2, sc3))\n\n\t\t// Verify that the arguments passed to 'ad' are correct.\n\t\texpectedRequest := &AdRequest{[]service{\n\t\t\t{Name: \"my-client\", Cost: 0},\n\t\t\t{Name: \"svc-2\", Cost: 0},\n\t\t\t{Name: \"svc-3\", Cost: 0},\n\t\t}}\n\t\trequire.Equal(t, expectedRequest, <-r.reqCh)\n\n\t\t// Verify readvertise happen after sleeping for ~advertiseInterval.\n\t\tr.mock.On(\"On\", Readvertised).Return().Times(10)\n\t\tfor i := 0; i < 10; i++ {\n\t\t\ts1 := <-r.sleepArgs\n\t\t\tcheckAdvertiseInterval(t, s1)\n\t\t\tr.sleepBlock <- struct{}{}\n\n\t\t\tr.setAdvertiseSuccess()\n\t\t\trequire.Equal(t, expectedRequest, <-r.reqCh)\n\t\t}\n\n\t\t// Block till the last advertise completes.\n\t\t<-r.sleepArgs\n\t})\n}\n\nvar advertiseErr = json.ErrApplication{\"type\": \"error\", \"message\": \"failed\"}\n\nfunc TestRetryTemporaryFailure(t *testing.T) {\n\trunRetryTest(t, func(r *retryTest) {\n\t\tr.mock.On(\"On\", SendAdvertise).Return().\n\t\t\tTimes(1 /* initial */ + 3 /* fail */ + 10 /* successful */)\n\t\tr.mock.On(\"On\", Advertised).Return().Once()\n\t\tr.setAdvertiseSuccess()\n\t\trequire.NoError(t, r.client.Advertise())\n\t\t<-r.reqCh\n\n\t\ts1 := <-r.sleepArgs\n\t\tcheckAdvertiseInterval(t, s1)\n\n\t\t// When registrations fail, it retries after a short connection and triggers OnError.\n\t\tr.mock.On(\"OnError\", ErrAdvertiseFailed{true, advertiseErr}).Return(nil).Times(3)\n\t\tfor i := 0; i < 3; i++ {\n\t\t\tr.sleepBlock <- struct{}{}\n\t\t\tr.setAdvertiseFailure()\n\n\t\t\ts1 := <-r.sleepArgs\n\t\t\t<-r.reqCh\n\t\t\tcheckRetryInterval(t, s1, i+1 /* retryNum */)\n\t\t}\n\n\t\t// If the retry suceeds, then it goes back to normal.\n\t\tr.mock.On(\"On\", Readvertised).Return().Times(10)\n\t\t// Verify re-registrations continue as usual when it succeeds.\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tr.sleepBlock <- struct{}{}\n\t\t\tr.setAdvertiseSuccess()\n\n\t\t\ts1 := <-r.sleepArgs\n\t\t\t<-r.reqCh\n\t\t\tcheckAdvertiseInterval(t, s1)\n\t\t}\n\t})\n}\n\nfunc TestRetryFailure(t *testing.T) {\n\trunRetryTest(t, func(r *retryTest) {\n\t\tr.mock.On(\"On\", Advertised).Return().Once()\n\t\tr.mock.On(\"On\", SendAdvertise).Return().Times(1 + (maxAdvertiseFailures * 2))\n\n\t\t// Since FailStrategyIgnore will keep retrying forever, we need to\n\t\t// signal when we're done testing.\n\t\tdoneTesting := make(chan struct{}, 1)\n\t\t// For all but the last failure, we just need to assert that the\n\t\t// OnError handler was called. The callback for the last failure needs\n\t\t// to signal that we're done testing and close the Hyperbahn client (so\n\t\t// that the Advertise call returns).\n\t\texpectationCalled := 0\n\t\tr.mock.On(\"OnError\", ErrAdvertiseFailed{true, advertiseErr}).Return(nil).Times(maxAdvertiseFailures * 2).Run(func(_ mock.Arguments) {\n\t\t\texpectationCalled++\n\t\t\tif expectationCalled >= maxAdvertiseFailures*2 {\n\t\t\t\tclose(doneTesting)\n\t\t\t\tr.client.Close()\n\t\t\t}\n\t\t})\n\t\t// For the last failure, we assert that the handler was called and\n\t\t// signal that the test is done.\n\n\t\tr.setAdvertiseSuccess()\n\t\trequire.NoError(t, r.client.Advertise())\n\t\t<-r.reqCh\n\n\t\tsleptFor := <-r.sleepArgs\n\t\tcheckAdvertiseInterval(t, sleptFor)\n\n\t\t// Even after maxRegistrationFailures failures to register with\n\t\t// Hyperbahn, FailStrategyIgnore should keep retrying.\n\t\tfor i := 1; i <= maxAdvertiseFailures*2; i++ {\n\t\t\tr.sleepBlock <- struct{}{}\n\t\t\tr.setAdvertiseFailure()\n\t\t\t<-r.reqCh\n\n\t\t\tsleptFor := <-r.sleepArgs\n\n\t\t\t// Make sure that we cap backoff at some reasonable duration, even\n\t\t\t// after many retries.\n\t\t\tif i <= maxAdvertiseFailures {\n\t\t\t\tcheckRetryInterval(t, sleptFor, i)\n\t\t\t} else {\n\t\t\t\tcheckRetryInterval(t, sleptFor, maxAdvertiseFailures)\n\t\t\t}\n\t\t}\n\n\t\tr.sleepClose()\n\n\t\t// Wait for the handler to be called and the mock expectation to be recorded.\n\t\t<-doneTesting\n\t})\n}\n\nfunc checkAdvertiseInterval(t *testing.T, sleptFor time.Duration) {\n\tassert.True(t, sleptFor >= advertiseInterval,\n\t\t\"advertise interval should be > advertiseInterval\")\n\tassert.True(t, sleptFor < advertiseInterval+advertiseFuzzInterval,\n\t\t\"advertise interval should be < advertiseInterval + advertiseFuzzInterval\")\n}\n\nfunc checkRetryInterval(t *testing.T, sleptFor time.Duration, retryNum int) {\n\tmaxRetryInterval := advertiseRetryInterval * time.Duration(1<<uint8(retryNum))\n\tassert.True(t, sleptFor < maxRetryInterval,\n\t\t\"retry #%v slept for %v, should sleep for less than %v\", retryNum, sleptFor, maxRetryInterval)\n}\n\nfunc configFor(node string) Configuration {\n\treturn Configuration{\n\t\tInitialNodes: []string{node},\n\t}\n}\n\nfunc stubbedSleep() *ClientOptions {\n\treturn &ClientOptions{\n\t\tTimeSleep: func(_ time.Duration) {},\n\t}\n}\n\nfunc withSetup(t *testing.T, f func(ch *tchannel.Channel, hostPort string)) {\n\tserverCh, err := tchannel.NewChannel(hyperbahnServiceName, nil)\n\trequire.NoError(t, err)\n\tdefer serverCh.Close()\n\tlistener, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\trequire.NoError(t, err)\n\tserverCh.Serve(listener)\n\n\tf(serverCh, listener.Addr().String())\n\tserverCh.Close()\n}\n"
  },
  {
    "path": "hyperbahn/call.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage hyperbahn\n\nimport (\n\t\"errors\"\n\n\t\"github.com/uber/tchannel-go\"\n)\n\nvar errEphemeralPeer = errors.New(\"cannot advertise on channel that has not called ListenAndServe\")\n\n// The following parameters define the request/response for the Hyperbahn 'ad' call.\ntype service struct {\n\tName string `json:\"serviceName\"`\n\tCost int    `json:\"cost\"`\n}\n\n// AdRequest is the Ad request sent to Hyperbahn.\ntype AdRequest struct {\n\tServices []service `json:\"services\"`\n}\n\n// AdResponse is the Ad response from Hyperbahn.\ntype AdResponse struct {\n\tConnectionCount int `json:\"connectionCount\"`\n}\n\nfunc (c *Client) createRequest() *AdRequest {\n\treq := &AdRequest{\n\t\tServices: make([]service, len(c.services)),\n\t}\n\tfor i, s := range c.services {\n\t\treq.Services[i] = service{\n\t\t\tName: s,\n\t\t\tCost: 0,\n\t\t}\n\t}\n\treturn req\n}\n\nfunc (c *Client) sendAdvertise() error {\n\t// Cannot advertise from an ephemeral peer.\n\tif c.tchan.PeerInfo().IsEphemeralHostPort() {\n\t\treturn errEphemeralPeer\n\t}\n\n\tretryOpts := &tchannel.RetryOptions{\n\t\tRetryOn:           tchannel.RetryIdempotent,\n\t\tTimeoutPerAttempt: c.opts.TimeoutPerAttempt,\n\t}\n\n\tctx, cancel := tchannel.NewContextBuilder(c.opts.Timeout).\n\t\tSetRetryOptions(retryOpts).\n\t\t// Disable tracing on Hyperbahn advertise messages to avoid cascading failures (see #790).\n\t\tDisableTracing().\n\t\tBuild()\n\tdefer cancel()\n\n\tvar resp AdResponse\n\tc.opts.Handler.On(SendAdvertise)\n\treturn c.jsonClient.Call(ctx, \"ad\", c.createRequest(), &resp)\n}\n"
  },
  {
    "path": "hyperbahn/client.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage hyperbahn\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\thtypes \"github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn\"\n\ttjson \"github.com/uber/tchannel-go/json\"\n\ttthrift \"github.com/uber/tchannel-go/thrift\"\n)\n\n// Client manages Hyperbahn connections and registrations.\ntype Client struct {\n\ttchan    *tchannel.Channel\n\tservices []string\n\topts     ClientOptions\n\tquit     chan struct{}\n\n\tjsonClient      *tjson.Client\n\thyperbahnClient htypes.TChanHyperbahn\n}\n\n// FailStrategy is the strategy to use when registration fails maxRegistrationFailures\n// times consecutively in the background. This is not used if the initial registration fails.\ntype FailStrategy int\n\nconst (\n\t// FailStrategyFatal will call Fatalf on the channel's logger after triggering handler.OnError.\n\t// This is the default strategy.\n\tFailStrategyFatal FailStrategy = iota\n\t// FailStrategyIgnore will only call handler.OnError, even after many\n\t// errors, and will continue to retry forever.\n\tFailStrategyIgnore\n)\n\nconst hyperbahnServiceName = \"hyperbahn\"\n\n// UnmarshalText implements encoding/text.Unmarshaler.\n// This allows FailStrategy to be specified as a string in many\n// file formats (e.g. JSON, YAML, TOML).\nfunc (f *FailStrategy) UnmarshalText(text []byte) error {\n\tswitch strategy := string(text); strategy {\n\tcase \"\", \"fatal\":\n\t\t*f = FailStrategyFatal\n\tcase \"ignore\":\n\t\t*f = FailStrategyIgnore\n\tdefault:\n\t\treturn fmt.Errorf(\"not a valid fail strategy: %q\", strategy)\n\t}\n\treturn nil\n}\n\n// ClientOptions are used to configure this Hyperbahn client.\ntype ClientOptions struct {\n\t// Timeout defaults to 3 seconds if it is not set.\n\tTimeout time.Duration\n\t// TimeoutPerAttempt defaults to 1 second if it is not set.\n\tTimeoutPerAttempt time.Duration\n\tHandler           Handler\n\tFailStrategy      FailStrategy\n\n\t// The following are variables for stubbing in unit tests.\n\t// They are not part of the stable API and may change.\n\tTimeSleep func(d time.Duration)\n}\n\n// NewClient creates a new Hyperbahn client using the given channel.\n// config is the environment-specific configuration for Hyperbahn such as the list of initial nodes.\n// opts are optional, and are used to customize the client.\nfunc NewClient(ch *tchannel.Channel, config Configuration, opts *ClientOptions) (*Client, error) {\n\tclient := &Client{tchan: ch, quit: make(chan struct{})}\n\tif opts != nil {\n\t\tclient.opts = *opts\n\t}\n\tif client.opts.Timeout == 0 {\n\t\tclient.opts.Timeout = 3 * time.Second\n\t}\n\tif client.opts.TimeoutPerAttempt == 0 {\n\t\tclient.opts.TimeoutPerAttempt = time.Second\n\t}\n\tif client.opts.Handler == nil {\n\t\tclient.opts.Handler = nullHandler{}\n\t}\n\tif client.opts.TimeSleep == nil {\n\t\tclient.opts.TimeSleep = func(d time.Duration) {\n\t\t\tselect {\n\t\t\tcase <-time.After(d):\n\t\t\t\treturn\n\t\t\tcase <-client.quit:\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := parseConfig(&config); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Add the given initial nodes as peers.\n\tfor _, node := range config.InitialNodes {\n\t\taddPeer(ch, node)\n\t}\n\n\tclient.jsonClient = tjson.NewClient(ch, hyperbahnServiceName, nil)\n\tthriftClient := tthrift.NewClient(ch, hyperbahnServiceName, nil)\n\tclient.hyperbahnClient = htypes.NewTChanHyperbahnClient(thriftClient)\n\n\treturn client, nil\n}\n\n// parseConfig parses the configuration options (e.g. InitialNodesFile)\nfunc parseConfig(config *Configuration) error {\n\tif config.InitialNodesFile != \"\" {\n\t\tf, err := os.Open(config.InitialNodesFile)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer f.Close()\n\n\t\tdecoder := json.NewDecoder(f)\n\t\tif err := decoder.Decode(&config.InitialNodes); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif len(config.InitialNodes) == 0 {\n\t\treturn fmt.Errorf(\"hyperbahn Client requires at least one initial node\")\n\t}\n\n\tfor _, node := range config.InitialNodes {\n\t\tif _, _, err := net.SplitHostPort(node); err != nil {\n\t\t\treturn fmt.Errorf(\"hyperbahn Client got invalid node %v: %v\", node, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// addPeer adds a peer to the Hyperbahn subchannel.\n// TODO(prashant): Start connections to the peers in the background.\nfunc addPeer(ch *tchannel.Channel, hostPort string) {\n\tpeers := ch.GetSubChannel(hyperbahnServiceName).Peers()\n\tpeers.Add(hostPort)\n}\n\nfunc (c *Client) getServiceNames(otherServices []tchannel.Registrar) {\n\tc.services = make([]string, 0, len(otherServices)+1)\n\tc.services = append(c.services, c.tchan.PeerInfo().ServiceName)\n\n\tfor _, s := range otherServices {\n\t\tc.services = append(c.services, s.ServiceName())\n\t}\n}\n\n// Advertise advertises the service with Hyperbahn, and returns any errors on initial advertisement.\n// Advertise can register multiple services hosted on the same endpoint.\n// If the advertisement succeeds, a goroutine is started to re-advertise periodically.\nfunc (c *Client) Advertise(otherServices ...tchannel.Registrar) error {\n\tc.getServiceNames(otherServices)\n\n\tif err := c.initialAdvertise(); err != nil {\n\t\treturn err\n\t}\n\n\tc.opts.Handler.On(Advertised)\n\tgo c.advertiseLoop()\n\treturn nil\n}\n\n// IsClosed returns whether this Client is closed.\nfunc (c *Client) IsClosed() bool {\n\tselect {\n\tcase <-c.quit:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// Close closes the Hyperbahn client, which stops any background re-advertisements.\nfunc (c *Client) Close() {\n\tif !c.IsClosed() {\n\t\tclose(c.quit)\n\t}\n}\n"
  },
  {
    "path": "hyperbahn/client_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage hyperbahn\n\nimport (\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"gopkg.in/yaml.v2\"\n)\n\nfunc getPeers(ch *tchannel.Channel) []string {\n\tvar peers []string\n\tfor k := range ch.Peers().Copy() {\n\t\tpeers = append(peers, k)\n\t}\n\tsort.Strings(peers)\n\treturn peers\n}\n\nfunc TestParseConfiguration(t *testing.T) {\n\tpeers1 := []string{\"1.1.1.1:1\", \"2.2.2.2:2\"}\n\tpeers2 := []string{\"3.3.3.3:3\", \"4.4.4.4:4\"}\n\tinvalidPeer1 := []string{\"2:2:2:2\"}\n\tinvalidPeer2 := []string{\"2.2.2.2\"}\n\tpeersFile2 := `[\"3.3.3.3:3\", \"4.4.4.4:4\"]`\n\n\ttests := []struct {\n\t\tname      string\n\t\tpeersArg  []string\n\t\tpeersFile string\n\t\twantPeers []string\n\t\twantErr   bool\n\t}{\n\t\t{\n\t\t\tname:    \"no peers\",\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname:      \"no peer list\",\n\t\t\tpeersArg:  peers1,\n\t\t\twantPeers: peers1,\n\t\t},\n\t\t{\n\t\t\tname:     \"invalid peers invalid format\",\n\t\t\tpeersArg: invalidPeer1,\n\t\t\twantErr:  true,\n\t\t},\n\t\t{\n\t\t\tname:     \"invalid peers no port\",\n\t\t\tpeersArg: invalidPeer2,\n\t\t\twantErr:  true,\n\t\t},\n\t\t{\n\t\t\tname:      \"peer file\",\n\t\t\tpeersFile: peersFile2,\n\t\t\twantPeers: peers2,\n\t\t},\n\t\t{\n\t\t\tname:      \"peer file overrides args\",\n\t\t\tpeersArg:  peers1,\n\t\t\tpeersFile: peersFile2,\n\t\t\twantPeers: peers2,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tpeerFile := \"\"\n\t\tif tt.peersFile != \"\" {\n\t\t\tf, err := ioutil.TempFile(\"\", \"hosts\")\n\t\t\tif !assert.NoError(t, err, \"%v: TempFile failed\", tt.name) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdefer os.Remove(f.Name())\n\n\t\t\t_, err = f.WriteString(tt.peersFile)\n\t\t\tassert.NoError(t, err, \"%v: write peer file failed\", tt.name)\n\n\t\t\tassert.NoError(t, err, \"%v: write peer file failed\", tt.name)\n\t\t\tassert.NoError(t, f.Close(), \"%v: close peer file failed\", tt.name)\n\t\t\tpeerFile = f.Name()\n\t\t}\n\n\t\tconfig := Configuration{InitialNodes: tt.peersArg, InitialNodesFile: peerFile}\n\t\tch := testutils.NewClient(t, nil)\n\t\tdefer ch.Close()\n\n\t\t_, err := NewClient(ch, config, nil)\n\t\tif tt.wantErr {\n\t\t\tassert.Error(t, err, \"%v: NewClient expected to fail\")\n\t\t\tcontinue\n\t\t}\n\t\tif !assert.NoError(t, err, \"%v: hyperbahn.NewClient failed\", tt.name) {\n\t\t\tcontinue\n\t\t}\n\n\t\tassert.Equal(t, tt.wantPeers, getPeers(ch), \"%v: got unexpected peers\", tt.name)\n\t}\n}\n\nfunc TestUnmarshalFailStrategyFormats(t *testing.T) {\n\ttype appConfig struct {\n\t\tName string       `json:\"name\" yaml:\"name\"`\n\t\tFS   FailStrategy `json:\"strategy\" yaml:\"strategy\"`\n\t}\n\texpected := appConfig{\n\t\tName: \"test\",\n\t\tFS:   FailStrategyIgnore,\n\t}\n\tjsonTest := `\n\t\t{\n\t\t\t\"name\": \"test\",\n\t\t\t\"strategy\": \"ignore\"\n\t\t}\n\t`\n\tyamlTest := `\nname: test\nstrategy: ignore\n`\n\n\tvar jsonConfig appConfig\n\terr := json.Unmarshal([]byte(jsonTest), &jsonConfig)\n\tif assert.NoError(t, err, \"JSON unmarshal failed\") {\n\t\tassert.Equal(t, expected, jsonConfig, \"JSON config mismatch\")\n\t}\n\n\tvar yamlConfig appConfig\n\terr = yaml.Unmarshal([]byte(yamlTest), &yamlConfig)\n\tif assert.NoError(t, err, \"YAML unmarshal failed\") {\n\t\tassert.Equal(t, expected, yamlConfig, \"YAML config mismatch\")\n\t}\n}\n\nfunc TestUnmarshalText(t *testing.T) {\n\ttests := []struct {\n\t\tstrategy string\n\t\texpected FailStrategy\n\t\twantErr  bool\n\t}{\n\t\t{\n\t\t\tstrategy: \"fatal\",\n\t\t\texpected: FailStrategyFatal,\n\t\t},\n\t\t{\n\t\t\tstrategy: \"ignore\",\n\t\t\texpected: FailStrategyIgnore,\n\t\t},\n\t\t{\n\t\t\tstrategy: \"unknown\",\n\t\t\twantErr:  true,\n\t\t},\n\t\t{\n\t\t\t// Empty string should use the default which is FailStrategyFatal.\n\t\t\tstrategy: \"\",\n\t\t\texpected: FailStrategyFatal,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tvar fs FailStrategy\n\t\terr := fs.UnmarshalText([]byte(tt.strategy))\n\t\tif tt.wantErr {\n\t\t\tassert.Error(t, err, \"Unmarshal %v should fail\", tt.strategy)\n\t\t} else {\n\t\t\tassert.NoError(t, err, \"Unmarshal %v shouldn't fail\", tt.strategy)\n\t\t}\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tassert.Equal(t, tt.expected, fs, \"FailStrategy mismatch\")\n\t}\n}\n"
  },
  {
    "path": "hyperbahn/configuration.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage hyperbahn\n\n// Configuration is the initial configuration\ntype Configuration struct {\n\t// InitialNodes is the list of known Hyperbahn nodes to add initially.\n\tInitialNodes []string\n\t// InitialNodesFile is a JSON file that contains the list of known Hyperbahn nodes.\n\t// If this option is set, it overrides InitialNodes.\n\tInitialNodesFile string\n}\n"
  },
  {
    "path": "hyperbahn/discover.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage hyperbahn\n\nimport (\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn\"\n\t\"github.com/uber/tchannel-go/thrift\"\n)\n\n// Discover queries Hyperbahn for a list of peers that are currently\n// advertised with the specified service name.\nfunc (c *Client) Discover(serviceName string) ([]string, error) {\n\tctx, cancel := thrift.NewContext(time.Second)\n\tdefer cancel()\n\n\tresult, err := c.hyperbahnClient.Discover(ctx, &hyperbahn.DiscoveryQuery{ServiceName: serviceName})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar hostPorts []string\n\tfor _, peer := range result.GetPeers() {\n\t\thostPorts = append(hostPorts, servicePeerToHostPort(peer))\n\t}\n\n\treturn hostPorts, nil\n}\n"
  },
  {
    "path": "hyperbahn/discover_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage hyperbahn_test\n\nimport (\n\t\"testing\"\n\n\t. \"github.com/uber/tchannel-go/hyperbahn\"\n\n\t\"github.com/uber/tchannel-go/testutils\"\n\t\"github.com/uber/tchannel-go/testutils/mockhyperbahn\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc withSetup(t *testing.T, f func(mh *mockhyperbahn.Mock, client *Client)) {\n\tmh, err := mockhyperbahn.New()\n\trequire.NoError(t, err, \"mockhyperbahn.New failed\")\n\tdefer mh.Close()\n\n\tclientCh := testutils.NewClient(t, nil)\n\tclient, err := NewClient(clientCh, mh.Configuration(), nil)\n\trequire.NoError(t, err, \"NewClient failed\")\n\tdefer clientCh.Close()\n\n\tf(mh, client)\n}\n\nfunc TestDiscoverSuccess(t *testing.T) {\n\tpeers := []string{\n\t\t\"127.0.0.1:8000\",\n\t\t\"127.0.0.2:8001\",\n\t\t\"1.2.4.8:21150\",\n\t\t\"10.254.254.1:21151\",\n\t}\n\n\twithSetup(t, func(mh *mockhyperbahn.Mock, client *Client) {\n\t\tmh.SetDiscoverResult(\"test-discover\", peers)\n\n\t\tgotPeers, err := client.Discover(\"test-discover\")\n\t\tassert.NoError(t, err, \"Discover failed\")\n\t\tassert.Equal(t, peers, gotPeers)\n\t})\n}\n\nfunc TestDiscoverFails(t *testing.T) {\n\twithSetup(t, func(mh *mockhyperbahn.Mock, client *Client) {\n\t\t_, err := client.Discover(\"test-discover\")\n\t\tassert.Error(t, err, \"Discover should fail due to no discover result set in the mock\")\n\t})\n}\n"
  },
  {
    "path": "hyperbahn/event_string.go",
    "content": "// generated by stringer -type=Event; DO NOT EDIT\n\npackage hyperbahn\n\nimport \"fmt\"\n\nconst _Event_name = \"UnknownEventRegistrationAttemptRegisteredRegistrationRefreshed\"\n\nvar _Event_index = [...]uint8{0, 12, 31, 41, 62}\n\nfunc (i Event) String() string {\n\tif i < 0 || i+1 >= Event(len(_Event_index)) {\n\t\treturn fmt.Sprintf(\"Event(%d)\", i)\n\t}\n\treturn _Event_name[_Event_index[i]:_Event_index[i+1]]\n}\n"
  },
  {
    "path": "hyperbahn/events.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage hyperbahn\n\n// Event describes different events that Client can trigger.\ntype Event int\n\nconst (\n\t// UnknownEvent should never be used.\n\tUnknownEvent Event = iota\n\t// SendAdvertise is triggered when the Hyperbahn client tries to advertise.\n\tSendAdvertise\n\t// Advertised is triggered when the initial advertisement for a service is successful.\n\tAdvertised\n\t// Readvertised is triggered on periodic advertisements.\n\tReadvertised\n)\n\n//go:generate stringer -type=Event\n\n// Handler is the interface for handling Hyperbahn events and errors.\ntype Handler interface {\n\t// On is called when events are triggered.\n\tOn(event Event)\n\t// OnError is called when an error is detected.\n\tOnError(err error)\n}\n\n// nullHandler is the default Handler if nil is passed, so handlers can always be called.\ntype nullHandler struct{}\n\nfunc (nullHandler) On(event Event)    {}\nfunc (nullHandler) OnError(err error) {}\n"
  },
  {
    "path": "hyperbahn/gen-go/hyperbahn/constants.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage hyperbahn\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\nfunc init() {\n}\n"
  },
  {
    "path": "hyperbahn/gen-go/hyperbahn/hyperbahn.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage hyperbahn\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\ntype Hyperbahn interface {\n\t// Parameters:\n\t//  - Query\n\tDiscover(query *DiscoveryQuery) (r *DiscoveryResult_, err error)\n}\n\ntype HyperbahnClient struct {\n\tTransport       thrift.TTransport\n\tProtocolFactory thrift.TProtocolFactory\n\tInputProtocol   thrift.TProtocol\n\tOutputProtocol  thrift.TProtocol\n\tSeqId           int32\n}\n\nfunc NewHyperbahnClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *HyperbahnClient {\n\treturn &HyperbahnClient{Transport: t,\n\t\tProtocolFactory: f,\n\t\tInputProtocol:   f.GetProtocol(t),\n\t\tOutputProtocol:  f.GetProtocol(t),\n\t\tSeqId:           0,\n\t}\n}\n\nfunc NewHyperbahnClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *HyperbahnClient {\n\treturn &HyperbahnClient{Transport: t,\n\t\tProtocolFactory: nil,\n\t\tInputProtocol:   iprot,\n\t\tOutputProtocol:  oprot,\n\t\tSeqId:           0,\n\t}\n}\n\n// Parameters:\n//   - Query\nfunc (p *HyperbahnClient) Discover(query *DiscoveryQuery) (r *DiscoveryResult_, err error) {\n\tif err = p.sendDiscover(query); err != nil {\n\t\treturn\n\t}\n\treturn p.recvDiscover()\n}\n\nfunc (p *HyperbahnClient) sendDiscover(query *DiscoveryQuery) (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"discover\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := HyperbahnDiscoverArgs{\n\t\tQuery: query,\n\t}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *HyperbahnClient) recvDiscover() (value *DiscoveryResult_, err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"discover\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"discover failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"discover failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror1 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error2 error\n\t\terror2, err = error1.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error2\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"discover failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := HyperbahnDiscoverResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tif result.NoPeersAvailable != nil {\n\t\terr = result.NoPeersAvailable\n\t\treturn\n\t} else if result.InvalidServiceName != nil {\n\t\terr = result.InvalidServiceName\n\t\treturn\n\t}\n\tvalue = result.GetSuccess()\n\treturn\n}\n\ntype HyperbahnProcessor struct {\n\tprocessorMap map[string]thrift.TProcessorFunction\n\thandler      Hyperbahn\n}\n\nfunc (p *HyperbahnProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {\n\tp.processorMap[key] = processor\n}\n\nfunc (p *HyperbahnProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {\n\tprocessor, ok = p.processorMap[key]\n\treturn processor, ok\n}\n\nfunc (p *HyperbahnProcessor) ProcessorMap() map[string]thrift.TProcessorFunction {\n\treturn p.processorMap\n}\n\nfunc NewHyperbahnProcessor(handler Hyperbahn) *HyperbahnProcessor {\n\n\tself3 := &HyperbahnProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)}\n\tself3.processorMap[\"discover\"] = &hyperbahnProcessorDiscover{handler: handler}\n\treturn self3\n}\n\nfunc (p *HyperbahnProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\tname, _, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif processor, ok := p.GetProcessorFunction(name); ok {\n\t\treturn processor.Process(seqId, iprot, oprot)\n\t}\n\tiprot.Skip(thrift.STRUCT)\n\tiprot.ReadMessageEnd()\n\tx4 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function \"+name)\n\toprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)\n\tx4.Write(oprot)\n\toprot.WriteMessageEnd()\n\toprot.Flush()\n\treturn false, x4\n\n}\n\ntype hyperbahnProcessorDiscover struct {\n\thandler Hyperbahn\n}\n\nfunc (p *hyperbahnProcessorDiscover) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := HyperbahnDiscoverArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"discover\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := HyperbahnDiscoverResult{}\n\tvar retval *DiscoveryResult_\n\tvar err2 error\n\tif retval, err2 = p.handler.Discover(args.Query); err2 != nil {\n\t\tswitch v := err2.(type) {\n\t\tcase *NoPeersAvailable:\n\t\t\tresult.NoPeersAvailable = v\n\t\tcase *InvalidServiceName:\n\t\t\tresult.InvalidServiceName = v\n\t\tdefault:\n\t\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing discover: \"+err2.Error())\n\t\t\toprot.WriteMessageBegin(\"discover\", thrift.EXCEPTION, seqId)\n\t\t\tx.Write(oprot)\n\t\t\toprot.WriteMessageEnd()\n\t\t\toprot.Flush()\n\t\t\treturn true, err2\n\t\t}\n\t} else {\n\t\tresult.Success = retval\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"discover\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\n// HELPER FUNCTIONS AND STRUCTURES\n\n// Attributes:\n//   - Query\ntype HyperbahnDiscoverArgs struct {\n\tQuery *DiscoveryQuery `thrift:\"query,1,required\" db:\"query\" json:\"query\"`\n}\n\nfunc NewHyperbahnDiscoverArgs() *HyperbahnDiscoverArgs {\n\treturn &HyperbahnDiscoverArgs{}\n}\n\nvar HyperbahnDiscoverArgs_Query_DEFAULT *DiscoveryQuery\n\nfunc (p *HyperbahnDiscoverArgs) GetQuery() *DiscoveryQuery {\n\tif !p.IsSetQuery() {\n\t\treturn HyperbahnDiscoverArgs_Query_DEFAULT\n\t}\n\treturn p.Query\n}\nfunc (p *HyperbahnDiscoverArgs) IsSetQuery() bool {\n\treturn p.Query != nil\n}\n\nfunc (p *HyperbahnDiscoverArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tvar issetQuery bool = false\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetQuery = true\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\tif !issetQuery {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field Query is not set\"))\n\t}\n\treturn nil\n}\n\nfunc (p *HyperbahnDiscoverArgs) ReadField1(iprot thrift.TProtocol) error {\n\tp.Query = &DiscoveryQuery{}\n\tif err := p.Query.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.Query), err)\n\t}\n\treturn nil\n}\n\nfunc (p *HyperbahnDiscoverArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"discover_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *HyperbahnDiscoverArgs) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"query\", thrift.STRUCT, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:query: \", p), err)\n\t}\n\tif err := p.Query.Write(oprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.Query), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:query: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *HyperbahnDiscoverArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"HyperbahnDiscoverArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Success\n//   - NoPeersAvailable\n//   - InvalidServiceName\ntype HyperbahnDiscoverResult struct {\n\tSuccess            *DiscoveryResult_   `thrift:\"success,0\" db:\"success\" json:\"success,omitempty\"`\n\tNoPeersAvailable   *NoPeersAvailable   `thrift:\"noPeersAvailable,1\" db:\"noPeersAvailable\" json:\"noPeersAvailable,omitempty\"`\n\tInvalidServiceName *InvalidServiceName `thrift:\"invalidServiceName,2\" db:\"invalidServiceName\" json:\"invalidServiceName,omitempty\"`\n}\n\nfunc NewHyperbahnDiscoverResult() *HyperbahnDiscoverResult {\n\treturn &HyperbahnDiscoverResult{}\n}\n\nvar HyperbahnDiscoverResult_Success_DEFAULT *DiscoveryResult_\n\nfunc (p *HyperbahnDiscoverResult) GetSuccess() *DiscoveryResult_ {\n\tif !p.IsSetSuccess() {\n\t\treturn HyperbahnDiscoverResult_Success_DEFAULT\n\t}\n\treturn p.Success\n}\n\nvar HyperbahnDiscoverResult_NoPeersAvailable_DEFAULT *NoPeersAvailable\n\nfunc (p *HyperbahnDiscoverResult) GetNoPeersAvailable() *NoPeersAvailable {\n\tif !p.IsSetNoPeersAvailable() {\n\t\treturn HyperbahnDiscoverResult_NoPeersAvailable_DEFAULT\n\t}\n\treturn p.NoPeersAvailable\n}\n\nvar HyperbahnDiscoverResult_InvalidServiceName_DEFAULT *InvalidServiceName\n\nfunc (p *HyperbahnDiscoverResult) GetInvalidServiceName() *InvalidServiceName {\n\tif !p.IsSetInvalidServiceName() {\n\t\treturn HyperbahnDiscoverResult_InvalidServiceName_DEFAULT\n\t}\n\treturn p.InvalidServiceName\n}\nfunc (p *HyperbahnDiscoverResult) IsSetSuccess() bool {\n\treturn p.Success != nil\n}\n\nfunc (p *HyperbahnDiscoverResult) IsSetNoPeersAvailable() bool {\n\treturn p.NoPeersAvailable != nil\n}\n\nfunc (p *HyperbahnDiscoverResult) IsSetInvalidServiceName() bool {\n\treturn p.InvalidServiceName != nil\n}\n\nfunc (p *HyperbahnDiscoverResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 0:\n\t\t\tif err := p.ReadField0(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *HyperbahnDiscoverResult) ReadField0(iprot thrift.TProtocol) error {\n\tp.Success = &DiscoveryResult_{}\n\tif err := p.Success.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.Success), err)\n\t}\n\treturn nil\n}\n\nfunc (p *HyperbahnDiscoverResult) ReadField1(iprot thrift.TProtocol) error {\n\tp.NoPeersAvailable = &NoPeersAvailable{}\n\tif err := p.NoPeersAvailable.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.NoPeersAvailable), err)\n\t}\n\treturn nil\n}\n\nfunc (p *HyperbahnDiscoverResult) ReadField2(iprot thrift.TProtocol) error {\n\tp.InvalidServiceName = &InvalidServiceName{}\n\tif err := p.InvalidServiceName.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.InvalidServiceName), err)\n\t}\n\treturn nil\n}\n\nfunc (p *HyperbahnDiscoverResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"discover_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField0(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *HyperbahnDiscoverResult) writeField0(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSuccess() {\n\t\tif err := oprot.WriteFieldBegin(\"success\", thrift.STRUCT, 0); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 0:success: \", p), err)\n\t\t}\n\t\tif err := p.Success.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.Success), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 0:success: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *HyperbahnDiscoverResult) writeField1(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetNoPeersAvailable() {\n\t\tif err := oprot.WriteFieldBegin(\"noPeersAvailable\", thrift.STRUCT, 1); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:noPeersAvailable: \", p), err)\n\t\t}\n\t\tif err := p.NoPeersAvailable.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.NoPeersAvailable), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:noPeersAvailable: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *HyperbahnDiscoverResult) writeField2(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetInvalidServiceName() {\n\t\tif err := oprot.WriteFieldBegin(\"invalidServiceName\", thrift.STRUCT, 2); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:invalidServiceName: \", p), err)\n\t\t}\n\t\tif err := p.InvalidServiceName.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.InvalidServiceName), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:invalidServiceName: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *HyperbahnDiscoverResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"HyperbahnDiscoverResult(%+v)\", *p)\n}\n"
  },
  {
    "path": "hyperbahn/gen-go/hyperbahn/tchan-hyperbahn.go",
    "content": "// @generated Code generated by thrift-gen. Do not modify.\n\n// Package hyperbahn is generated code used to make or handle TChannel calls using Thrift.\npackage hyperbahn\n\nimport (\n\t\"fmt\"\n\n\tathrift \"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n\t\"github.com/uber/tchannel-go/thrift\"\n)\n\n// Interfaces for the service and client for the services defined in the IDL.\n\n// TChanHyperbahn is the interface that defines the server handler and client interface.\ntype TChanHyperbahn interface {\n\tDiscover(ctx thrift.Context, query *DiscoveryQuery) (*DiscoveryResult_, error)\n}\n\n// Implementation of a client and service handler.\n\ntype tchanHyperbahnClient struct {\n\tthriftService string\n\tclient        thrift.TChanClient\n}\n\nfunc NewTChanHyperbahnInheritedClient(thriftService string, client thrift.TChanClient) *tchanHyperbahnClient {\n\treturn &tchanHyperbahnClient{\n\t\tthriftService,\n\t\tclient,\n\t}\n}\n\n// NewTChanHyperbahnClient creates a client that can be used to make remote calls.\nfunc NewTChanHyperbahnClient(client thrift.TChanClient) TChanHyperbahn {\n\treturn NewTChanHyperbahnInheritedClient(\"Hyperbahn\", client)\n}\n\nfunc (c *tchanHyperbahnClient) Discover(ctx thrift.Context, query *DiscoveryQuery) (*DiscoveryResult_, error) {\n\tvar resp HyperbahnDiscoverResult\n\targs := HyperbahnDiscoverArgs{\n\t\tQuery: query,\n\t}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"discover\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tcase resp.NoPeersAvailable != nil:\n\t\t\terr = resp.NoPeersAvailable\n\t\tcase resp.InvalidServiceName != nil:\n\t\t\terr = resp.InvalidServiceName\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for discover\")\n\t\t}\n\t}\n\n\treturn resp.GetSuccess(), err\n}\n\ntype tchanHyperbahnServer struct {\n\thandler TChanHyperbahn\n}\n\n// NewTChanHyperbahnServer wraps a handler for TChanHyperbahn so it can be\n// registered with a thrift.Server.\nfunc NewTChanHyperbahnServer(handler TChanHyperbahn) thrift.TChanServer {\n\treturn &tchanHyperbahnServer{\n\t\thandler,\n\t}\n}\n\nfunc (s *tchanHyperbahnServer) Service() string {\n\treturn \"Hyperbahn\"\n}\n\nfunc (s *tchanHyperbahnServer) Methods() []string {\n\treturn []string{\n\t\t\"discover\",\n\t}\n}\n\nfunc (s *tchanHyperbahnServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\tcase \"discover\":\n\t\treturn s.handleDiscover(ctx, protocol)\n\n\tdefault:\n\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\nfunc (s *tchanHyperbahnServer) handleDiscover(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req HyperbahnDiscoverArgs\n\tvar res HyperbahnDiscoverResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tr, err :=\n\t\ts.handler.Discover(ctx, req.Query)\n\n\tif err != nil {\n\t\tswitch v := err.(type) {\n\t\tcase *NoPeersAvailable:\n\t\t\tif v == nil {\n\t\t\t\treturn false, nil, fmt.Errorf(\"Handler for noPeersAvailable returned non-nil error type *NoPeersAvailable but nil value\")\n\t\t\t}\n\t\t\tres.NoPeersAvailable = v\n\t\tcase *InvalidServiceName:\n\t\t\tif v == nil {\n\t\t\t\treturn false, nil, fmt.Errorf(\"Handler for invalidServiceName returned non-nil error type *InvalidServiceName but nil value\")\n\t\t\t}\n\t\t\tres.InvalidServiceName = v\n\t\tdefault:\n\t\t\treturn false, nil, err\n\t\t}\n\t} else {\n\t\tres.Success = r\n\t}\n\n\treturn err == nil, &res, nil\n}\n"
  },
  {
    "path": "hyperbahn/gen-go/hyperbahn/ttypes.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage hyperbahn\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\nvar GoUnusedProtection__ int\n\n// Attributes:\n//   - Message\n//   - ServiceName\ntype NoPeersAvailable struct {\n\tMessage     string `thrift:\"message,1,required\" db:\"message\" json:\"message\"`\n\tServiceName string `thrift:\"serviceName,2,required\" db:\"serviceName\" json:\"serviceName\"`\n}\n\nfunc NewNoPeersAvailable() *NoPeersAvailable {\n\treturn &NoPeersAvailable{}\n}\n\nfunc (p *NoPeersAvailable) GetMessage() string {\n\treturn p.Message\n}\n\nfunc (p *NoPeersAvailable) GetServiceName() string {\n\treturn p.ServiceName\n}\nfunc (p *NoPeersAvailable) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tvar issetMessage bool = false\n\tvar issetServiceName bool = false\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetMessage = true\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetServiceName = true\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\tif !issetMessage {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field Message is not set\"))\n\t}\n\tif !issetServiceName {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field ServiceName is not set\"))\n\t}\n\treturn nil\n}\n\nfunc (p *NoPeersAvailable) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Message = v\n\t}\n\treturn nil\n}\n\nfunc (p *NoPeersAvailable) ReadField2(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 2: \", err)\n\t} else {\n\t\tp.ServiceName = v\n\t}\n\treturn nil\n}\n\nfunc (p *NoPeersAvailable) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"NoPeersAvailable\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *NoPeersAvailable) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"message\", thrift.STRING, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:message: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Message)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.message (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:message: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *NoPeersAvailable) writeField2(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"serviceName\", thrift.STRING, 2); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:serviceName: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.ServiceName)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.serviceName (2) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:serviceName: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *NoPeersAvailable) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"NoPeersAvailable(%+v)\", *p)\n}\n\nfunc (p *NoPeersAvailable) Error() string {\n\treturn p.String()\n}\n\n// Attributes:\n//   - Message\n//   - ServiceName\ntype InvalidServiceName struct {\n\tMessage     string `thrift:\"message,1,required\" db:\"message\" json:\"message\"`\n\tServiceName string `thrift:\"serviceName,2,required\" db:\"serviceName\" json:\"serviceName\"`\n}\n\nfunc NewInvalidServiceName() *InvalidServiceName {\n\treturn &InvalidServiceName{}\n}\n\nfunc (p *InvalidServiceName) GetMessage() string {\n\treturn p.Message\n}\n\nfunc (p *InvalidServiceName) GetServiceName() string {\n\treturn p.ServiceName\n}\nfunc (p *InvalidServiceName) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tvar issetMessage bool = false\n\tvar issetServiceName bool = false\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetMessage = true\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetServiceName = true\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\tif !issetMessage {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field Message is not set\"))\n\t}\n\tif !issetServiceName {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field ServiceName is not set\"))\n\t}\n\treturn nil\n}\n\nfunc (p *InvalidServiceName) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Message = v\n\t}\n\treturn nil\n}\n\nfunc (p *InvalidServiceName) ReadField2(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 2: \", err)\n\t} else {\n\t\tp.ServiceName = v\n\t}\n\treturn nil\n}\n\nfunc (p *InvalidServiceName) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"InvalidServiceName\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *InvalidServiceName) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"message\", thrift.STRING, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:message: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Message)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.message (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:message: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *InvalidServiceName) writeField2(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"serviceName\", thrift.STRING, 2); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:serviceName: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.ServiceName)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.serviceName (2) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:serviceName: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *InvalidServiceName) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"InvalidServiceName(%+v)\", *p)\n}\n\nfunc (p *InvalidServiceName) Error() string {\n\treturn p.String()\n}\n\n// Attributes:\n//   - ServiceName\ntype DiscoveryQuery struct {\n\tServiceName string `thrift:\"serviceName,1,required\" db:\"serviceName\" json:\"serviceName\"`\n}\n\nfunc NewDiscoveryQuery() *DiscoveryQuery {\n\treturn &DiscoveryQuery{}\n}\n\nfunc (p *DiscoveryQuery) GetServiceName() string {\n\treturn p.ServiceName\n}\nfunc (p *DiscoveryQuery) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tvar issetServiceName bool = false\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetServiceName = true\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\tif !issetServiceName {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field ServiceName is not set\"))\n\t}\n\treturn nil\n}\n\nfunc (p *DiscoveryQuery) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.ServiceName = v\n\t}\n\treturn nil\n}\n\nfunc (p *DiscoveryQuery) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"DiscoveryQuery\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *DiscoveryQuery) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"serviceName\", thrift.STRING, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:serviceName: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.ServiceName)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.serviceName (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:serviceName: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *DiscoveryQuery) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"DiscoveryQuery(%+v)\", *p)\n}\n\n// Attributes:\n//   - Ipv4\ntype IpAddress struct {\n\tIpv4 *int32 `thrift:\"ipv4,1\" db:\"ipv4\" json:\"ipv4,omitempty\"`\n}\n\nfunc NewIpAddress() *IpAddress {\n\treturn &IpAddress{}\n}\n\nvar IpAddress_Ipv4_DEFAULT int32\n\nfunc (p *IpAddress) GetIpv4() int32 {\n\tif !p.IsSetIpv4() {\n\t\treturn IpAddress_Ipv4_DEFAULT\n\t}\n\treturn *p.Ipv4\n}\nfunc (p *IpAddress) CountSetFieldsIpAddress() int {\n\tcount := 0\n\tif p.IsSetIpv4() {\n\t\tcount++\n\t}\n\treturn count\n\n}\n\nfunc (p *IpAddress) IsSetIpv4() bool {\n\treturn p.Ipv4 != nil\n}\n\nfunc (p *IpAddress) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *IpAddress) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadI32(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Ipv4 = &v\n\t}\n\treturn nil\n}\n\nfunc (p *IpAddress) Write(oprot thrift.TProtocol) error {\n\tif c := p.CountSetFieldsIpAddress(); c != 1 {\n\t\treturn fmt.Errorf(\"%T write union: exactly one field must be set (%d set).\", p, c)\n\t}\n\tif err := oprot.WriteStructBegin(\"IpAddress\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *IpAddress) writeField1(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetIpv4() {\n\t\tif err := oprot.WriteFieldBegin(\"ipv4\", thrift.I32, 1); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:ipv4: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteI32(int32(*p.Ipv4)); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.ipv4 (1) field write error: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:ipv4: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *IpAddress) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"IpAddress(%+v)\", *p)\n}\n\n// Attributes:\n//   - IP\n//   - Port\ntype ServicePeer struct {\n\tIP   *IpAddress `thrift:\"ip,1,required\" db:\"ip\" json:\"ip\"`\n\tPort int32      `thrift:\"port,2,required\" db:\"port\" json:\"port\"`\n}\n\nfunc NewServicePeer() *ServicePeer {\n\treturn &ServicePeer{}\n}\n\nvar ServicePeer_IP_DEFAULT *IpAddress\n\nfunc (p *ServicePeer) GetIP() *IpAddress {\n\tif !p.IsSetIP() {\n\t\treturn ServicePeer_IP_DEFAULT\n\t}\n\treturn p.IP\n}\n\nfunc (p *ServicePeer) GetPort() int32 {\n\treturn p.Port\n}\nfunc (p *ServicePeer) IsSetIP() bool {\n\treturn p.IP != nil\n}\n\nfunc (p *ServicePeer) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tvar issetIP bool = false\n\tvar issetPort bool = false\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetIP = true\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetPort = true\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\tif !issetIP {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field IP is not set\"))\n\t}\n\tif !issetPort {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field Port is not set\"))\n\t}\n\treturn nil\n}\n\nfunc (p *ServicePeer) ReadField1(iprot thrift.TProtocol) error {\n\tp.IP = &IpAddress{}\n\tif err := p.IP.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.IP), err)\n\t}\n\treturn nil\n}\n\nfunc (p *ServicePeer) ReadField2(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadI32(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 2: \", err)\n\t} else {\n\t\tp.Port = v\n\t}\n\treturn nil\n}\n\nfunc (p *ServicePeer) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"ServicePeer\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *ServicePeer) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"ip\", thrift.STRUCT, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:ip: \", p), err)\n\t}\n\tif err := p.IP.Write(oprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.IP), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:ip: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *ServicePeer) writeField2(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"port\", thrift.I32, 2); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:port: \", p), err)\n\t}\n\tif err := oprot.WriteI32(int32(p.Port)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.port (2) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:port: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *ServicePeer) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"ServicePeer(%+v)\", *p)\n}\n\n// Attributes:\n//   - Peers\ntype DiscoveryResult_ struct {\n\tPeers []*ServicePeer `thrift:\"peers,1,required\" db:\"peers\" json:\"peers\"`\n}\n\nfunc NewDiscoveryResult_() *DiscoveryResult_ {\n\treturn &DiscoveryResult_{}\n}\n\nfunc (p *DiscoveryResult_) GetPeers() []*ServicePeer {\n\treturn p.Peers\n}\nfunc (p *DiscoveryResult_) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tvar issetPeers bool = false\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetPeers = true\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\tif !issetPeers {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field Peers is not set\"))\n\t}\n\treturn nil\n}\n\nfunc (p *DiscoveryResult_) ReadField1(iprot thrift.TProtocol) error {\n\t_, size, err := iprot.ReadListBegin()\n\tif err != nil {\n\t\treturn thrift.PrependError(\"error reading list begin: \", err)\n\t}\n\ttSlice := make([]*ServicePeer, 0, size)\n\tp.Peers = tSlice\n\tfor i := 0; i < size; i++ {\n\t\t_elem0 := &ServicePeer{}\n\t\tif err := _elem0.Read(iprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", _elem0), err)\n\t\t}\n\t\tp.Peers = append(p.Peers, _elem0)\n\t}\n\tif err := iprot.ReadListEnd(); err != nil {\n\t\treturn thrift.PrependError(\"error reading list end: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *DiscoveryResult_) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"DiscoveryResult\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *DiscoveryResult_) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"peers\", thrift.LIST, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:peers: \", p), err)\n\t}\n\tif err := oprot.WriteListBegin(thrift.STRUCT, len(p.Peers)); err != nil {\n\t\treturn thrift.PrependError(\"error writing list begin: \", err)\n\t}\n\tfor _, v := range p.Peers {\n\t\tif err := v.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", v), err)\n\t\t}\n\t}\n\tif err := oprot.WriteListEnd(); err != nil {\n\t\treturn thrift.PrependError(\"error writing list end: \", err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:peers: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *DiscoveryResult_) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"DiscoveryResult_(%+v)\", *p)\n}\n"
  },
  {
    "path": "hyperbahn/hyperbahn.thrift",
    "content": "exception NoPeersAvailable {\n    1: required string message\n    2: required string serviceName\n}\n\nexception InvalidServiceName {\n    1: required string message\n    2: required string serviceName\n}\n\nstruct DiscoveryQuery {\n    1: required string serviceName\n}\n\nunion IpAddress {\n  1: i32 ipv4\n}\n\nstruct ServicePeer {\n  1: required IpAddress ip\n  2: required i32 port\n}\n\nstruct DiscoveryResult {\n  1: required list<ServicePeer> peers\n}\n\nservice Hyperbahn {\n    DiscoveryResult discover(\n        1: required DiscoveryQuery query\n    ) throws (\n        1: NoPeersAvailable noPeersAvailable\n        2: InvalidServiceName invalidServiceName\n    )\n}"
  },
  {
    "path": "hyperbahn/utils.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage hyperbahn\n\nimport (\n\t\"net\"\n\t\"strconv\"\n\n\t\"github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn\"\n)\n\n// intToIP4 converts an integer IP representation into a 4-byte net.IP struct\nfunc intToIP4(ip uint32) net.IP {\n\treturn net.IP{\n\t\tbyte(ip >> 24 & 0xff),\n\t\tbyte(ip >> 16 & 0xff),\n\t\tbyte(ip >> 8 & 0xff),\n\t\tbyte(ip & 0xff),\n\t}\n}\n\n// servicePeerToHostPort converts a Hyperbahn ServicePeer into a hostPort string.\nfunc servicePeerToHostPort(peer *hyperbahn.ServicePeer) string {\n\thost := intToIP4(uint32(*peer.IP.Ipv4)).String()\n\tport := strconv.Itoa(int(peer.Port))\n\treturn net.JoinHostPort(host, port)\n}\n"
  },
  {
    "path": "hyperbahn/utils_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage hyperbahn\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestIntToIP4(t *testing.T) {\n\ttests := []struct {\n\t\tip       uint32\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tip:       0,\n\t\t\texpected: \"0.0.0.0\",\n\t\t},\n\t\t{\n\t\t\tip:       0x01010101,\n\t\t\texpected: \"1.1.1.1\",\n\t\t},\n\t\t{\n\t\t\tip:       0x01030507,\n\t\t\texpected: \"1.3.5.7\",\n\t\t},\n\t\t{\n\t\t\tip:       0xFFFFFFFF,\n\t\t\texpected: \"255.255.255.255\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tgot := intToIP4(tt.ip).String()\n\t\tassert.Equal(t, tt.expected, got, \"IP %v not converted correctly\", tt.ip)\n\t}\n}\n"
  },
  {
    "path": "idle_sweep.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport \"time\"\n\n// idleSweep controls a periodic task that looks for idle connections and clears\n// them from the peer list.\n// NOTE: This struct is not thread-safe on its own. Calls to Start() and Stop()\n// should be guarded by locking ch.mutable\ntype idleSweep struct {\n\tch                *Channel\n\tmaxIdleTime       time.Duration\n\tidleCheckInterval time.Duration\n\tstopCh            chan struct{}\n\tstarted           bool\n}\n\n// startIdleSweep starts a poller that checks for idle connections at given\n// intervals.\nfunc startIdleSweep(ch *Channel, opts *ChannelOptions) *idleSweep {\n\tis := &idleSweep{\n\t\tch:                ch,\n\t\tmaxIdleTime:       opts.MaxIdleTime,\n\t\tidleCheckInterval: opts.IdleCheckInterval,\n\t}\n\n\tis.start()\n\treturn is\n}\n\n// Start runs the goroutine responsible for checking idle connections.\nfunc (is *idleSweep) start() {\n\tif is.started || is.idleCheckInterval <= 0 {\n\t\treturn\n\t}\n\n\tis.ch.log.WithFields(\n\t\tLogField{\"idleCheckInterval\", is.idleCheckInterval},\n\t\tLogField{\"maxIdleTime\", is.maxIdleTime},\n\t).Info(\"Starting idle connections poller.\")\n\n\tis.started = true\n\tis.stopCh = make(chan struct{})\n\tgo is.pollerLoop()\n}\n\n// Stop kills the poller checking for idle connections.\nfunc (is *idleSweep) Stop() {\n\tif !is.started {\n\t\treturn\n\t}\n\n\tis.started = false\n\tis.ch.log.Info(\"Stopping idle connections poller.\")\n\tclose(is.stopCh)\n}\n\nfunc (is *idleSweep) pollerLoop() {\n\tticker := is.ch.timeTicker(is.idleCheckInterval)\n\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\tis.checkIdleConnections()\n\t\tcase <-is.stopCh:\n\t\t\tticker.Stop()\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (is *idleSweep) checkIdleConnections() {\n\tnow := is.ch.timeNow()\n\n\t// Acquire the read lock and examine which connections are idle.\n\tidleConnections := make([]*Connection, 0, 10)\n\tis.ch.mutable.RLock()\n\tfor _, conn := range is.ch.mutable.conns {\n\t\tlastActivityTime := conn.getLastActivityReadTime()\n\t\tif sendActivityTime := conn.getLastActivityWriteTime(); lastActivityTime.Before(sendActivityTime) {\n\t\t\tlastActivityTime = sendActivityTime\n\t\t}\n\n\t\tif idleTime := now.Sub(lastActivityTime); idleTime >= is.maxIdleTime {\n\t\t\tidleConnections = append(idleConnections, conn)\n\t\t}\n\t}\n\tis.ch.mutable.RUnlock()\n\n\tfor _, conn := range idleConnections {\n\t\t// It's possible that the connection is already closed when we get here.\n\t\tif !conn.IsActive() {\n\t\t\tcontinue\n\t\t}\n\n\t\t// We shouldn't get to a state where we have pending calls, but the connection\n\t\t// is idle. This either means the max-idle time is too low, or there's a stuck call.\n\t\tif conn.hasPendingCalls() {\n\t\t\tconn.log.Error(\"Skip closing idle Connection as it has pending calls.\")\n\t\t\tcontinue\n\t\t}\n\n\t\tconn.close(\n\t\t\tLogField{\"reason\", \"Idle connection closed\"},\n\t\t\tLogField{\"lastActivityTimeRead\", conn.getLastActivityReadTime()},\n\t\t\tLogField{\"lastActivityTimeWrite\", conn.getLastActivityWriteTime()},\n\t\t)\n\t}\n}\n"
  },
  {
    "path": "idle_sweep_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// peerStatusListener is a test tool used to wait for connections to drop by\n// listening to status events from a channel.\ntype peerStatusListener struct {\n\tchanges chan struct{}\n}\n\nfunc newPeerStatusListener() *peerStatusListener {\n\treturn &peerStatusListener{\n\t\tchanges: make(chan struct{}, 10),\n\t}\n}\n\nfunc (pl *peerStatusListener) onStatusChange(p *Peer) {\n\tpl.changes <- struct{}{}\n}\n\nfunc (pl *peerStatusListener) waitForZeroConnections(t testing.TB, channels ...*Channel) bool {\n\tfor {\n\t\tselect {\n\t\tcase <-pl.changes:\n\t\t\tif allConnectionsClosed(channels) {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\tcase <-time.After(testutils.Timeout(time.Second)):\n\t\t\tt.Fatalf(\"Some connections are still open: %s\", connectionStatus(channels))\n\t\t\treturn false\n\t\t}\n\t}\n}\n\nfunc (pl *peerStatusListener) waitForZeroExchanges(t testing.TB, channels ...*Channel) {\n\tvar (\n\t\tisEmpty bool\n\t\tstatus  []string\n\t)\n\tif !testutils.WaitFor(100*time.Millisecond, func() bool {\n\t\tisEmpty, status = allExchangesEmpty(channels)\n\t\treturn isEmpty\n\t}) {\n\t\tt.Fatalf(\"Some exchanges are still non-empty: %s\", strings.Join(status, \", \"))\n\t}\n}\n\nfunc allConnectionsClosed(channels []*Channel) bool {\n\tfor _, ch := range channels {\n\t\tif numConnections(ch) != 0 {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunc allExchangesEmpty(channels []*Channel) (isEmpty bool, status []string) {\n\tfor _, ch := range channels {\n\t\tn := numExchanges(ch)\n\t\tif n == 0 {\n\t\t\treturn true, nil\n\t\t}\n\t\tstatus = append(status,\n\t\t\tfmt.Sprintf(\"%s: %d open\", ch.PeerInfo().ProcessName, n))\n\t}\n\treturn false, status\n}\n\nfunc numConnections(ch *Channel) int {\n\trootPeers := ch.RootPeers().Copy()\n\tcount := 0\n\n\tfor _, peer := range rootPeers {\n\t\tin, out := peer.NumConnections()\n\t\tcount += in + out\n\t}\n\n\treturn count\n}\n\nfunc numExchanges(ch *Channel) int {\n\tvar num int\n\trootPeers := ch.RootPeers().Copy()\n\tfor _, p := range rootPeers {\n\t\tstate := p.IntrospectState(nil)\n\t\tfor _, c := range state.InboundConnections {\n\t\t\tnum += c.InboundExchange.Count + c.OutboundExchange.Count\n\t\t}\n\t\tfor _, c := range state.OutboundConnections {\n\t\t\tnum += c.InboundExchange.Count + c.OutboundExchange.Count\n\t\t}\n\t}\n\treturn num\n}\n\nfunc connectionStatus(channels []*Channel) string {\n\tstatus := make([]string, 0)\n\tfor _, ch := range channels {\n\t\tstatus = append(status,\n\t\t\tfmt.Sprintf(\"%s: %d open\", ch.PeerInfo().ProcessName, numConnections(ch)))\n\t}\n\treturn strings.Join(status, \", \")\n}\n\n// Validates that inbound idle connections are dropped.\nfunc TestServerBasedSweep(t *testing.T) {\n\tlistener := newPeerStatusListener()\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tserverTicker := testutils.NewFakeTicker()\n\tclock := testutils.NewStubClock(time.Now())\n\n\tserverOpts := testutils.NewOpts().\n\t\tSetTimeTicker(serverTicker.New).\n\t\tSetIdleCheckInterval(30 * time.Second).\n\t\tSetMaxIdleTime(3 * time.Minute).\n\t\tSetOnPeerStatusChanged(listener.onStatusChange).\n\t\tSetTimeNow(clock.Now).\n\t\tNoRelay()\n\n\tclientOpts := testutils.NewOpts().\n\t\tSetOnPeerStatusChanged(listener.onStatusChange)\n\n\ttestutils.WithTestServer(t, serverOpts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\n\t\tclient := ts.NewClient(clientOpts)\n\t\traw.Call(ctx, client, ts.HostPort(), ts.ServiceName(), \"echo\", nil, nil)\n\n\t\t// Both server and client now have an active connection. After 3 minutes they\n\t\t// should be cleared out by the idle sweep.\n\t\tfor i := 0; i < 2; i++ {\n\t\t\tclock.Elapse(1 * time.Minute)\n\t\t\tserverTicker.Tick()\n\n\t\t\tassert.Equal(t, 1, numConnections(ts.Server()))\n\t\t\tassert.Equal(t, 1, numConnections(client))\n\t\t}\n\n\t\t// Move the clock forward and trigger the idle poller.\n\t\tclock.Elapse(90 * time.Second)\n\t\tserverTicker.Tick()\n\t\tlistener.waitForZeroConnections(t, ts.Server(), client)\n\t})\n}\n\n// Validates that outbound idle connections are dropped.\nfunc TestClientBasedSweep(t *testing.T) {\n\tlistener := newPeerStatusListener()\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tclientTicker := testutils.NewFakeTicker()\n\tclock := testutils.NewStubClock(time.Now())\n\n\tserverOpts := testutils.NewOpts().\n\t\tSetOnPeerStatusChanged(listener.onStatusChange).\n\t\tNoRelay()\n\n\tclientOpts := testutils.NewOpts().\n\t\tSetTimeNow(clock.Now).\n\t\tSetTimeTicker(clientTicker.New).\n\t\tSetMaxIdleTime(3 * time.Minute).\n\t\tSetOnPeerStatusChanged(listener.onStatusChange).\n\t\tSetIdleCheckInterval(30 * time.Second)\n\n\ttestutils.WithTestServer(t, serverOpts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\n\t\tclient := ts.NewClient(clientOpts)\n\t\traw.Call(ctx, client, ts.HostPort(), ts.ServiceName(), \"echo\", nil, nil)\n\n\t\t// Both server and client now have an active connection. After 3 minutes they\n\t\t// should be cleared out by the idle sweep.\n\t\tclientTicker.Tick()\n\n\t\tassert.Equal(t, 1, numConnections(ts.Server()))\n\t\tassert.Equal(t, 1, numConnections(client))\n\n\t\t// Move the clock forward and trigger the idle poller.\n\t\tclock.Elapse(180 * time.Second)\n\t\tclientTicker.Tick()\n\t\tlistener.waitForZeroConnections(t, ts.Server(), client)\n\t})\n}\n\n// Validates that a relay also disconnects idle connections - both inbound and\n// outbound.\nfunc TestRelayBasedSweep(t *testing.T) {\n\tlistener := newPeerStatusListener()\n\trelayTicker := testutils.NewFakeTicker()\n\tclock := testutils.NewStubClock(time.Now())\n\n\topts := testutils.NewOpts().\n\t\tSetOnPeerStatusChanged(listener.onStatusChange)\n\n\trelayOpts := testutils.NewOpts().\n\t\tSetTimeNow(clock.Now).\n\t\tSetTimeTicker(relayTicker.New).\n\t\tSetMaxIdleTime(3 * time.Minute).\n\t\tSetIdleCheckInterval(30 * time.Second).\n\t\tSetOnPeerStatusChanged(listener.onStatusChange).\n\t\tSetDisableServer(). // We create our own server without the idle sweeper.\n\t\tSetRelayOnly()\n\ttestutils.WithTestServer(t, relayOpts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tserver := ts.NewServer(opts)\n\t\ttestutils.RegisterEcho(server, nil)\n\n\t\t// Make a call to the server via relay, which will establish connections:\n\t\t// Client -> Relay -> Server\n\t\tclient := ts.NewClient(opts)\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), server.ServiceName())\n\n\t\trelayTicker.Tick()\n\n\t\t// Relay has 1 inbound + 1 outbound\n\t\tassert.Equal(t, 2, numConnections(ts.Relay()))\n\t\tassert.Equal(t, 1, numConnections(server))\n\t\tassert.Equal(t, 1, numConnections(client))\n\n\t\t// The relay will drop both sides of the connection after 3 minutes of inactivity.\n\t\tclock.Elapse(180 * time.Second)\n\t\tlistener.waitForZeroExchanges(t, client)\n\n\t\trelayTicker.Tick()\n\t\tlistener.waitForZeroConnections(t, ts.Relay(), server, client)\n\t})\n}\n\n// Validates that pings do not keep the connection alive.\nfunc TestIdleSweepWithPings(t *testing.T) {\n\tlistener := newPeerStatusListener()\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tclientTicker := testutils.NewFakeTicker()\n\tclock := testutils.NewStubClock(time.Now())\n\n\tserverOpts := testutils.NewOpts().\n\t\tSetOnPeerStatusChanged(listener.onStatusChange).\n\t\tNoRelay()\n\n\tclientOpts := testutils.NewOpts().\n\t\tSetTimeNow(clock.Now).\n\t\tSetTimeTicker(clientTicker.New).\n\t\tSetMaxIdleTime(3 * time.Minute).\n\t\tSetIdleCheckInterval(30 * time.Second).\n\t\tSetOnPeerStatusChanged(listener.onStatusChange)\n\n\ttestutils.WithTestServer(t, serverOpts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\n\t\tclient := ts.NewClient(clientOpts)\n\t\traw.Call(ctx, client, ts.HostPort(), ts.ServiceName(), \"echo\", nil, nil)\n\n\t\t// Generate pings every minute.\n\t\tfor i := 0; i < 2; i++ {\n\t\t\tclock.Elapse(60 * time.Second)\n\t\t\tclient.Ping(ctx, ts.HostPort())\n\n\t\t\tclientTicker.Tick()\n\n\t\t\tassert.Equal(t, 1, numConnections(ts.Server()))\n\t\t\tassert.Equal(t, 1, numConnections(client))\n\t\t}\n\n\t\tclock.Elapse(60 * time.Second)\n\t\tclientTicker.Tick()\n\n\t\t// Connections should still drop, regardless of the ping.\n\t\tlistener.waitForZeroConnections(t, ts.Server(), client)\n\t})\n}\n\n// Validates that when MaxIdleTime isn't set, NewChannel returns an error.\nfunc TestIdleSweepMisconfiguration(t *testing.T) {\n\tch, err := NewChannel(\"svc\", &ChannelOptions{\n\t\tIdleCheckInterval: time.Duration(30 * time.Second),\n\t})\n\n\tassert.Nil(t, ch, \"NewChannel should not return a channel\")\n\tassert.Error(t, err, \"NewChannel should fail\")\n}\n\nfunc TestIdleSweepIgnoresConnectionsWithCalls(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tclientTicker := testutils.NewFakeTicker()\n\tclock := testutils.NewStubClock(time.Now())\n\n\tlistener := newPeerStatusListener()\n\t// TODO: Log filtering doesn't require the message to be seen.\n\tserverOpts := testutils.NewOpts().\n\t\tAddLogFilter(\"Skip closing idle Connection as it has pending calls.\", 2).\n\t\tSetOnPeerStatusChanged(listener.onStatusChange).\n\t\tSetTimeNow(clock.Now).\n\t\tSetTimeTicker(clientTicker.New).\n\t\tSetRelayMaxTimeout(time.Hour).\n\t\tSetMaxIdleTime(3 * time.Minute).\n\t\tSetIdleCheckInterval(30 * time.Second)\n\n\ttestutils.WithTestServer(t, serverOpts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tvar (\n\t\t\tgotCall = make(chan struct{})\n\t\t\tblock   = make(chan struct{})\n\t\t)\n\t\ttestutils.RegisterEcho(ts.Server(), func() {\n\t\t\tclose(gotCall)\n\t\t\t<-block\n\t\t})\n\n\t\tclientOpts := testutils.NewOpts().SetOnPeerStatusChanged(listener.onStatusChange)\n\n\t\t// Client 1 will just ping, so we create a connection that should be closed.\n\t\tc1 := ts.NewClient(clientOpts)\n\t\trequire.True(t, testutils.WaitFor(10*time.Second, func() bool {\n\t\t\treturn c1.Ping(ctx, ts.HostPort()) == nil\n\t\t}), \"Ping failed\")\n\n\t\t// Client 2 will make a call that will be blocked. Wait for the call to be received.\n\t\tc2CallComplete := make(chan struct{})\n\t\tc2 := ts.NewClient(clientOpts)\n\t\tgo func() {\n\t\t\ttestutils.AssertEcho(t, c2, ts.HostPort(), ts.ServiceName())\n\t\t\tclose(c2CallComplete)\n\t\t}()\n\t\t<-gotCall\n\n\t\t// If we are in no-relay mode, we expect 2 connections to the server (from each client).\n\t\t// If we are in relay mode, the relay will have the 2 connections from clients + 1 connection to the server.\n\t\tcheck := struct {\n\t\t\tch            *Channel\n\t\t\tpreCloseConns int\n\t\t\ttick          func()\n\t\t}{\n\t\t\tch:            ts.Server(),\n\t\t\tpreCloseConns: 2,\n\t\t\ttick: func() {\n\t\t\t\tclock.Elapse(5 * time.Minute)\n\t\t\t\tclientTicker.Tick()\n\t\t\t},\n\t\t}\n\n\t\tif ts.HasRelay() {\n\t\t\trelay := ts.Relay()\n\t\t\tcheck.ch = relay\n\t\t\tcheck.preCloseConns++\n\t\t\toldTick := check.tick\n\t\t\tcheck.tick = func() {\n\t\t\t\toldTick()\n\n\t\t\t\t// The same ticker is being used by the server and the relay\n\t\t\t\t// so we need to tick it twice.\n\t\t\t\tclientTicker.Tick()\n\t\t\t}\n\t\t}\n\n\t\tassert.Equal(t, check.preCloseConns, check.ch.IntrospectNumConnections(), \"Expect connection to client 1 and client 2\")\n\n\t\t// Let the idle checker close client 1's connection.\n\t\tlistener.waitForZeroExchanges(t, c1)\n\n\t\tcheck.tick()\n\t\tlistener.waitForZeroConnections(t, c1)\n\n\t\t// Make sure we have only a connection for client 2, which is active.\n\t\tassert.Equal(t, check.preCloseConns-1, check.ch.IntrospectNumConnections(), \"Expect connection only to client 2\")\n\t\tstate := check.ch.IntrospectState(nil)\n\t\trequire.Empty(t, state.InactiveConnections, \"Ensure all connections are active\")\n\n\t\t// Unblock the call.\n\t\tclose(block)\n\t\t<-c2CallComplete\n\n\t\t// Since the idle sweep loop and message exchange run concurrently, there is\n\t\t// a race between the idle sweep and exchange shutdown. To mitigate this,\n\t\t// wait for the exchanges to shut down before triggering the idle sweep.\n\t\tlistener.waitForZeroExchanges(t, ts.Server(), c2)\n\n\t\tcheck.tick()\n\t\tlistener.waitForZeroConnections(t, ts.Server(), c2)\n\t})\n}\n"
  },
  {
    "path": "inbound.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/ext\"\n\t\"golang.org/x/net/context\"\n)\n\nconst (\n\tsystemErrorType = \"system\"\n\tappErrorType    = \"application\"\n)\n\nvar errInboundRequestAlreadyActive = errors.New(\"inbound request is already active; possible duplicate client id\")\n\n// handleCallReq handles an incoming call request, registering a message\n// exchange to receive further fragments for that call, and dispatching it in\n// another goroutine\nfunc (c *Connection) handleCallReq(frame *Frame) bool {\n\tnow := c.timeNow()\n\tswitch state := c.readState(); state {\n\tcase connectionActive:\n\t\tbreak\n\tcase connectionStartClose, connectionInboundClosed, connectionClosed:\n\t\tc.SendSystemError(frame.Header.ID, callReqSpan(frame), ErrChannelClosed)\n\t\treturn true\n\tdefault:\n\t\tpanic(fmt.Errorf(\"unknown connection state for call req: %v\", state))\n\t}\n\n\tcallReq := new(callReq)\n\tcallReq.id = frame.Header.ID\n\tinitialFragment, err := parseInboundFragment(c.opts.FramePool, frame, callReq)\n\tif err != nil {\n\t\t// TODO(mmihic): Probably want to treat this as a protocol error\n\t\tc.log.WithFields(\n\t\t\tLogField{\"header\", frame.Header},\n\t\t\tErrField(err),\n\t\t).Error(\"Couldn't decode initial fragment.\")\n\t\treturn true\n\t}\n\n\tcall := new(InboundCall)\n\tcall.conn = c\n\tctx, cancel := newIncomingContext(c.baseContext, call, callReq.TimeToLive)\n\n\tmex, err := c.inbound.newExchange(ctx, cancel, c.opts.FramePool, callReq.messageType(), frame.Header.ID, mexChannelBufferSize)\n\tif err != nil {\n\t\tif err == errDuplicateMex {\n\t\t\terr = errInboundRequestAlreadyActive\n\t\t}\n\t\tc.log.WithFields(LogField{\"header\", frame.Header}).Error(\"Couldn't register exchange.\")\n\t\tc.protocolError(frame.Header.ID, errInboundRequestAlreadyActive)\n\t\treturn true\n\t}\n\n\t// Close may have been called between the time we checked the state and us creating the exchange.\n\tif c.readState() != connectionActive {\n\t\tmex.shutdown()\n\t\treturn true\n\t}\n\n\tresponse := new(InboundCallResponse)\n\tresponse.call = call\n\tresponse.calledAt = now\n\tresponse.timeNow = c.timeNow\n\tresponse.span = c.extractInboundSpan(callReq)\n\tif response.span != nil {\n\t\tmex.ctx = opentracing.ContextWithSpan(mex.ctx, response.span)\n\t}\n\tresponse.mex = mex\n\tresponse.conn = c\n\tresponse.cancel = cancel\n\tresponse.log = c.log.WithFields(LogField{\"In-Response\", callReq.ID()})\n\tresponse.contents = newFragmentingWriter(response.log, response, initialFragment.checksumType.New())\n\tresponse.headers = transportHeaders{}\n\tresponse.messageForFragment = func(initial bool) message {\n\t\tif initial {\n\t\t\tcallRes := new(callRes)\n\t\t\tcallRes.Headers = response.headers\n\t\t\tcallRes.ResponseCode = responseOK\n\t\t\tif response.applicationError {\n\t\t\t\tcallRes.ResponseCode = responseApplicationError\n\t\t\t}\n\t\t\treturn callRes\n\t\t}\n\n\t\treturn new(callResContinue)\n\t}\n\n\tcall.mex = mex\n\tcall.initialFragment = initialFragment\n\tcall.serviceName = string(callReq.Service)\n\tcall.headers = callReq.Headers\n\tcall.response = response\n\tcall.log = c.log.WithFields(LogField{\"In-Call\", callReq.ID()})\n\tcall.messageForFragment = func(initial bool) message { return new(callReqContinue) }\n\tcall.contents = newFragmentingReader(call.log, call)\n\tcall.statsReporter = c.statsReporter\n\tcall.createStatsTags(c.commonStatsTags)\n\n\tresponse.statsReporter = c.statsReporter\n\tresponse.commonStatsTags = call.commonStatsTags\n\n\tsetResponseHeaders(call.headers, response.headers)\n\tgo c.dispatchInbound(c.connID, callReq.ID(), call, frame)\n\treturn false\n}\n\n// handleCallReqContinue handles the continuation of a call request, forwarding\n// it to the request channel for that request, where it can be pulled during\n// defragmentation\nfunc (c *Connection) handleCallReqContinue(frame *Frame) bool {\n\tif err := c.inbound.forwardPeerFrame(frame); err != nil {\n\t\t// If forward fails, it's due to a timeout. We can free this frame.\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (c *Connection) handleCancel(frame *Frame) bool {\n\tc.statsReporter.IncCounter(\"inbound.cancels.requested\", c.commonStatsTags, 1)\n\n\tif !c.opts.PropagateCancel {\n\t\tif c.log.Enabled(LogLevelDebug) {\n\t\t\tc.log.Debugf(\"Ignoring cancel for %v\", frame.Header.ID)\n\t\t}\n\t\treturn true\n\t}\n\n\tc.statsReporter.IncCounter(\"inbound.cancels.honored\", c.commonStatsTags, 1)\n\n\tc.inbound.handleCancel(frame)\n\n\t// Free the frame, as it's consumed immediately.\n\treturn true\n}\n\n// createStatsTags creates the common stats tags, if they are not already created.\nfunc (call *InboundCall) createStatsTags(connectionTags map[string]string) {\n\tcall.commonStatsTags = map[string]string{\n\t\t\"calling-service\": call.CallerName(),\n\t}\n\tfor k, v := range connectionTags {\n\t\tcall.commonStatsTags[k] = v\n\t}\n}\n\n// dispatchInbound ispatches an inbound call to the appropriate handler\nfunc (c *Connection) dispatchInbound(_ uint32, _ uint32, call *InboundCall, frame *Frame) {\n\tif call.log.Enabled(LogLevelDebug) {\n\t\tcall.log.Debugf(\"Received incoming call for %s from %s\", call.ServiceName(), c.remotePeerInfo)\n\t}\n\n\tif err := call.readMethod(); err != nil {\n\t\tcall.log.WithFields(\n\t\t\tLogField{\"remotePeer\", c.remotePeerInfo},\n\t\t\tErrField(err),\n\t\t).Error(\"Couldn't read method.\")\n\t\tc.opts.FramePool.Release(frame)\n\t\treturn\n\t}\n\n\tcall.commonStatsTags[\"endpoint\"] = call.methodString\n\tcall.statsReporter.IncCounter(\"inbound.calls.recvd\", call.commonStatsTags, 1)\n\tif span := call.response.span; span != nil {\n\t\tspan.SetOperationName(call.methodString)\n\t}\n\n\t// TODO(prashant): This is an expensive way to check for cancellation. Use a heap for timeouts.\n\tgo func() {\n\t\tselect {\n\t\tcase <-call.mex.ctx.Done():\n\t\t\t// checking if message exchange timedout or was cancelled\n\t\t\t// only two possible errors at this step:\n\t\t\t// context.DeadlineExceeded\n\t\t\t// context.Canceled\n\t\t\tif call.mex.ctx.Err() != nil {\n\t\t\t\tcall.mex.inboundExpired()\n\t\t\t}\n\t\tcase <-call.mex.errCh.c:\n\t\t\tif c.log.Enabled(LogLevelDebug) {\n\t\t\t\tcall.log.Debugf(\"Wait for timeout/cancellation interrupted by error: %v\", call.mex.errCh.err)\n\t\t\t}\n\t\t\t// when an exchange errors out, mark the exchange as expired\n\t\t\t// and call cancel so the server handler's context is canceled\n\t\t\t// TODO: move the cancel to the parent context at connnection level\n\t\t\tcall.response.cancel()\n\t\t\tcall.mex.inboundExpired()\n\t\t}\n\t}()\n\n\t// Internal handlers (e.g., introspection) trump all other user-registered handlers on\n\t// the \"tchannel\" name.\n\tif call.ServiceName() == \"tchannel\" {\n\t\tif h := c.internalHandlers.find(call.Method()); h != nil {\n\t\t\th.Handle(call.mex.ctx, call)\n\t\t\treturn\n\t\t}\n\t}\n\n\tc.handler.Handle(call.mex.ctx, call)\n}\n\n// An InboundCall is an incoming call from a peer\ntype InboundCall struct {\n\treqResReader\n\n\tconn            *Connection\n\tresponse        *InboundCallResponse\n\tserviceName     string\n\tmethod          []byte\n\tmethodString    string\n\theaders         transportHeaders\n\tstatsReporter   StatsReporter\n\tcommonStatsTags map[string]string\n}\n\n// ServiceName returns the name of the service being called\nfunc (call *InboundCall) ServiceName() string {\n\treturn call.serviceName\n}\n\n// Method returns the method being called\nfunc (call *InboundCall) Method() []byte {\n\treturn call.method\n}\n\n// MethodString returns the method being called as a string.\nfunc (call *InboundCall) MethodString() string {\n\treturn call.methodString\n}\n\n// Format the format of the request from the ArgScheme transport header.\nfunc (call *InboundCall) Format() Format {\n\treturn Format(call.headers[ArgScheme])\n}\n\n// CallerName returns the caller name from the CallerName transport header.\nfunc (call *InboundCall) CallerName() string {\n\treturn call.headers[CallerName]\n}\n\n// ShardKey returns the shard key from the ShardKey transport header.\nfunc (call *InboundCall) ShardKey() string {\n\treturn call.headers[ShardKey]\n}\n\n// RoutingKey returns the routing key from the RoutingKey transport header.\nfunc (call *InboundCall) RoutingKey() string {\n\treturn call.headers[RoutingKey]\n}\n\n// RoutingDelegate returns the routing delegate from the RoutingDelegate transport header.\nfunc (call *InboundCall) RoutingDelegate() string {\n\treturn call.headers[RoutingDelegate]\n}\n\n// LocalPeer returns the local peer information for this call.\nfunc (call *InboundCall) LocalPeer() LocalPeerInfo {\n\treturn call.conn.localPeerInfo\n}\n\n// RemotePeer returns the remote peer information for this call.\nfunc (call *InboundCall) RemotePeer() PeerInfo {\n\treturn call.conn.RemotePeerInfo()\n}\n\n// Connection returns the underlying raw net connection.\nfunc (call *InboundCall) Connection() net.Conn {\n\treturn call.conn.conn\n}\n\n// CallOptions returns a CallOptions struct suitable for forwarding a request.\nfunc (call *InboundCall) CallOptions() *CallOptions {\n\treturn &CallOptions{\n\t\tCallerName:      call.CallerName(),\n\t\tFormat:          call.Format(),\n\t\tShardKey:        call.ShardKey(),\n\t\tRoutingDelegate: call.RoutingDelegate(),\n\t\tRoutingKey:      call.RoutingKey(),\n\t}\n}\n\n// Reads the entire method name (arg1) from the request stream.\nfunc (call *InboundCall) readMethod() error {\n\tvar arg1 []byte\n\tif err := NewArgReader(call.arg1Reader()).Read(&arg1); err != nil {\n\t\treturn call.failed(err)\n\t}\n\n\tcall.method = arg1\n\tcall.methodString = string(arg1)\n\treturn nil\n}\n\n// Arg2Reader returns an ArgReader to read the second argument.\n// The ReadCloser must be closed once the argument has been read.\nfunc (call *InboundCall) Arg2Reader() (ArgReader, error) {\n\treturn call.arg2Reader()\n}\n\n// Arg3Reader returns an ArgReader to read the last argument.\n// The ReadCloser must be closed once the argument has been read.\nfunc (call *InboundCall) Arg3Reader() (ArgReader, error) {\n\treturn call.arg3Reader()\n}\n\n// Response provides access to the InboundCallResponse object which can be used\n// to write back to the calling peer\nfunc (call *InboundCall) Response() *InboundCallResponse {\n\tif call.err != nil {\n\t\t// While reading Thrift, we cannot distinguish between malformed Thrift and other errors,\n\t\t// and so we may try to respond with a bad request. We should ensure that the response\n\t\t// is marked as failed if the request has failed so that we don't try to shutdown the exchange\n\t\t// a second time.\n\t\tcall.response.err = call.err\n\t}\n\treturn call.response\n}\n\nfunc (call *InboundCall) doneReading(unexpected error) {}\n\n// An InboundCallResponse is used to send the response back to the calling peer\ntype InboundCallResponse struct {\n\treqResWriter\n\n\tcall   *InboundCall\n\tcancel context.CancelFunc\n\t// calledAt is the time the inbound call was routed to the application.\n\tcalledAt         time.Time\n\ttimeNow          func() time.Time\n\tapplicationError bool\n\tsystemError      bool\n\theaders          transportHeaders\n\tspan             opentracing.Span\n\tstatsReporter    StatsReporter\n\tcommonStatsTags  map[string]string\n}\n\n// SendSystemError returns a system error response to the peer.  The call is considered\n// complete after this method is called, and no further data can be written.\nfunc (response *InboundCallResponse) SendSystemError(err error) error {\n\tif response.err != nil {\n\t\treturn response.err\n\t}\n\t// Fail all future attempts to read fragments\n\tresponse.state = reqResWriterComplete\n\tresponse.systemError = true\n\tresponse.setSpanErrorDetails(err)\n\tresponse.doneSending()\n\tresponse.call.releasePreviousFragment()\n\n\tspan := CurrentSpan(response.mex.ctx)\n\n\treturn response.conn.SendSystemError(response.mex.msgID, *span, err)\n}\n\n// SetApplicationError marks the response as being an application error.  This method can\n// only be called before any arguments have been sent to the calling peer.\nfunc (response *InboundCallResponse) SetApplicationError() error {\n\tif response.state > reqResWriterPreArg2 {\n\t\treturn response.failed(errReqResWriterStateMismatch{\n\t\t\tstate:         response.state,\n\t\t\texpectedState: reqResWriterPreArg2,\n\t\t})\n\t}\n\tresponse.applicationError = true\n\tresponse.setSpanErrorDetails(nil)\n\treturn nil\n}\n\n// Blackhole indicates no response will be sent, and cleans up any resources\n// associated with this request. This allows for services to trigger a timeout in\n// clients without holding on to any goroutines on the server.\nfunc (response *InboundCallResponse) Blackhole() {\n\tresponse.cancel()\n}\n\n// Arg2Writer returns a WriteCloser that can be used to write the second argument.\n// The returned writer must be closed once the write is complete.\nfunc (response *InboundCallResponse) Arg2Writer() (ArgWriter, error) {\n\tif err := NewArgWriter(response.arg1Writer()).Write(nil); err != nil {\n\t\treturn nil, err\n\t}\n\treturn response.arg2Writer()\n}\n\n// Arg3Writer returns a WriteCloser that can be used to write the last argument.\n// The returned writer must be closed once the write is complete.\nfunc (response *InboundCallResponse) Arg3Writer() (ArgWriter, error) {\n\treturn response.arg3Writer()\n}\n\n// setSpanErrorDetails sets the span tags for the error type.\nfunc (response *InboundCallResponse) setSpanErrorDetails(err error) {\n\tif span := response.span; span != nil {\n\t\tif response.applicationError || response.systemError {\n\t\t\terrorType := appErrorType\n\t\t\tif response.systemError {\n\t\t\t\terrorType = systemErrorType\n\t\t\t\t// if the error is a system error, set the error code as a span tag\n\t\t\t\tspan.SetTag(\"rpc.tchannel.system_error_code\", GetSystemErrorCode(err).MetricsKey())\n\t\t\t}\n\t\t\tspan.SetTag(\"rpc.tchannel.error_type\", errorType)\n\t\t}\n\t}\n}\n\n// doneSending shuts down the message exchange for this call.\n// For incoming calls, the last message is sending the call response.\nfunc (response *InboundCallResponse) doneSending() {\n\t// TODO(prashant): Move this to when the message is actually being sent.\n\tnow := response.timeNow()\n\n\tif span := response.span; span != nil {\n\t\tif response.applicationError || response.systemError {\n\t\t\text.Error.Set(span, true)\n\t\t}\n\t\tspan.FinishWithOptions(opentracing.FinishOptions{FinishTime: now})\n\t}\n\n\tlatency := now.Sub(response.calledAt)\n\tresponse.statsReporter.RecordTimer(\"inbound.calls.latency\", response.commonStatsTags, latency)\n\n\tif response.systemError {\n\t\t// TODO(prashant): Report the error code type as per metrics doc and enable.\n\t\t// response.statsReporter.IncCounter(\"inbound.calls.system-errors\", response.commonStatsTags, 1)\n\t} else if response.applicationError {\n\t\tresponse.statsReporter.IncCounter(\"inbound.calls.app-errors\", response.commonStatsTags, 1)\n\t} else {\n\t\tresponse.statsReporter.IncCounter(\"inbound.calls.success\", response.commonStatsTags, 1)\n\t}\n\n\t// Cancel the context since the response is complete.\n\tresponse.cancel()\n\n\t// The message exchange is still open if there are no errors, call shutdown.\n\tif response.err == nil {\n\t\tresponse.mex.shutdown()\n\t}\n}\n"
  },
  {
    "path": "inbound_internal_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/opentracing/opentracing-go/mocktracer\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype statsReporter struct{}\n\nfunc (w *statsReporter) IncCounter(name string, tags map[string]string, value int64) {\n}\n\nfunc (w *statsReporter) UpdateGauge(name string, tags map[string]string, value int64) {\n}\n\nfunc (w *statsReporter) RecordTimer(name string, tags map[string]string, d time.Duration) {\n}\n\ntype testCase struct {\n\tname                   string\n\tinjectedError          error\n\tsystemError            bool\n\tapplicationError       bool\n\texpectedSpanError      bool\n\texpectedSpanErrorType  string\n\texpectedSystemErrorKey string\n}\n\nfunc TestTracingSpanError(t *testing.T) {\n\tvar (\n\t\tsystemError      = NewSystemError(ErrCodeBusy, \"foo\")\n\t\tapplicationError = fmt.Errorf(\"application\")\n\t)\n\n\ttestCases := []testCase{\n\t\t{\n\t\t\tname:                   \"ApplicationError\",\n\t\t\tinjectedError:          applicationError,\n\t\t\tsystemError:            false,\n\t\t\tapplicationError:       true,\n\t\t\texpectedSpanError:      true,\n\t\t\texpectedSpanErrorType:  \"application\",\n\t\t\texpectedSystemErrorKey: \"\",\n\t\t},\n\t\t{\n\t\t\tname:                   \"SystemError\",\n\t\t\tinjectedError:          systemError,\n\t\t\tsystemError:            true,\n\t\t\tapplicationError:       false,\n\t\t\texpectedSpanError:      true,\n\t\t\texpectedSpanErrorType:  \"system\",\n\t\t\texpectedSystemErrorKey: GetSystemErrorCode(systemError).MetricsKey(),\n\t\t},\n\t}\n\n\tfor _, tt := range testCases {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\n\t\t\tvar (\n\t\t\t\tparsedSpan *mocktracer.MockSpan\n\n\t\t\t\ttracer   = mocktracer.New()\n\t\t\t\tcallResp = &InboundCallResponse{\n\t\t\t\t\tspan:          tracer.StartSpan(\"test\"),\n\t\t\t\t\tstatsReporter: &statsReporter{},\n\t\t\t\t\treqResWriter: reqResWriter{\n\t\t\t\t\t\terr: tt.injectedError,\n\t\t\t\t\t},\n\t\t\t\t\tapplicationError: tt.applicationError,\n\t\t\t\t\tsystemError:      tt.systemError,\n\t\t\t\t\ttimeNow:          time.Now,\n\t\t\t\t\tcancel:           func() {},\n\t\t\t\t}\n\t\t\t)\n\n\t\t\tcallResp.setSpanErrorDetails(tt.injectedError)\n\t\t\tcallResp.doneSending()\n\n\t\t\tparsedSpan = callResp.span.(*mocktracer.MockSpan)\n\n\t\t\tassert.Equal(t, tt.expectedSpanError, parsedSpan.Tag(\"error\").(bool))\n\t\t\tif tt.expectedSystemErrorKey == \"\" {\n\t\t\t\tassert.Nil(t, parsedSpan.Tag(\"rpc.tchannel.system_error_code\"))\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, tt.expectedSystemErrorKey, parsedSpan.Tag(\"rpc.tchannel.system_error_code\").(string))\n\t\t\t}\n\t\t\tassert.Equal(t, tt.expectedSpanErrorType, parsedSpan.Tag(\"rpc.tchannel.error_type\").(string))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "inbound_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/jaeger-client-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\nfunc TestSpanReportingForErrors(t *testing.T) {\n\tinjectedSystemError := ErrTimeout\n\ttests := []struct {\n\t\tname           string\n\t\tmethod         string\n\t\tsystemErr      bool\n\t\tapplicationErr bool\n\t}{\n\t\t{\n\t\t\tname:           \"System Error\",\n\t\t\tmethod:         \"system-error\",\n\t\t\tsystemErr:      true,\n\t\t\tapplicationErr: false,\n\t\t},\n\t\t{\n\t\t\tname:           \"Application Error\",\n\t\t\tmethod:         \"app-error\",\n\t\t\tsystemErr:      false,\n\t\t\tapplicationErr: true,\n\t\t},\n\t\t{\n\t\t\tname:           \"No Error\",\n\t\t\tmethod:         \"no-error\",\n\t\t\tsystemErr:      false,\n\t\t\tapplicationErr: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t// We use a jaeger tracer here and not Mocktracer: because jaeger supports\n\t\t\t// zipkin format which is essential for inbound span extraction\n\t\t\tjaegerReporter := jaeger.NewInMemoryReporter()\n\t\t\tjaegerTracer, jaegerCloser := jaeger.NewTracer(testutils.DefaultServerName,\n\t\t\t\tjaeger.NewConstSampler(true),\n\t\t\t\tjaegerReporter)\n\t\t\tdefer jaegerCloser.Close()\n\n\t\t\topts := &testutils.ChannelOpts{\n\t\t\t\tChannelOptions: ChannelOptions{Tracer: jaegerTracer},\n\t\t\t}\n\n\t\t\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\t\t// Register handler that returns app error\n\t\t\t\tts.RegisterFunc(\"app-error\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\t\t\treturn &raw.Res{\n\t\t\t\t\t\tIsErr: true,\n\t\t\t\t\t}, nil\n\t\t\t\t})\n\t\t\t\t// Register handler that returns system error\n\t\t\t\tts.RegisterFunc(\"system-error\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\t\t\treturn &raw.Res{\n\t\t\t\t\t\tSystemErr: injectedSystemError,\n\t\t\t\t\t}, nil\n\t\t\t\t})\n\t\t\t\t// Register handler that returns no error\n\t\t\t\tts.RegisterFunc(\"no-error\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\t\t\treturn &raw.Res{}, nil\n\t\t\t\t})\n\n\t\t\t\tctx, cancel := NewContext(20 * time.Second)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tclientCh := ts.NewClient(opts)\n\t\t\t\tdefer clientCh.Close()\n\n\t\t\t\t// Make a new call, which should fail\n\t\t\t\t_, _, resp, err := raw.Call(ctx, clientCh, ts.HostPort(), ts.ServiceName(), tt.method, []byte(\"Arg2\"), []byte(\"Arg3\"))\n\n\t\t\t\tif tt.systemErr {\n\t\t\t\t\t// Providing 'got: %q' is necessary since SystemErrCode is a type alias of byte; testify's\n\t\t\t\t\t// failed test ouput would otherwise print out hex codes.\n\t\t\t\t\tassert.Equal(t, injectedSystemError, err, \"expected cancelled error code, got: %q\", err)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Nil(t, err, \"expected no system error code\")\n\t\t\t\t}\n\n\t\t\t\tif tt.applicationErr {\n\t\t\t\t\tassert.True(t, resp.ApplicationError(), \"Call(%v) check application error\")\n\t\t\t\t} else if !tt.systemErr {\n\t\t\t\t\tassert.False(t, resp.ApplicationError(), \"Call(%v) check application error\")\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// We should have 4 spans, 2 for client and 2 for server\n\t\t\tassert.Equal(t, len(jaegerReporter.GetSpans()), 4)\n\t\t\tfor _, span := range jaegerReporter.GetSpans() {\n\t\t\t\tif span.(*jaeger.Span).Tags()[\"span.kind\"] == \"server\" {\n\t\t\t\t\tassert.Equal(t, span.(*jaeger.Span).Tags()[\"error\"], true)\n\t\t\t\t\tif tt.applicationErr {\n\t\t\t\t\t\tassert.Equal(t, span.(*jaeger.Span).Tags()[\"rpc.tchannel.error_type\"], \"application\")\n\t\t\t\t\t\tassert.Nil(t, span.(*jaeger.Span).Tags()[\"rpc.tchannel.system_error_code\"])\n\t\t\t\t\t} else if tt.systemErr {\n\t\t\t\t\t\tassert.Equal(t, span.(*jaeger.Span).Tags()[\"rpc.tchannel.error_type\"], \"system\")\n\t\t\t\t\t\tassert.Equal(t, span.(*jaeger.Span).Tags()[\"rpc.tchannel.system_error_code\"], GetSystemErrorCode(injectedSystemError).MetricsKey())\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Nil(t, span.(*jaeger.Span).Tags()[\"rpc.tchannel.error_type\"])\n\t\t\t\t\t\tassert.Nil(t, span.(*jaeger.Span).Tags()[\"rpc.tchannel.system_error_code\"])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tjaegerReporter.Reset()\n\t\t})\n\t}\n}\n\nfunc TestActiveCallReq(t *testing.T) {\n\tt.Skip(\"Test skipped due to unreliable way to test for protocol errors\")\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\t// Note: This test cannot use log verification as the duplicate ID causes a log.\n\t// It does not use a verified server, as it leaks a message exchange due to the\n\t// modification of IDs in the relay.\n\topts := testutils.NewOpts().DisableLogVerification()\n\ttestutils.WithServer(t, opts, func(ch *Channel, hostPort string) {\n\t\tgotCall := make(chan struct{})\n\t\tunblock := make(chan struct{})\n\n\t\ttestutils.RegisterFunc(ch, \"blocked\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\tgotCall <- struct{}{}\n\t\t\t<-unblock\n\t\t\treturn &raw.Res{}, nil\n\t\t})\n\n\t\trelayFunc := func(outgoing bool, frame *Frame) *Frame {\n\t\t\tif outgoing && frame.Header.ID == 3 {\n\t\t\t\tframe.Header.ID = 2\n\t\t\t}\n\t\t\treturn frame\n\t\t}\n\n\t\trelayHostPort, closeRelay := testutils.FrameRelay(t, hostPort, relayFunc)\n\t\tdefer closeRelay()\n\n\t\tfirstComplete := make(chan struct{})\n\t\tgo func() {\n\t\t\t// This call will block until we close unblock.\n\t\t\traw.Call(ctx, ch, relayHostPort, ch.PeerInfo().ServiceName, \"blocked\", nil, nil)\n\t\t\tclose(firstComplete)\n\t\t}()\n\n\t\t// Wait for the first call to be received by the server\n\t\t<-gotCall\n\n\t\t// Make a new call, which should fail\n\t\t_, _, _, err := raw.Call(ctx, ch, relayHostPort, ch.PeerInfo().ServiceName, \"blocked\", nil, nil)\n\t\tassert.Error(t, err, \"Expect error\")\n\t\tassert.True(t, strings.Contains(err.Error(), \"already active\"),\n\t\t\t\"expected already active error, got %v\", err)\n\n\t\tclose(unblock)\n\t\t<-firstComplete\n\t})\n}\n\nfunc TestInboundConnection(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\t// Disable relay since relays hide host:port on outbound calls.\n\topts := testutils.NewOpts().NoRelay()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ts2 := ts.NewServer(nil)\n\n\t\tts.RegisterFunc(\"test\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\tc, rawConn := InboundConnection(CurrentCall(ctx))\n\t\t\tassert.Equal(t, s2.PeerInfo().HostPort, c.RemotePeerInfo().HostPort, \"Unexpected host port\")\n\t\t\tassert.NotNil(t, rawConn, \"unexpected connection\")\n\t\t\treturn &raw.Res{}, nil\n\t\t})\n\n\t\t_, _, _, err := raw.Call(ctx, s2, ts.HostPort(), ts.ServiceName(), \"test\", nil, nil)\n\t\trequire.NoError(t, err, \"Call failed\")\n\t})\n}\n\nfunc TestInboundConnection_CallOptions(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, server *testutils.TestServer) {\n\t\tserver.RegisterFunc(\"test\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\tassert.Equal(t, \"client\", CurrentCall(ctx).CallerName(), \"Expected caller name to be passed through\")\n\t\t\treturn &raw.Res{}, nil\n\t\t})\n\n\t\tbackendName := server.ServiceName()\n\n\t\tproxyCh := server.NewServer(&testutils.ChannelOpts{ServiceName: \"proxy\"})\n\t\tdefer proxyCh.Close()\n\n\t\tsubCh := proxyCh.GetSubChannel(backendName)\n\t\tsubCh.SetHandler(HandlerFunc(func(ctx context.Context, inbound *InboundCall) {\n\t\t\toutbound, err := proxyCh.BeginCall(ctx, server.HostPort(), backendName, inbound.MethodString(), inbound.CallOptions())\n\t\t\trequire.NoError(t, err, \"Create outbound call failed\")\n\t\t\targ2, arg3, _, err := raw.WriteArgs(outbound, []byte(\"hello\"), []byte(\"world\"))\n\t\t\trequire.NoError(t, err, \"Write outbound call failed\")\n\t\t\trequire.NoError(t, raw.WriteResponse(inbound.Response(), &raw.Res{\n\t\t\t\tArg2: arg2,\n\t\t\t\tArg3: arg3,\n\t\t\t}), \"Write response failed\")\n\t\t}))\n\n\t\tclientCh := server.NewClient(&testutils.ChannelOpts{\n\t\t\tServiceName: \"client\",\n\t\t})\n\t\tdefer clientCh.Close()\n\n\t\t_, _, _, err := raw.Call(ctx, clientCh, proxyCh.PeerInfo().HostPort, backendName, \"test\", nil, nil)\n\t\trequire.NoError(t, err, \"Call through proxy failed\")\n\t})\n}\n\nfunc TestCallOptionsPropogated(t *testing.T) {\n\tconst handler = \"handler\"\n\n\tgiveCallOpts := CallOptions{\n\t\tFormat:          JSON,\n\t\tCallerName:      \"test-caller-name\",\n\t\tShardKey:        \"test-shard-key\",\n\t\tRoutingKey:      \"test-routing-key\",\n\t\tRoutingDelegate: \"test-routing-delegate\",\n\t}\n\n\tvar gotCallOpts *CallOptions\n\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(HandlerFunc(func(ctx context.Context, inbound *InboundCall) {\n\t\t\tgotCallOpts = inbound.CallOptions()\n\n\t\t\terr := raw.WriteResponse(inbound.Response(), &raw.Res{})\n\t\t\tassert.NoError(t, err, \"write response failed\")\n\t\t}), handler)\n\n\t\tctx, cancel := NewContext(testutils.Timeout(time.Second))\n\t\tdefer cancel()\n\n\t\tcall, err := ts.Server().BeginCall(ctx, ts.HostPort(), ts.ServiceName(), handler, &giveCallOpts)\n\t\trequire.NoError(t, err, \"could not call test server\")\n\n\t\t_, _, _, err = raw.WriteArgs(call, nil, nil)\n\t\trequire.NoError(t, err, \"could not write args\")\n\n\t\tassert.Equal(t, &giveCallOpts, gotCallOpts)\n\t})\n}\n\nfunc TestBlackhole(t *testing.T) {\n\tctx, cancel := NewContext(testutils.Timeout(time.Hour))\n\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, server *testutils.TestServer) {\n\t\tserviceName := server.ServiceName()\n\t\thandlerName := \"test-handler\"\n\n\t\tserver.Register(HandlerFunc(func(ctx context.Context, inbound *InboundCall) {\n\t\t\t// cancel client context in handler so the client can return after being blackholed\n\t\t\tdefer cancel()\n\n\t\t\tc, _ := InboundConnection(inbound)\n\t\t\trequire.NotNil(t, c)\n\n\t\t\tstate := c.IntrospectState(&IntrospectionOptions{})\n\t\t\trequire.Equal(t, 1, state.InboundExchange.Count, \"expected exactly one inbound exchange\")\n\n\t\t\t// blackhole request\n\t\t\tinbound.Response().Blackhole()\n\n\t\t\t// give time for exchange to cleanup\n\t\t\trequire.True(t, testutils.WaitFor(10*time.Millisecond, func() bool {\n\t\t\t\tstate = c.IntrospectState(&IntrospectionOptions{})\n\t\t\t\treturn state.InboundExchange.Count == 0\n\t\t\t}),\n\t\t\t\t\"expected no inbound exchanges\",\n\t\t\t)\n\n\t\t}), handlerName)\n\n\t\tclientCh := server.NewClient(nil)\n\t\tdefer clientCh.Close()\n\n\t\t_, _, _, err := raw.Call(ctx, clientCh, server.HostPort(), serviceName, handlerName, nil, nil)\n\t\trequire.Error(t, err, \"expected call error\")\n\n\t\terrCode := GetSystemErrorCode(err)\n\t\t// Providing 'got: %q' is necessary since SystemErrCode is a type alias of byte; testify's\n\t\t// failed test output would otherwise print out hex codes.\n\t\tassert.Equal(t, ErrCodeCancelled, errCode, \"expected cancelled error code, got: %q\", errCode)\n\t})\n}\n"
  },
  {
    "path": "incoming_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestPeersIncomingConnection(t *testing.T) {\n\tnewService := func(svcName string) (*Channel, string) {\n\t\tch, _, hostPort := NewServer(t, &testutils.ChannelOpts{ServiceName: svcName})\n\t\treturn ch, hostPort\n\t}\n\n\topts := testutils.NewOpts().NoRelay()\n\tWithVerifiedServer(t, opts, func(ch *Channel, hostPort string) {\n\t\tdoPing := func(ch *Channel) {\n\t\t\tctx, cancel := NewContext(time.Second)\n\t\t\tdefer cancel()\n\t\t\tassert.NoError(t, ch.Ping(ctx, hostPort), \"Ping failed\")\n\t\t}\n\n\t\thyperbahnSC := ch.GetSubChannel(\"hyperbahn\")\n\t\tringpopSC := ch.GetSubChannel(\"ringpop\", Isolated)\n\n\t\thyperbahn, hyperbahnHostPort := newService(\"hyperbahn\")\n\t\tdefer hyperbahn.Close()\n\t\tringpop, ringpopHostPort := newService(\"ringpop\")\n\t\tdefer ringpop.Close()\n\n\t\tdoPing(hyperbahn)\n\t\tdoPing(ringpop)\n\n\t\t// The root peer list should contain all incoming connections.\n\t\trootPeers := ch.RootPeers().Copy()\n\t\tassert.NotNil(t, rootPeers[hyperbahnHostPort], \"missing hyperbahn peer\")\n\t\tassert.NotNil(t, rootPeers[ringpopHostPort], \"missing ringpop peer\")\n\n\t\tfor _, sc := range []Registrar{ch, hyperbahnSC, ringpopSC} {\n\t\t\t_, err := sc.Peers().Get(nil)\n\t\t\tassert.Equal(t, ErrNoPeers, err,\n\t\t\t\t\"incoming connections should not be added to non-root peer list\")\n\t\t}\n\n\t\t// verify number of peers/connections on the client side\n\t\tserverState := ch.IntrospectState(nil).RootPeers\n\t\tserverHostPort := ch.PeerInfo().HostPort\n\n\t\tassert.Equal(t, len(serverState), 2, \"Incorrect peer count\")\n\t\tfor _, client := range []*Channel{ringpop, hyperbahn} {\n\t\t\tclientPeerState := client.IntrospectState(nil).RootPeers\n\t\t\tclientHostPort := client.PeerInfo().HostPort\n\t\t\tassert.Equal(t, len(clientPeerState), 1, \"Incorrect peer count\")\n\t\t\tassert.Equal(t, len(clientPeerState[serverHostPort].OutboundConnections), 1, \"Incorrect outbound connection count\")\n\t\t\tassert.Equal(t, len(clientPeerState[serverHostPort].InboundConnections), 0, \"Incorrect inbound connection count\")\n\n\t\t\tassert.Equal(t, len(serverState[clientHostPort].InboundConnections), 1, \"Incorrect inbound connection count\")\n\t\t\tassert.Equal(t, len(serverState[clientHostPort].OutboundConnections), 0, \"Incorrect outbound connection count\")\n\t\t}\n\n\t\t// In future when connections send a service name, we should be able to\n\t\t// check that a new connection containing a service name for an isolated\n\t\t// subchannel is only added to the isolated subchannels' peers, but all\n\t\t// other incoming connections are added to the shared peer list.\n\t})\n}\n"
  },
  {
    "path": "init_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc writeMessage(w io.Writer, msg message) error {\n\tf := NewFrame(MaxFramePayloadSize)\n\tif err := f.write(msg); err != nil {\n\t\treturn err\n\t}\n\treturn f.WriteOut(w)\n}\n\nfunc readFrame(r io.Reader) (*Frame, error) {\n\tf := NewFrame(MaxFramePayloadSize)\n\treturn f, f.ReadIn(r)\n}\n\nfunc TestUnexpectedInitReq(t *testing.T) {\n\ttests := []struct {\n\t\tname          string\n\t\tinitMsg       message\n\t\texpectedError errorMessage\n\t}{\n\t\t{\n\t\t\tname: \"bad version\",\n\t\t\tinitMsg: &initReq{initMessage{id: 1, Version: 0x1, initParams: initParams{\n\t\t\t\tInitParamHostPort:    \"0.0.0.0:0\",\n\t\t\t\tInitParamProcessName: \"test\",\n\t\t\t}}},\n\t\t\texpectedError: errorMessage{\n\t\t\t\tid:      1,\n\t\t\t\terrCode: ErrCodeProtocol,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"missing InitParamHostPort\",\n\t\t\tinitMsg: &initReq{initMessage{id: 2, Version: CurrentProtocolVersion, initParams: initParams{\n\t\t\t\tInitParamProcessName: \"test\",\n\t\t\t}}},\n\t\t\texpectedError: errorMessage{\n\t\t\t\tid:      2,\n\t\t\t\terrCode: ErrCodeProtocol,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"missing InitParamProcessName\",\n\t\t\tinitMsg: &initReq{initMessage{id: 3, Version: CurrentProtocolVersion, initParams: initParams{\n\t\t\t\tInitParamHostPort: \"0.0.0.0:0\",\n\t\t\t}}},\n\t\t\texpectedError: errorMessage{\n\t\t\t\tid:      3,\n\t\t\t\terrCode: ErrCodeProtocol,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"unexpected message type\",\n\t\t\tinitMsg: &pingReq{\n\t\t\t\tid: 1,\n\t\t\t},\n\t\t\texpectedError: errorMessage{\n\t\t\t\tid:      1,\n\t\t\t\terrCode: ErrCodeProtocol,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tch, err := NewChannel(\"test\", nil)\n\t\trequire.NoError(t, err)\n\t\tdefer ch.Close()\n\t\trequire.NoError(t, ch.ListenAndServe(\"127.0.0.1:0\"))\n\t\thostPort := ch.PeerInfo().HostPort\n\n\t\tconn, err := net.Dial(\"tcp\", hostPort)\n\t\trequire.NoError(t, err)\n\t\tconn.SetReadDeadline(time.Now().Add(time.Second))\n\n\t\tif !assert.NoError(t, writeMessage(conn, tt.initMsg), \"write to conn failed\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tf, err := readFrame(conn)\n\t\tif !assert.NoError(t, err, \"read frame failed\") {\n\t\t\tcontinue\n\t\t}\n\t\tassert.Equal(t, messageTypeError, f.Header.messageType)\n\t\tvar errMsg errorMessage\n\t\tif !assert.NoError(t, f.read(&errMsg), \"parse frame to errorMessage\") {\n\t\t\tcontinue\n\t\t}\n\t\tassert.Equal(t, tt.expectedError.ID(), f.Header.ID, \"test %v got bad ID\", tt.name)\n\t\tassert.Equal(t, tt.expectedError.errCode, errMsg.errCode, \"test %v got bad code\", tt.name)\n\t\tassert.NoError(t, conn.Close(), \"closing connection failed\")\n\t}\n}\n\nfunc TestUnexpectedInitRes(t *testing.T) {\n\tvalidParams := initParams{\n\t\tInitParamHostPort:    \"0.0.0.0:0\",\n\t\tInitParamProcessName: \"tchannel-go.test\",\n\t}\n\ttests := []struct {\n\t\tmsg    message\n\t\terrMsg string\n\t}{\n\t\t{\n\t\t\tmsg: &initRes{initMessage{\n\t\t\t\tid:         1,\n\t\t\t\tVersion:    CurrentProtocolVersion - 1,\n\t\t\t\tinitParams: validParams,\n\t\t\t}},\n\t\t\terrMsg: \"unsupported protocol version\",\n\t\t},\n\t\t{\n\t\t\tmsg: &initRes{initMessage{\n\t\t\t\tid:         1,\n\t\t\t\tVersion:    CurrentProtocolVersion + 1,\n\t\t\t\tinitParams: validParams,\n\t\t\t}},\n\t\t\terrMsg: \"unsupported protocol version\",\n\t\t},\n\t\t{\n\t\t\tmsg: &initRes{initMessage{\n\t\t\t\tid:      1,\n\t\t\t\tVersion: CurrentProtocolVersion,\n\t\t\t}},\n\t\t\terrMsg: \"header host_port is required\",\n\t\t},\n\t\t{\n\t\t\tmsg: &initRes{initMessage{\n\t\t\t\tid:      1,\n\t\t\t\tVersion: CurrentProtocolVersion,\n\t\t\t\tinitParams: initParams{\n\t\t\t\t\tInitParamHostPort: \"0.0.0.0:0\",\n\t\t\t\t},\n\t\t\t}},\n\t\t\terrMsg: \"header process_name is required\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\t\trequire.NoError(t, err, \"net.Listen failed\")\n\t\tdefer ln.Close()\n\n\t\tdone := make(chan struct{})\n\t\tgo func() {\n\t\t\tdefer close(done)\n\t\t\tch, err := NewChannel(\"test\", nil)\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer ch.Close()\n\n\t\t\tctx, cancel := NewContext(time.Second)\n\t\t\tdefer cancel()\n\t\t\t_, err = ch.Peers().GetOrAdd(ln.Addr().String()).GetConnection(ctx)\n\t\t\tif !assert.Error(t, err, \"Expected GetConnection to fail\") {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tassert.Equal(t, ErrCodeProtocol, GetSystemErrorCode(err), \"Unexpected error code, got error: %v\", err)\n\t\t\tassert.Contains(t, err.Error(), tt.errMsg)\n\t\t}()\n\n\t\tconn, err := ln.Accept()\n\t\trequire.NoError(t, err, \"Failed to accept connection\")\n\n\t\t// Read the frame and verify that it's an initReq.\n\t\tf, err := readFrame(conn)\n\t\trequire.NoError(t, err, \"read frame failed\")\n\t\tif !assert.Equal(t, messageTypeInitReq, f.messageType(), \"Expected first message to be initReq\") {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Write out the specified initRes wait for the channel to get an error.\n\t\tassert.NoError(t, writeMessage(conn, tt.msg), \"write initRes failed\")\n\t\t<-done\n\t}\n}\n\nfunc TestHandleInitReqNewVersion(t *testing.T) {\n\tch, err := NewChannel(\"test\", nil)\n\trequire.NoError(t, err)\n\tdefer ch.Close()\n\trequire.NoError(t, ch.ListenAndServe(\"127.0.0.1:0\"))\n\thostPort := ch.PeerInfo().HostPort\n\n\tconn, err := net.Dial(\"tcp\", hostPort)\n\trequire.NoError(t, err)\n\tdefer conn.Close()\n\tconn.SetReadDeadline(time.Now().Add(time.Second))\n\n\tinitMsg := &initReq{initMessage{id: 1, Version: CurrentProtocolVersion + 3, initParams: initParams{\n\t\tInitParamHostPort:    \"0.0.0.0:0\",\n\t\tInitParamProcessName: \"test\",\n\t}}}\n\trequire.NoError(t, writeMessage(conn, initMsg), \"write to conn failed\")\n\n\t// Verify we get an initRes back with the current protocol version.\n\tf, err := readFrame(conn)\n\trequire.NoError(t, err, \"expected frame with init res\")\n\n\tvar msg initRes\n\trequire.NoError(t, f.read(&msg), \"could not read init res from frame\")\n\tif assert.Equal(t, messageTypeInitRes, f.Header.messageType, \"expected initRes, got %v\", f.Header.messageType) {\n\t\tassert.Equal(t, initRes{\n\t\t\tinitMessage: initMessage{\n\t\t\t\tVersion: CurrentProtocolVersion,\n\t\t\t\tinitParams: initParams{\n\t\t\t\t\tInitParamHostPort:                ch.PeerInfo().HostPort,\n\t\t\t\t\tInitParamProcessName:             ch.PeerInfo().ProcessName,\n\t\t\t\t\tInitParamTChannelLanguage:        \"go\",\n\t\t\t\t\tInitParamTChannelLanguageVersion: strings.TrimPrefix(runtime.Version(), \"go\"),\n\t\t\t\t\tInitParamTChannelVersion:         VersionInfo,\n\t\t\t\t},\n\t\t\t},\n\t\t}, msg, \"unexpected init res\")\n\t}\n}\n\n// TestHandleInitRes ensures that a Connection is ready to handle messages immediately\n// after receiving an InitRes.\nfunc TestHandleInitRes(t *testing.T) {\n\tl := newListener(t)\n\tlistenerComplete := make(chan struct{})\n\n\tgo func() {\n\t\tconn, err := l.Accept()\n\t\trequire.NoError(t, err, \"l.Accept failed\")\n\n\t\t// The connection should be kept open until the test has completed running.\n\t\tdefer conn.Close()\n\t\tdefer func() { listenerComplete <- struct{}{} }()\n\n\t\tf, err := readFrame(conn)\n\t\trequire.NoError(t, err, \"readFrame failed\")\n\t\tassert.Equal(t, messageTypeInitReq, f.Header.messageType, \"expected initReq message\")\n\n\t\tvar msg initReq\n\t\trequire.NoError(t, f.read(&msg), \"read frame into initMsg failed\")\n\t\tinitRes := initRes{msg.initMessage}\n\t\tinitRes.initMessage.id = f.Header.ID\n\t\trequire.NoError(t, writeMessage(conn, &initRes), \"write initRes failed\")\n\t\trequire.NoError(t, writeMessage(conn, &pingReq{noBodyMsg{}, 10}), \"write pingReq failed\")\n\n\t\tf, err = readFrame(conn)\n\t\trequire.NoError(t, err, \"readFrame failed\")\n\t\tassert.Equal(t, messageTypePingRes, f.Header.messageType, \"expected pingRes message\")\n\t}()\n\n\tch, err := NewChannel(\"test-svc\", nil)\n\trequire.NoError(t, err, \"NewChannel failed\")\n\tdefer ch.Close()\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\t_, err = ch.Peers().GetOrAdd(l.Addr().String()).GetConnection(ctx)\n\trequire.NoError(t, err, \"GetConnection failed\")\n\n\t<-listenerComplete\n}\n\nfunc TestInitReqGetsError(t *testing.T) {\n\tl := newListener(t)\n\tlistenerComplete := make(chan struct{})\n\tconnectionComplete := make(chan struct{})\n\tgo func() {\n\t\tdefer func() { listenerComplete <- struct{}{} }()\n\t\tconn, err := l.Accept()\n\t\trequire.NoError(t, err, \"l.Accept failed\")\n\t\tdefer conn.Close()\n\n\t\tf, err := readFrame(conn)\n\t\trequire.NoError(t, err, \"readFrame failed\")\n\t\tassert.Equal(t, messageTypeInitReq, f.Header.messageType, \"expected initReq message\")\n\t\terr = writeMessage(conn, &errorMessage{\n\t\t\tid:      f.Header.ID,\n\t\t\terrCode: ErrCodeBadRequest,\n\t\t\tmessage: \"invalid host:port\",\n\t\t})\n\t\tassert.NoError(t, err, \"Failed to write errorMessage\")\n\t\t// Wait till GetConnection returns before closing the connection.\n\t\t<-connectionComplete\n\t}()\n\n\tlogOut := &bytes.Buffer{}\n\tch, err := NewChannel(\"test-svc\", &ChannelOptions{Logger: NewLevelLogger(NewLogger(logOut), LogLevelWarn)})\n\trequire.NoError(t, err, \"NewClient failed\")\n\tdefer ch.Close()\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\t_, err = ch.Peers().GetOrAdd(l.Addr().String()).GetConnection(ctx)\n\texpectedErr := NewSystemError(ErrCodeBadRequest, \"invalid host:port\")\n\tassert.Equal(t, expectedErr, err, \"Error mismatch\")\n\tassert.Contains(t, logOut.String(),\n\t\t\"[E] Failed during connection handshake.\",\n\t\t\"Message should be logged\")\n\tassert.Contains(t, logOut.String(),\n\t\t\"tchannel error ErrCodeBadRequest: invalid host:port\",\n\t\t\"Error should be logged\")\n\tclose(connectionComplete)\n\n\t<-listenerComplete\n}\n\nfunc newListener(t *testing.T) net.Listener {\n\tl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\trequire.NoError(t, err, \"Listen failed\")\n\treturn l\n}\n"
  },
  {
    "path": "internal/argreader/empty.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage argreader\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"sync\"\n)\n\nvar _bufPool = sync.Pool{\n\tNew: func() interface{} {\n\t\tb := make([]byte, 128)\n\t\treturn &b\n\t},\n}\n\n// EnsureEmpty ensures that the specified reader is empty. If the reader is\n// not empty, it returns an error with the specified stage in the message.\nfunc EnsureEmpty(r io.Reader, stage string) error {\n\tbuf := _bufPool.Get().(*[]byte)\n\tdefer _bufPool.Put(buf)\n\n\tn, err := r.Read(*buf)\n\tif n > 0 {\n\t\treturn fmt.Errorf(\"found unexpected bytes after %s, found (upto 128 bytes): %x\", stage, (*buf)[:n])\n\t}\n\tif err == io.EOF {\n\t\treturn nil\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "internal/argreader/empty_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage argreader\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/uber/tchannel-go/testutils/testreader\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestEnsureEmptySuccess(t *testing.T) {\n\treader := bytes.NewReader(nil)\n\terr := EnsureEmpty(reader, \"success\")\n\trequire.NoError(t, err, \"ensureEmpty should succeed with empty reader\")\n}\n\nfunc TestEnsureEmptyHasBytes(t *testing.T) {\n\treader := bytes.NewReader([]byte{1, 2, 3})\n\terr := EnsureEmpty(reader, \"T\")\n\trequire.Error(t, err, \"ensureEmpty should fail when there's bytes\")\n\tassert.Equal(t, err.Error(), \"found unexpected bytes after T, found (upto 128 bytes): 010203\")\n}\n\nfunc TestEnsureEmptyError(t *testing.T) {\n\tcontrol, reader := testreader.ChunkReader()\n\tcontrol <- nil\n\tclose(control)\n\n\terr := EnsureEmpty(reader, \"has bytes\")\n\trequire.Error(t, err, \"ensureEmpty should fail when there's an error\")\n\tassert.Equal(t, testreader.ErrUser, err, \"Unexpected error\")\n}\n"
  },
  {
    "path": "internal/testcert/testcert.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testcert\n\n// TestCert is a PEM-encoded TLS cert with SAN IPs\n// \"127.0.0.1\" and \"[::1]\", expiring at Jan 29 16:00:00 2084 GMT.\n// generated from src/crypto/tls:\n// go run \"$(go env GOROOT)/src/crypto/tls/generate_cert.go\"  --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date \"Jan 1 00:00:00 1970\" --duration=1000000h\nvar TestCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIDOTCCAiGgAwIBAgIQD2X8uKDzMVRc0crgmNX/0zANBgkqhkiG9w0BAQsFADAS\nMRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw\nMDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\nMIIBCgKCAQEAqvPofDY9ItZCO7TWb/Symnb38SuuJt4o6iTNlsE0wFPfWdYlE760\nPRW2rUqE7t0M2AQwHD3OWPpzLZcqZA2aSKEyx/GmQuNUYN87idYW1JhbxD3zn14P\nfflcf9s3PiWscnOM9xmPOkSvCptG9IdOs2l1TqmM91+z6AIS/M1yJvETcLJjZqTE\nv5YK8RuSdTk1prgKA25HLSnwn8JFkG3L9lc0y96W2gwcW5j3+RmVie+k57pa67LD\naD2cMBDXcI+OFlDxecjtuaKJBZtbU/0QS0ehc9XXCgRvwUlg1T/MDb5Oi5z+rhuK\nCP2aLd7QvTYiSgw3J0f/g52QWdBzkBaZFQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE\nAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\nDgQWBBQqXSCk6h8ksO7U+3NH2nsM0GPkRjAuBgNVHREEJzAlggtleGFtcGxlLmNv\nbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAf4DP\nyoGZ26s5IkBK5iJBpIFtIWnejBSPc7gdFmQsFb9qjRt7kQf7bKLkER0FLFmq3I0f\nlsmWcYwvuLZSCQppxNB1lzcWqiE9LkHrO1wNJqcipPtOwhg9VYLgwi2BJd6mMr++\nEHJntBgGpsvM4nqSanjjMlaE1ZPP2flt8/xSnikY78P7aYmHPL4xY5Al8zI09H1o\npc96r62fgMPMSDibhF5tqz5nK7Olt2Jd/alHd7LMzVOQw2DfCaBrj8OPO2J4ppvu\nrqJ+Izqv7kZpwU1Ye6dFG/F8TOp1iWhkCoVR17FP6dqY1BZLfxiz3YsoS+2XVh3z\nCTWY1J1Aj1WiEVBTfg==\n-----END CERTIFICATE-----`)\n\n// TestKey is the private key for TestCert.\nvar TestKey = []byte(`-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCq8+h8Nj0i1kI7\ntNZv9LKadvfxK64m3ijqJM2WwTTAU99Z1iUTvrQ9FbatSoTu3QzYBDAcPc5Y+nMt\nlypkDZpIoTLH8aZC41Rg3zuJ1hbUmFvEPfOfXg99+Vx/2zc+Jaxyc4z3GY86RK8K\nm0b0h06zaXVOqYz3X7PoAhL8zXIm8RNwsmNmpMS/lgrxG5J1OTWmuAoDbkctKfCf\nwkWQbcv2VzTL3pbaDBxbmPf5GZWJ76TnulrrssNoPZwwENdwj44WUPF5yO25ookF\nm1tT/RBLR6Fz1dcKBG/BSWDVP8wNvk6LnP6uG4oI/Zot3tC9NiJKDDcnR/+DnZBZ\n0HOQFpkVAgMBAAECggEATDuyW9mwD53uMUPmMEy1bK5KyNBKu+hr5GX/DBAiXvXH\n7v7Qz+pF48uQB9zoRMBsXtQXRDDHmOQugpEbhTyPpX3E8GaxVribQwupOEExMyKy\nIWPjBRlj3TBa8GUoUF1qditTHEnYlgpU6GzwClFgZh9MAYUYaKPTzU1HfFZ9ZiF2\njZB841HorsAJzbTnKXpHSK51GZ0ecOPGhRMkImsAskuI/EY5RBUZJmI9vVrs0pIu\nOO9TcAvSs9tNXfM8YrJwZVMG11qiCcvfHD3VuYhsYEOvCsjxSmRp4DCYlISTlUr+\nLXv7VdhGMoeSdQVQqpqPF9kqkghfOzQFQ9ppzw6iDQKBgQDSmPNIY0f7nZH4diir\nA0WUl7QzzUyf2qX4UrYzgGHufEfanTlrS3sTAdEkK85oxfNygLBXYmxtrzcQWVFD\ngx5cXDHaH6ZVoZxSRrDyO37vrVv76NSrOH3yqq9j8gytf3M74dTcunMVOGGdx1Zi\nD/AQ05KpjdKmhBDyCdGcHvXAqwKBgQDPzu8YdP56w3VNkPAlXRLZu9g5eZHj4uPF\nNRexV8BdbQ8EVu3KnIjzCSUSjPdGDN18ycgTrU0AzQ8MxQE8rqebs/otPTKsYJt4\nSwR/Ol+lDC+lGdSTREUu677MPE0buAce0UBQ9RtWoYUEsNEI6sFqReaCqmri55tm\nioM4T3qNPwKBgQCQU8YXDANfC2PodYH1gW6EIVucTMyAmSY5guXfcdKr0Hyl9C5P\nvBECu7ILKgJxh4gKJuuzV36bxQLlr3Cj5g4+meiIZjxmXzV0pYHK4L9jntl1UOG+\n3h5i2lsNEetiVAAzP9fT1evc1SEBMoWe+vE5duYCUXHWMJg0aEpAxm8BtQKBgQCX\nBYBlecDnXt0E/exIexeT/RvqyRrpTp7RVwBc9bTrMLLVKIev04sDdQXoMWITGo5s\nfghVpIBtsJjbYuC/RP6x/V43Ol51P9A83+fovnd77xtBFUCTte3BZ7pFmx0+o8Mo\n9lGThE3V65RMEGQZ4uGlZh9bnpYHSOJ65vbuGXSq6QKBgHthfDeAsW7V4JIm0IG+\nsEkFjGvYhyngDbOKMSf9YN3YuuuLPawHQJYe7gmH4p/Wry+oUcF8t5ddhwLd63xz\nq4LAT9EgEvfLEbMnxjvLHUG/eeRx6zqCf54+KHfGCcooOI4kbI7lkQglLq5DWDe2\n4n6AEKY0aVWJ1zN9B/vaJMZM\n-----END PRIVATE KEY-----`)\n"
  },
  {
    "path": "introspection.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"runtime\"\n\t\"sort\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"golang.org/x/net/context\"\n)\n\n// IntrospectionOptions are the options used when introspecting the Channel.\ntype IntrospectionOptions struct {\n\t// IncludeExchanges will include all the IDs in the message exchanges.\n\tIncludeExchanges bool `json:\"includeExchanges\"`\n\n\t// IncludeEmptyPeers will include peers, even if they have no connections.\n\tIncludeEmptyPeers bool `json:\"includeEmptyPeers\"`\n\n\t// IncludeTombstones will include tombstones when introspecting relays.\n\tIncludeTombstones bool `json:\"includeTombstones\"`\n\n\t// IncludeOtherChannels will include basic information about other channels\n\t// created in the same process as this channel.\n\tIncludeOtherChannels bool `json:\"includeOtherChannels\"`\n}\n\n// RuntimeVersion includes version information about the runtime and\n// the tchannel library.\ntype RuntimeVersion struct {\n\tGoVersion      string `json:\"goVersion\"`\n\tLibraryVersion string `json:\"tchannelVersion\"`\n}\n\n// RuntimeState is a snapshot of the runtime state for a channel.\ntype RuntimeState struct {\n\tID           uint32 `json:\"id\"`\n\tChannelState string `json:\"channelState\"`\n\n\t// CreatedStack is the stack for how this channel was created.\n\tCreatedStack string `json:\"createdStack\"`\n\n\t// LocalPeer is the local peer information (service name, host-port, etc).\n\tLocalPeer LocalPeerInfo `json:\"localPeer\"`\n\n\t// SubChannels contains information about any subchannels.\n\tSubChannels map[string]SubChannelRuntimeState `json:\"subChannels\"`\n\n\t// RootPeers contains information about all the peers on this channel and their connections.\n\tRootPeers map[string]PeerRuntimeState `json:\"rootPeers\"`\n\n\t// Peers is the list of shared peers for this channel.\n\tPeers []SubPeerScore `json:\"peers\"`\n\n\t// NumConnections is the number of connections stored in the channel.\n\tNumConnections int `json:\"numConnections\"`\n\n\t// Connections is the list of connection IDs in the channel\n\tConnections []uint32 ` json:\"connections\"`\n\n\t// InactiveConnections is the connection state for connections that are not active,\n\t// and hence are not reported as part of root peers.\n\tInactiveConnections []ConnectionRuntimeState `json:\"inactiveConnections\"`\n\n\t// OtherChannels is information about any other channels running in this process.\n\tOtherChannels map[string][]ChannelInfo `json:\"otherChannels,omitEmpty\"`\n\n\t// RuntimeVersion is the version information about the runtime and the library.\n\tRuntimeVersion RuntimeVersion `json:\"runtimeVersion\"`\n}\n\n// GoRuntimeStateOptions are the options used when getting Go runtime state.\ntype GoRuntimeStateOptions struct {\n\t// IncludeGoStacks will include all goroutine stacks.\n\tIncludeGoStacks bool `json:\"includeGoStacks\"`\n}\n\n// ChannelInfo is the state of other channels in the same process.\ntype ChannelInfo struct {\n\tID           uint32        `json:\"id\"`\n\tCreatedStack string        `json:\"createdStack\"`\n\tLocalPeer    LocalPeerInfo `json:\"localPeer\"`\n}\n\n// GoRuntimeState is a snapshot of runtime stats from the runtime.\ntype GoRuntimeState struct {\n\tMemStats      runtime.MemStats `json:\"memStats\"`\n\tNumGoroutines int              `json:\"numGoRoutines\"`\n\tNumCPU        int              `json:\"numCPU\"`\n\tNumCGo        int64            `json:\"numCGo\"`\n\tGoStacks      []byte           `json:\"goStacks,omitempty\"`\n}\n\n// SubChannelRuntimeState is the runtime state for a subchannel.\ntype SubChannelRuntimeState struct {\n\tService  string `json:\"service\"`\n\tIsolated bool   `json:\"isolated\"`\n\t// IsolatedPeers is the list of all isolated peers for this channel.\n\tIsolatedPeers []SubPeerScore      `json:\"isolatedPeers,omitempty\"`\n\tHandler       HandlerRuntimeState `json:\"handler\"`\n}\n\n// HandlerRuntimeState TODO\ntype HandlerRuntimeState struct {\n\tType    handlerType `json:\"type\"`\n\tMethods []string    `json:\"methods,omitempty\"`\n}\n\ntype handlerType string\n\nfunc (h handlerType) String() string { return string(h) }\n\nconst (\n\tmethodHandler   handlerType = \"methods\"\n\toverrideHandler             = \"overriden\"\n)\n\n// SubPeerScore show the runtime state of a peer with score.\ntype SubPeerScore struct {\n\tHostPort string `json:\"hostPort\"`\n\tScore    uint64 `json:\"score\"`\n}\n\n// ConnectionRuntimeState is the runtime state for a single connection.\ntype ConnectionRuntimeState struct {\n\tID                uint32                  `json:\"id\"`\n\tConnectionState   string                  `json:\"connectionState\"`\n\tLocalHostPort     string                  `json:\"localHostPort\"`\n\tRemoteHostPort    string                  `json:\"remoteHostPort\"`\n\tOutboundHostPort  string                  `json:\"outboundHostPort\"`\n\tRemotePeer        PeerInfo                `json:\"remotePeer\"`\n\tInboundExchange   ExchangeSetRuntimeState `json:\"inboundExchange\"`\n\tOutboundExchange  ExchangeSetRuntimeState `json:\"outboundExchange\"`\n\tRelayer           RelayerRuntimeState     `json:\"relayer\"`\n\tHealthChecks      []bool                  `json:\"healthChecks,omitempty\"`\n\tLastActivityRead  int64                   `json:\"lastActivityRead\"`\n\tLastActivityWrite int64                   `json:\"lastActivityWrite\"`\n\tSendChQueued      int                     `json:\"sendChQueued\"`\n\tSendChCapacity    int                     `json:\"sendChCapacity\"`\n\tSendBufferUsage   int                     `json:\"sendBufferUsage\"`\n\tSendBufferSize    int                     `json:\"sendBufferSize\"`\n}\n\n// RelayerRuntimeState is the runtime state for a single relayer.\ntype RelayerRuntimeState struct {\n\tCount                int               `json:\"count\"`\n\tInboundItems         RelayItemSetState `json:\"inboundItems\"`\n\tOutboundItems        RelayItemSetState `json:\"outboundItems\"`\n\tMaxTimeout           time.Duration     `json:\"maxTimeout\"`\n\tMaxConnectionTimeout time.Duration     `json:\"maxConnectionTimeout\"`\n}\n\n// ExchangeSetRuntimeState is the runtime state for a message exchange set.\ntype ExchangeSetRuntimeState struct {\n\tName      string                          `json:\"name\"`\n\tCount     int                             `json:\"count\"`\n\tExchanges map[string]ExchangeRuntimeState `json:\"exchanges,omitempty\"`\n}\n\n// RelayItemSetState is the runtime state for a list of relay items.\ntype RelayItemSetState struct {\n\tName  string                    `json:\"name\"`\n\tCount int                       `json:\"count\"`\n\tItems map[string]RelayItemState `json:\"items,omitempty\"`\n}\n\n// ExchangeRuntimeState is the runtime state for a single message exchange.\ntype ExchangeRuntimeState struct {\n\tID          uint32      `json:\"id\"`\n\tMessageType messageType `json:\"messageType\"`\n}\n\n// RelayItemState is the runtime state for a single relay item.\ntype RelayItemState struct {\n\tID                      uint32 `json:\"id\"`\n\tRemapID                 uint32 `json:\"remapID\"`\n\tDestinationConnectionID uint32 `json:\"destinationConnectionID\"`\n\tTomb                    bool   `json:\"tomb\"`\n}\n\n// PeerRuntimeState is the runtime state for a single peer.\ntype PeerRuntimeState struct {\n\tHostPort            string                   `json:\"hostPort\"`\n\tOutboundConnections []ConnectionRuntimeState `json:\"outboundConnections\"`\n\tInboundConnections  []ConnectionRuntimeState `json:\"inboundConnections\"`\n\tChosenCount         uint64                   `json:\"chosenCount\"`\n\tSCCount             uint32                   `json:\"scCount\"`\n}\n\n// IntrospectState returns the RuntimeState for this channel.\n// Note: this is purely for debugging and monitoring, and may slow down your Channel.\nfunc (ch *Channel) IntrospectState(opts *IntrospectionOptions) *RuntimeState {\n\tif opts == nil {\n\t\topts = &IntrospectionOptions{}\n\t}\n\n\tch.mutable.RLock()\n\tstate := ch.mutable.state\n\tnumConns := len(ch.mutable.conns)\n\tinactiveConns := make([]*Connection, 0, numConns)\n\tconnIDs := make([]uint32, 0, numConns)\n\tfor id, conn := range ch.mutable.conns {\n\t\tconnIDs = append(connIDs, id)\n\t\tif !conn.IsActive() {\n\t\t\tinactiveConns = append(inactiveConns, conn)\n\t\t}\n\t}\n\n\tch.mutable.RUnlock()\n\n\tch.State()\n\treturn &RuntimeState{\n\t\tID:                  ch.chID,\n\t\tChannelState:        state.String(),\n\t\tCreatedStack:        ch.createdStack,\n\t\tLocalPeer:           ch.PeerInfo(),\n\t\tSubChannels:         ch.subChannels.IntrospectState(opts),\n\t\tRootPeers:           ch.RootPeers().IntrospectState(opts),\n\t\tPeers:               ch.Peers().IntrospectList(opts),\n\t\tNumConnections:      numConns,\n\t\tConnections:         connIDs,\n\t\tInactiveConnections: getConnectionRuntimeState(inactiveConns, opts),\n\t\tOtherChannels:       ch.IntrospectOthers(opts),\n\t\tRuntimeVersion:      introspectRuntimeVersion(),\n\t}\n}\n\n// IntrospectOthers returns the ChannelInfo for all other channels in this process.\nfunc (ch *Channel) IntrospectOthers(opts *IntrospectionOptions) map[string][]ChannelInfo {\n\tif !opts.IncludeOtherChannels {\n\t\treturn nil\n\t}\n\n\tchannelMap.Lock()\n\tdefer channelMap.Unlock()\n\n\tstates := make(map[string][]ChannelInfo)\n\tfor svc, channels := range channelMap.existing {\n\t\tchannelInfos := make([]ChannelInfo, 0, len(channels))\n\t\tfor _, otherChan := range channels {\n\t\t\tif ch == otherChan {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tchannelInfos = append(channelInfos, otherChan.ReportInfo(opts))\n\t\t}\n\t\tstates[svc] = channelInfos\n\t}\n\n\treturn states\n}\n\n// ReportInfo returns ChannelInfo for a channel.\nfunc (ch *Channel) ReportInfo(opts *IntrospectionOptions) ChannelInfo {\n\treturn ChannelInfo{\n\t\tID:           ch.chID,\n\t\tCreatedStack: ch.createdStack,\n\t\tLocalPeer:    ch.PeerInfo(),\n\t}\n}\n\ntype containsPeerList interface {\n\tCopy() map[string]*Peer\n}\n\nfunc fromPeerList(peers containsPeerList, opts *IntrospectionOptions) map[string]PeerRuntimeState {\n\tm := make(map[string]PeerRuntimeState)\n\tfor _, peer := range peers.Copy() {\n\t\tpeerState := peer.IntrospectState(opts)\n\t\tif len(peerState.InboundConnections)+len(peerState.OutboundConnections) > 0 || opts.IncludeEmptyPeers {\n\t\t\tm[peer.HostPort()] = peerState\n\t\t}\n\t}\n\treturn m\n}\n\n// IntrospectState returns the runtime state of the\nfunc (l *RootPeerList) IntrospectState(opts *IntrospectionOptions) map[string]PeerRuntimeState {\n\treturn fromPeerList(l, opts)\n}\n\n// IntrospectState returns the runtime state of the subchannels.\nfunc (subChMap *subChannelMap) IntrospectState(opts *IntrospectionOptions) map[string]SubChannelRuntimeState {\n\tm := make(map[string]SubChannelRuntimeState)\n\tsubChMap.RLock()\n\tfor k, sc := range subChMap.subchannels {\n\t\tstate := SubChannelRuntimeState{\n\t\t\tService:  k,\n\t\t\tIsolated: sc.Isolated(),\n\t\t}\n\t\tif state.Isolated {\n\t\t\tstate.IsolatedPeers = sc.Peers().IntrospectList(opts)\n\t\t}\n\t\tif hmap, ok := sc.handler.(*handlerMap); ok {\n\t\t\tstate.Handler.Type = methodHandler\n\t\t\tmethods := make([]string, 0, len(hmap.handlers))\n\t\t\tfor k := range hmap.handlers {\n\t\t\t\tmethods = append(methods, k)\n\t\t\t}\n\t\t\tsort.Strings(methods)\n\t\t\tstate.Handler.Methods = methods\n\t\t} else {\n\t\t\tstate.Handler.Type = overrideHandler\n\t\t}\n\t\tm[k] = state\n\t}\n\tsubChMap.RUnlock()\n\treturn m\n}\n\nfunc getConnectionRuntimeState(conns []*Connection, opts *IntrospectionOptions) []ConnectionRuntimeState {\n\tconnStates := make([]ConnectionRuntimeState, len(conns))\n\n\tfor i, conn := range conns {\n\t\tconnStates[i] = conn.IntrospectState(opts)\n\t}\n\n\treturn connStates\n}\n\n// IntrospectState returns the runtime state for this peer.\nfunc (p *Peer) IntrospectState(opts *IntrospectionOptions) PeerRuntimeState {\n\tp.RLock()\n\tdefer p.RUnlock()\n\n\treturn PeerRuntimeState{\n\t\tHostPort:            p.hostPort,\n\t\tInboundConnections:  getConnectionRuntimeState(p.inboundConnections, opts),\n\t\tOutboundConnections: getConnectionRuntimeState(p.outboundConnections, opts),\n\t\tChosenCount:         p.chosenCount.Load(),\n\t\tSCCount:             p.scCount,\n\t}\n}\n\n// IntrospectState returns the runtime state for this connection.\nfunc (c *Connection) IntrospectState(opts *IntrospectionOptions) ConnectionRuntimeState {\n\tc.stateMut.RLock()\n\tdefer c.stateMut.RUnlock()\n\n\t// Ignore errors getting send buffer sizes.\n\tsendBufUsage, sendBufSize, _ := c.sendBufSize()\n\n\t// TODO(prashantv): Add total number of health checks, and health check options.\n\tstate := ConnectionRuntimeState{\n\t\tID:                c.connID,\n\t\tConnectionState:   c.state.String(),\n\t\tLocalHostPort:     c.conn.LocalAddr().String(),\n\t\tRemoteHostPort:    c.conn.RemoteAddr().String(),\n\t\tOutboundHostPort:  c.outboundHP,\n\t\tRemotePeer:        c.remotePeerInfo,\n\t\tInboundExchange:   c.inbound.IntrospectState(opts),\n\t\tOutboundExchange:  c.outbound.IntrospectState(opts),\n\t\tHealthChecks:      c.healthCheckHistory.asBools(),\n\t\tLastActivityRead:  c.lastActivityRead.Load(),\n\t\tLastActivityWrite: c.lastActivityWrite.Load(),\n\t\tSendChQueued:      len(c.sendCh),\n\t\tSendChCapacity:    cap(c.sendCh),\n\t\tSendBufferUsage:   sendBufUsage,\n\t\tSendBufferSize:    sendBufSize,\n\t}\n\tif c.relay != nil {\n\t\tstate.Relayer = c.relay.IntrospectState(opts)\n\t}\n\treturn state\n}\n\n// IntrospectState returns the runtime state for this relayer.\nfunc (r *Relayer) IntrospectState(opts *IntrospectionOptions) RelayerRuntimeState {\n\tcount := r.inbound.Count() + r.outbound.Count()\n\treturn RelayerRuntimeState{\n\t\tCount:                count,\n\t\tInboundItems:         r.inbound.IntrospectState(opts, \"inbound\"),\n\t\tOutboundItems:        r.outbound.IntrospectState(opts, \"outbound\"),\n\t\tMaxTimeout:           r.maxTimeout,\n\t\tMaxConnectionTimeout: r.maxConnTimeout,\n\t}\n}\n\n// IntrospectState returns the runtime state for this relayItems.\nfunc (ri *relayItems) IntrospectState(opts *IntrospectionOptions, name string) RelayItemSetState {\n\tsetState := RelayItemSetState{\n\t\tName:  name,\n\t\tCount: ri.Count(),\n\t}\n\tif opts.IncludeExchanges {\n\t\tri.RLock()\n\t\tdefer ri.RUnlock()\n\n\t\tsetState.Items = make(map[string]RelayItemState, len(ri.items))\n\t\tfor k, v := range ri.items {\n\t\t\tif !opts.IncludeTombstones && v.tomb {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tstate := RelayItemState{\n\t\t\t\tID:                      k,\n\t\t\t\tRemapID:                 v.remapID,\n\t\t\t\tDestinationConnectionID: v.destination.conn.connID,\n\t\t\t\tTomb:                    v.tomb,\n\t\t\t}\n\t\t\tsetState.Items[strconv.Itoa(int(k))] = state\n\t\t}\n\t}\n\n\treturn setState\n}\n\n// IntrospectState returns the runtime state for this messsage exchange set.\nfunc (mexset *messageExchangeSet) IntrospectState(opts *IntrospectionOptions) ExchangeSetRuntimeState {\n\tmexset.RLock()\n\tsetState := ExchangeSetRuntimeState{\n\t\tName:  mexset.name,\n\t\tCount: len(mexset.exchanges),\n\t}\n\n\tif opts != nil && opts.IncludeExchanges {\n\t\tsetState.Exchanges = make(map[string]ExchangeRuntimeState, len(mexset.exchanges))\n\t\tfor k, v := range mexset.exchanges {\n\t\t\tstate := ExchangeRuntimeState{\n\t\t\t\tID:          k,\n\t\t\t\tMessageType: v.msgType,\n\t\t\t}\n\t\t\tsetState.Exchanges[strconv.Itoa(int(k))] = state\n\t\t}\n\t}\n\n\tmexset.RUnlock()\n\treturn setState\n}\n\nfunc getStacks(all bool) []byte {\n\tvar buf []byte\n\tfor n := 4096; n < 10*1024*1024; n *= 2 {\n\t\tbuf = make([]byte, n)\n\t\tstackLen := runtime.Stack(buf, all)\n\t\tif stackLen < n {\n\t\t\treturn buf[:stackLen]\n\t\t}\n\t}\n\n\t// return the first 10MB of stacks if we have more than 10MB.\n\treturn buf\n}\nfunc (ch *Channel) handleIntrospection(arg3 []byte) interface{} {\n\tvar opts struct {\n\t\tIntrospectionOptions\n\n\t\t// (optional) ID of the channel to introspection. If unspecified, uses ch.\n\t\tChannelID *uint32 `json:\"id\"`\n\t}\n\tjson.Unmarshal(arg3, &opts)\n\n\tif opts.ChannelID != nil {\n\t\tid := *opts.ChannelID\n\n\t\tvar ok bool\n\t\tch, ok = findChannelByID(id)\n\t\tif !ok {\n\t\t\treturn map[string]string{\"error\": fmt.Sprintf(`failed to find channel with \"id\": %v`, id)}\n\t\t}\n\t}\n\n\treturn ch.IntrospectState(&opts.IntrospectionOptions)\n}\n\n// IntrospectList returns the list of peers (hostport, score) in this peer list.\nfunc (l *PeerList) IntrospectList(opts *IntrospectionOptions) []SubPeerScore {\n\tvar peers []SubPeerScore\n\tl.RLock()\n\tfor _, ps := range l.peerHeap.peerScores {\n\t\tpeers = append(peers, SubPeerScore{\n\t\t\tHostPort: ps.Peer.hostPort,\n\t\t\tScore:    ps.score,\n\t\t})\n\t}\n\tl.RUnlock()\n\n\treturn peers\n}\n\n// IntrospectNumConnections returns the number of connections returns the number\n// of connections. Note: like other introspection APIs, this is not a stable API.\nfunc (ch *Channel) IntrospectNumConnections() int {\n\tch.mutable.RLock()\n\tnumConns := len(ch.mutable.conns)\n\tch.mutable.RUnlock()\n\treturn numConns\n}\n\nfunc handleInternalRuntime(arg3 []byte) interface{} {\n\tvar opts GoRuntimeStateOptions\n\tjson.Unmarshal(arg3, &opts)\n\n\tstate := GoRuntimeState{\n\t\tNumGoroutines: runtime.NumGoroutine(),\n\t\tNumCPU:        runtime.NumCPU(),\n\t\tNumCGo:        runtime.NumCgoCall(),\n\t}\n\truntime.ReadMemStats(&state.MemStats)\n\tif opts.IncludeGoStacks {\n\t\tstate.GoStacks = getStacks(true /* all */)\n\t}\n\n\treturn state\n}\n\nfunc introspectRuntimeVersion() RuntimeVersion {\n\treturn RuntimeVersion{\n\t\tGoVersion:      runtime.Version(),\n\t\tLibraryVersion: VersionInfo,\n\t}\n}\n\n// registerInternal registers the following internal handlers which return runtime state:\n//\n//\t_gometa_introspect: TChannel internal state.\n//\t_gometa_runtime: Golang runtime stats.\nfunc (ch *Channel) createInternalHandlers() *handlerMap {\n\tinternalHandlers := &handlerMap{}\n\n\tendpoints := []struct {\n\t\tname    string\n\t\thandler func([]byte) interface{}\n\t}{\n\t\t{\"_gometa_introspect\", ch.handleIntrospection},\n\t\t{\"_gometa_runtime\", handleInternalRuntime},\n\t}\n\n\tfor _, ep := range endpoints {\n\t\t// We need ep in our closure.\n\t\tep := ep\n\t\thandler := func(ctx context.Context, call *InboundCall) {\n\t\t\tvar arg2, arg3 []byte\n\t\t\tif err := NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err := NewArgReader(call.Arg3Reader()).Read(&arg3); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err := NewArgWriter(call.Response().Arg2Writer()).Write(nil); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tNewArgWriter(call.Response().Arg3Writer()).WriteJSON(ep.handler(arg3))\n\t\t}\n\n\t\th := HandlerFunc(handler)\n\t\tinternalHandlers.Register(h, ep.name)\n\n\t\t// Register under the service name of channel as well (for backwards compatibility).\n\t\tch.GetSubChannel(ch.PeerInfo().ServiceName).Register(h, ep.name)\n\t}\n\n\treturn internalHandlers\n}\n"
  },
  {
    "path": "introspection_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"context\"\n\t\"math\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/json\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// Purpose of this test is to ensure introspection doesn't cause any panics\n// and we have coverage of the introspection code.\nfunc TestIntrospection(t *testing.T) {\n\topts := testutils.NewOpts().\n\t\tAddLogFilter(\"Couldn't find handler\", 1). // call with service name fails\n\t\tNoRelay()                                 // \"tchannel\" service name is not forwarded.\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tclient := testutils.NewClient(t, nil)\n\t\tdefer client.Close()\n\n\t\tctx, cancel := json.NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\tvar resp map[string]interface{}\n\t\tpeer := client.Peers().GetOrAdd(ts.HostPort())\n\t\terr := json.CallPeer(ctx, peer, \"tchannel\", \"_gometa_introspect\", map[string]interface{}{\n\t\t\t\"includeExchanges\":  true,\n\t\t\t\"includeEmptyPeers\": true,\n\t\t\t\"includeTombstones\": true,\n\t\t}, &resp)\n\t\trequire.NoError(t, err, \"Call _gometa_introspect failed\")\n\n\t\terr = json.CallPeer(ctx, peer, ts.ServiceName(), \"_gometa_introspect\", nil /* arg */, &resp)\n\t\trequire.NoError(t, err, \"Call _gometa_introspect failed\")\n\n\t\t// Try making the call on any other service name will fail.\n\t\terr = json.CallPeer(ctx, peer, \"unknown-service\", \"_gometa_runtime\", map[string]interface{}{\n\t\t\t\"includeGoStacks\": true,\n\t\t}, &resp)\n\t\trequire.Error(t, err, \"_gometa_introspect should only be registered under tchannel\")\n\t})\n}\n\nfunc TestIntrospectByID(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tclient := testutils.NewClient(t, nil)\n\t\tdefer client.Close()\n\n\t\tctx, cancel := json.NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\tclientID := client.IntrospectState(nil).ID\n\n\t\tvar resp map[string]interface{}\n\t\tpeer := client.Peers().GetOrAdd(ts.HostPort())\n\t\terr := json.CallPeer(ctx, peer, ts.ServiceName(), \"_gometa_introspect\", map[string]interface{}{\n\t\t\t\"id\": clientID,\n\t\t}, &resp)\n\t\trequire.NoError(t, err, \"Call _gometa_introspect failed\")\n\n\t\t// Verify that the response matches the channel ID we expected.\n\t\tassert.EqualValues(t, clientID, resp[\"id\"], \"unexpected response channel ID\")\n\n\t\t// If use an ID which doesn't exist, we get an error\n\t\tresp = nil\n\t\terr = json.CallPeer(ctx, peer, ts.ServiceName(), \"_gometa_introspect\", map[string]interface{}{\n\t\t\t\"id\": math.MaxUint32,\n\t\t}, &resp)\n\t\trequire.NoError(t, err, \"Call _gometa_introspect failed\")\n\t\tassert.EqualValues(t, `failed to find channel with \"id\": `+strconv.Itoa(math.MaxUint32), resp[\"error\"])\n\t})\n}\n\nfunc TestIntrospectClosedConn(t *testing.T) {\n\t// Disable the relay, since the relay does not maintain a 1:1 mapping betewen\n\t// incoming connections vs outgoing connections.\n\topts := testutils.NewOpts().NoRelay()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tblockEcho := make(chan struct{})\n\t\tgotEcho := make(chan struct{})\n\t\ttestutils.RegisterEcho(ts.Server(), func() {\n\t\t\tclose(gotEcho)\n\t\t\t<-blockEcho\n\t\t})\n\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\tassert.Equal(t, 0, ts.Server().IntrospectNumConnections(), \"Expected no connection on new server\")\n\n\t\t// Make sure that a closed connection will reduce NumConnections.\n\t\tclient := ts.NewClient(nil)\n\t\trequire.NoError(t, client.Ping(ctx, ts.HostPort()), \"Ping from new client failed\")\n\t\tassert.Equal(t, 1, ts.Server().IntrospectNumConnections(), \"Number of connections expected to increase\")\n\n\t\tgo testutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\n\t\t// The state will change to \"closeStarted\", but be blocked due to the blocked\n\t\t// echo call.\n\t\t<-gotEcho\n\t\tclient.Close()\n\n\t\tintrospected := client.IntrospectState(nil)\n\t\tassert.Len(t, introspected.Connections, 1, \"Expected single connection due to blocked call\")\n\t\tassert.Len(t, introspected.InactiveConnections, 1, \"Expected inactive connection due to blocked call\")\n\n\t\tclose(blockEcho)\n\t\trequire.True(t, testutils.WaitFor(100*time.Millisecond, func() bool {\n\t\t\treturn ts.Server().IntrospectNumConnections() == 0\n\t\t}), \"Closed connection did not get removed, num connections is %v\", ts.Server().IntrospectNumConnections())\n\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tclient := ts.NewClient(nil)\n\t\t\tdefer client.Close()\n\n\t\t\trequire.NoError(t, client.Ping(ctx, ts.HostPort()), \"Ping from new client failed\")\n\t\t\tassert.Equal(t, 1, client.IntrospectNumConnections(), \"Client should have single connection\")\n\t\t\tassert.Equal(t, i+1, ts.Server().IntrospectNumConnections(), \"Incorrect number of server connections\")\n\t\t}\n\t})\n}\n\nfunc TestIntrospectionNotBlocked(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tsubCh := ts.Server().GetSubChannel(\"tchannel\")\n\t\tsubCh.SetHandler(HandlerFunc(func(ctx context.Context, inbound *InboundCall) {\n\t\t\tpanic(\"should not be called\")\n\t\t}))\n\n\t\t// Ensure that tchannel is also relayed\n\t\tif ts.HasRelay() {\n\t\t\tts.RelayHost().Add(\"tchannel\", ts.Server().PeerInfo().HostPort)\n\t\t}\n\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\tclient := ts.NewClient(nil)\n\t\tpeer := client.Peers().GetOrAdd(ts.HostPort())\n\n\t\t// Ensure that SetHandler doesn't block introspection.\n\t\tvar resp interface{}\n\t\terr := json.CallPeer(Wrap(ctx), peer, \"tchannel\", \"_gometa_runtime\", nil, &resp)\n\t\trequire.NoError(t, err, \"Call _gometa_runtime failed\")\n\t})\n}\n"
  },
  {
    "path": "json/call.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage json\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/uber/tchannel-go\"\n\n\t\"golang.org/x/net/context\"\n)\n\n// ErrApplication is an application error which contains the object returned from the other side.\ntype ErrApplication map[string]interface{}\n\nfunc (e ErrApplication) Error() string {\n\treturn fmt.Sprintf(\"JSON call failed: %v\", map[string]interface{}(e))\n}\n\n// Client is used to make JSON calls to other services.\ntype Client struct {\n\tch            *tchannel.Channel\n\ttargetService string\n\thostPort      string\n}\n\n// ClientOptions are options used when creating a client.\ntype ClientOptions struct {\n\tHostPort string\n}\n\n// NewClient returns a json.Client used to make outbound JSON calls.\nfunc NewClient(ch *tchannel.Channel, targetService string, opts *ClientOptions) *Client {\n\tclient := &Client{\n\t\tch:            ch,\n\t\ttargetService: targetService,\n\t}\n\tif opts != nil && opts.HostPort != \"\" {\n\t\tclient.hostPort = opts.HostPort\n\t}\n\treturn client\n}\n\nfunc makeCall(call *tchannel.OutboundCall, headers, arg3In, respHeaders, arg3Out, errorOut interface{}) (bool, string, error) {\n\tif mapHeaders, ok := headers.(map[string]string); ok {\n\t\theaders = tchannel.InjectOutboundSpan(call.Response(), mapHeaders)\n\t}\n\tif err := tchannel.NewArgWriter(call.Arg2Writer()).WriteJSON(headers); err != nil {\n\t\treturn false, \"arg2 write failed\", err\n\t}\n\tif err := tchannel.NewArgWriter(call.Arg3Writer()).WriteJSON(arg3In); err != nil {\n\t\treturn false, \"arg3 write failed\", err\n\t}\n\n\t// Call Arg2Reader before checking application error.\n\tif err := tchannel.NewArgReader(call.Response().Arg2Reader()).ReadJSON(respHeaders); err != nil {\n\t\treturn false, \"arg2 read failed\", err\n\t}\n\n\t// If this is an error response, read the response into a map and return a jsonCallErr.\n\tif call.Response().ApplicationError() {\n\t\tif err := tchannel.NewArgReader(call.Response().Arg3Reader()).ReadJSON(errorOut); err != nil {\n\t\t\treturn false, \"arg3 read error failed\", err\n\t\t}\n\t\treturn false, \"\", nil\n\t}\n\n\tif err := tchannel.NewArgReader(call.Response().Arg3Reader()).ReadJSON(arg3Out); err != nil {\n\t\treturn false, \"arg3 read failed\", err\n\t}\n\n\treturn true, \"\", nil\n}\n\nfunc (c *Client) startCall(ctx context.Context, method string, callOptions *tchannel.CallOptions) (*tchannel.OutboundCall, error) {\n\tif c.hostPort != \"\" {\n\t\treturn c.ch.BeginCall(ctx, c.hostPort, c.targetService, method, callOptions)\n\t}\n\n\treturn c.ch.GetSubChannel(c.targetService).BeginCall(ctx, method, callOptions)\n}\n\n// Call makes a JSON call, with retries.\nfunc (c *Client) Call(ctx Context, method string, arg, resp interface{}) error {\n\tvar (\n\t\theaders = ctx.Headers()\n\n\t\trespHeaders map[string]string\n\t\trespErr     ErrApplication\n\t\terrAt       string\n\t\tisOK        bool\n\t)\n\n\terr := c.ch.RunWithRetry(ctx, func(ctx context.Context, rs *tchannel.RequestState) error {\n\t\trespHeaders, respErr, isOK = nil, nil, false\n\t\terrAt = \"connect\"\n\n\t\tcall, err := c.startCall(ctx, method, &tchannel.CallOptions{\n\t\t\tFormat:       tchannel.JSON,\n\t\t\tRequestState: rs,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tisOK, errAt, err = makeCall(call, headers, arg, &respHeaders, resp, &respErr)\n\t\treturn err\n\t})\n\tif err != nil {\n\t\t// TODO: Don't lose the error type here.\n\t\treturn fmt.Errorf(\"%s: %v\", errAt, err)\n\t}\n\tif !isOK {\n\t\treturn respErr\n\t}\n\n\treturn nil\n}\n\n// TODO(prashantv): Clean up json.Call* interfaces.\nfunc wrapCall(ctx Context, call *tchannel.OutboundCall, method string, arg, resp interface{}) error {\n\tvar respHeaders map[string]string\n\tvar respErr ErrApplication\n\tisOK, errAt, err := makeCall(call, ctx.Headers(), arg, &respHeaders, resp, &respErr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%s: %v\", errAt, err)\n\t}\n\tif !isOK {\n\t\treturn respErr\n\t}\n\n\tctx.SetResponseHeaders(respHeaders)\n\treturn nil\n}\n\n// CallPeer makes a JSON call using the given peer.\nfunc CallPeer(ctx Context, peer *tchannel.Peer, serviceName, method string, arg, resp interface{}) error {\n\tcall, err := peer.BeginCall(ctx, serviceName, method, &tchannel.CallOptions{Format: tchannel.JSON})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn wrapCall(ctx, call, method, arg, resp)\n}\n\n// CallSC makes a JSON call using the given subchannel.\nfunc CallSC(ctx Context, sc *tchannel.SubChannel, method string, arg, resp interface{}) error {\n\tcall, err := sc.BeginCall(ctx, method, &tchannel.CallOptions{Format: tchannel.JSON})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn wrapCall(ctx, call, method, arg, resp)\n}\n"
  },
  {
    "path": "json/context.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage json\n\nimport (\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\n\t\"golang.org/x/net/context\"\n)\n\n// Context is a JSON Context which contains request and response headers.\ntype Context tchannel.ContextWithHeaders\n\n// NewContext returns a Context that can be used to make JSON calls.\nfunc NewContext(timeout time.Duration) (Context, context.CancelFunc) {\n\tctx, cancel := tchannel.NewContext(timeout)\n\treturn tchannel.WrapWithHeaders(ctx, nil), cancel\n}\n\n// Wrap returns a JSON Context that wraps around a Context.\nfunc Wrap(ctx context.Context) Context {\n\treturn tchannel.WrapWithHeaders(ctx, nil)\n}\n\n// WithHeaders returns a Context that can be used to make a call with request headers.\nfunc WithHeaders(ctx context.Context, headers map[string]string) Context {\n\treturn tchannel.WrapWithHeaders(ctx, headers)\n}\n"
  },
  {
    "path": "json/handler.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage json\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/uber/tchannel-go\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"golang.org/x/net/context\"\n)\n\nvar (\n\ttypeOfError   = reflect.TypeOf((*error)(nil)).Elem()\n\ttypeOfContext = reflect.TypeOf((*Context)(nil)).Elem()\n)\n\n// Handlers is the map from method names to handlers.\ntype Handlers map[string]interface{}\n\n// verifyHandler ensures that the given t is a function with the following signature:\n// func(json.Context, *ArgType)(*ResType, error)\nfunc verifyHandler(t reflect.Type) error {\n\tif t.NumIn() != 2 || t.NumOut() != 2 {\n\t\treturn fmt.Errorf(\"handler should be of format func(json.Context, *ArgType) (*ResType, error)\")\n\t}\n\n\tisStructPtr := func(t reflect.Type) bool {\n\t\treturn t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct\n\t}\n\tisMap := func(t reflect.Type) bool {\n\t\treturn t.Kind() == reflect.Map && t.Key().Kind() == reflect.String\n\t}\n\tvalidateArgRes := func(t reflect.Type, name string) error {\n\t\tif !isStructPtr(t) && !isMap(t) {\n\t\t\treturn fmt.Errorf(\"%v should be a pointer to a struct, or a map[string]interface{}\", name)\n\t\t}\n\t\treturn nil\n\t}\n\n\tif t.In(0) != typeOfContext {\n\t\treturn fmt.Errorf(\"arg0 should be of type json.Context\")\n\t}\n\tif err := validateArgRes(t.In(1), \"second argument\"); err != nil {\n\t\treturn err\n\t}\n\tif err := validateArgRes(t.Out(0), \"first return value\"); err != nil {\n\t\treturn err\n\t}\n\tif !t.Out(1).AssignableTo(typeOfError) {\n\t\treturn fmt.Errorf(\"second return value should be an error\")\n\t}\n\n\treturn nil\n}\n\ntype handler struct {\n\thandler  reflect.Value\n\targType  reflect.Type\n\tisArgMap bool\n\ttracer   func() opentracing.Tracer\n}\n\nfunc toHandler(f interface{}) (*handler, error) {\n\thV := reflect.ValueOf(f)\n\tif err := verifyHandler(hV.Type()); err != nil {\n\t\treturn nil, err\n\t}\n\targType := hV.Type().In(1)\n\treturn &handler{handler: hV, argType: argType, isArgMap: argType.Kind() == reflect.Map}, nil\n}\n\n// Register registers the specified methods specified as a map from method name to the\n// JSON handler function. The handler functions should have the following signature:\n// func(context.Context, *ArgType)(*ResType, error)\nfunc Register(registrar tchannel.Registrar, funcs Handlers, onError func(context.Context, error)) error {\n\thandlers := make(map[string]*handler)\n\n\thandler := tchannel.HandlerFunc(func(ctx context.Context, call *tchannel.InboundCall) {\n\t\th, ok := handlers[string(call.Method())]\n\t\tif !ok {\n\t\t\tonError(ctx, fmt.Errorf(\"call for unregistered method: %s\", call.Method()))\n\t\t\treturn\n\t\t}\n\n\t\tif err := h.Handle(ctx, call); err != nil {\n\t\t\tonError(ctx, err)\n\t\t}\n\t})\n\n\tfor m, f := range funcs {\n\t\th, err := toHandler(f)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%v cannot be used as a handler: %v\", m, err)\n\t\t}\n\t\th.tracer = func() opentracing.Tracer {\n\t\t\treturn tchannel.TracerFromRegistrar(registrar)\n\t\t}\n\t\thandlers[m] = h\n\t\tregistrar.Register(handler, m)\n\t}\n\n\treturn nil\n}\n\n// Handle deserializes the JSON arguments and calls the underlying handler.\nfunc (h *handler) Handle(tctx context.Context, call *tchannel.InboundCall) error {\n\tvar headers map[string]string\n\tif err := tchannel.NewArgReader(call.Arg2Reader()).ReadJSON(&headers); err != nil {\n\t\treturn fmt.Errorf(\"arg2 read failed: %v\", err)\n\t}\n\ttctx = tchannel.ExtractInboundSpan(tctx, call, headers, h.tracer())\n\tctx := WithHeaders(tctx, headers)\n\n\tvar arg3 reflect.Value\n\tvar callArg reflect.Value\n\tif h.isArgMap {\n\t\targ3 = reflect.New(h.argType)\n\t\t// New returns a pointer, but the method accepts the map directly.\n\t\tcallArg = arg3.Elem()\n\t} else {\n\t\targ3 = reflect.New(h.argType.Elem())\n\t\tcallArg = arg3\n\t}\n\tif err := tchannel.NewArgReader(call.Arg3Reader()).ReadJSON(arg3.Interface()); err != nil {\n\t\treturn fmt.Errorf(\"arg3 read failed: %v\", err)\n\t}\n\n\targs := []reflect.Value{reflect.ValueOf(ctx), callArg}\n\tresults := h.handler.Call(args)\n\n\tres := results[0].Interface()\n\terr := results[1].Interface()\n\t// If an error was returned, we create an error arg3 to respond with.\n\tif err != nil {\n\t\t// TODO(prashantv): More consistent error handling between json/raw/thrift..\n\t\tif serr, ok := err.(tchannel.SystemError); ok {\n\t\t\treturn call.Response().SendSystemError(serr)\n\t\t}\n\n\t\tcall.Response().SetApplicationError()\n\t\t// TODO(prashant): Allow client to customize the error in more ways.\n\t\tres = struct {\n\t\t\tType    string `json:\"type\"`\n\t\t\tMessage string `json:\"message\"`\n\t\t}{\n\t\t\tType:    \"error\",\n\t\t\tMessage: err.(error).Error(),\n\t\t}\n\t}\n\n\tif err := tchannel.NewArgWriter(call.Response().Arg2Writer()).WriteJSON(ctx.ResponseHeaders()); err != nil {\n\t\treturn err\n\t}\n\n\treturn tchannel.NewArgWriter(call.Response().Arg3Writer()).WriteJSON(res)\n}\n"
  },
  {
    "path": "json/json_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage json\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\n// ForwardArgs are the arguments specifying who to forward to (and the message to forward).\ntype ForwardArgs struct {\n\tHeaderVal   string\n\tService     string\n\tMethod      string\n\tNextForward *ForwardArgs\n}\n\n// Res is the final result.\ntype Res struct {\n\tResult string\n}\n\ntype testHandler struct {\n\tcalls   []string\n\tcallers []string\n\tpeer    *tchannel.Peer\n\tt       *testing.T\n}\n\nfunc (h *testHandler) forward(ctx Context, args *ForwardArgs) (*Res, error) {\n\theaderVal := ctx.Headers()[\"hdr\"]\n\tctx.SetResponseHeaders(map[string]string{\"hdr\": headerVal + \"-resp\"})\n\th.calls = append(h.calls, \"forward-\"+headerVal)\n\th.callers = append(h.callers, tchannel.CurrentCall(ctx).CallerName())\n\n\tif args.HeaderVal != \"\" {\n\t\tctx = WithHeaders(ctx, map[string]string{\"hdr\": args.HeaderVal})\n\t}\n\tres := &Res{}\n\n\tif args.Method == \"forward\" {\n\t\tif err := CallPeer(ctx, h.peer, args.Service, args.Method, args.NextForward, res); err != nil {\n\t\t\th.t.Errorf(\"forward->forward Call failed: %v\", err)\n\t\t\treturn nil, err\n\t\t}\n\t\tassert.Equal(h.t, map[string]string{\"hdr\": args.HeaderVal + \"-resp\"}, ctx.ResponseHeaders())\n\t\treturn res, nil\n\t}\n\n\tif err := CallPeer(ctx, h.peer, args.Service, args.Method, nil, res); err != nil {\n\t\th.t.Errorf(\"forward->%v Call failed: %v\", args.Method, err)\n\t\treturn nil, err\n\t}\n\n\treturn res, nil\n}\n\nfunc (h *testHandler) leaf(ctx Context, _ *struct{}) (*Res, error) {\n\theaderVal := ctx.Headers()[\"hdr\"]\n\th.calls = append(h.calls, \"leaf-\"+headerVal)\n\th.callers = append(h.callers, tchannel.CurrentCall(ctx).CallerName())\n\treturn &Res{\"leaf called!\"}, nil\n}\n\nfunc (h *testHandler) onError(ctx context.Context, err error) {\n\th.t.Errorf(\"onError(%v)\", err)\n}\n\nfunc TestForwardChain(t *testing.T) {\n\tservers := map[string]*struct {\n\t\tchannel   *tchannel.Channel\n\t\thandler   *testHandler\n\t\totherPeer string\n\t}{\n\t\t\"serv1\": {otherPeer: \"serv2\"},\n\t\t\"serv2\": {otherPeer: \"serv3\"},\n\t\t\"serv3\": {otherPeer: \"serv1\"},\n\t}\n\n\t// We want the following call graph:\n\t// serv1.forward\n\t// -> (1) serv2.forward\n\t// -> (2) serv3.forward\n\t// -> (3) serv1.forward\n\t// -> (4) serv2.forward\n\t// ....\n\t// -> (11) serv3.leaf\n\trootArg := &ForwardArgs{}\n\tcurArg := rootArg\n\tfor i := 1; i <= 10; i++ {\n\t\tservice := fmt.Sprintf(\"serv%v\", (i%3)+1)\n\n\t\tcurArg.Method = \"forward\"\n\t\tcurArg.HeaderVal = fmt.Sprint(i)\n\t\tcurArg.Service = service\n\t\tcurArg.NextForward = &ForwardArgs{}\n\n\t\tcurArg = curArg.NextForward\n\t}\n\tcurArg.Service = \"serv3\"\n\tcurArg.HeaderVal = \"11\"\n\tcurArg.Method = \"leaf\"\n\n\texpectedCalls := map[string]struct {\n\t\tcalls   []string\n\t\tcallers []string\n\t}{\n\t\t\"serv1\": {\n\t\t\tcalls:   []string{\"forward-initial\", \"forward-3\", \"forward-6\", \"forward-9\"},\n\t\t\tcallers: []string{\"serv3\", \"serv3\", \"serv3\", \"serv3\"},\n\t\t},\n\t\t\"serv2\": {\n\t\t\tcalls:   []string{\"forward-1\", \"forward-4\", \"forward-7\", \"forward-10\"},\n\t\t\tcallers: []string{\"serv1\", \"serv1\", \"serv1\", \"serv1\"},\n\t\t},\n\t\t\"serv3\": {\n\t\t\tcalls:   []string{\"forward-2\", \"forward-5\", \"forward-8\", \"leaf-11\"},\n\t\t\tcallers: []string{\"serv2\", \"serv2\", \"serv2\", \"serv2\"},\n\t\t},\n\t}\n\n\t// Use the above data to setup the test and ensure the calls are made as expected.\n\tfor name, s := range servers {\n\t\tvar err error\n\t\ts.channel, err = tchannel.NewChannel(name, nil)\n\t\trequire.NoError(t, err)\n\n\t\ts.handler = &testHandler{t: t}\n\t\trequire.NoError(t, Register(s.channel, Handlers{\n\t\t\t\"forward\": s.handler.forward,\n\t\t\t\"leaf\":    s.handler.leaf,\n\t\t}, s.handler.onError))\n\n\t\trequire.NoError(t, s.channel.ListenAndServe(\"127.0.0.1:0\"))\n\t}\n\tfor _, s := range servers {\n\t\ts.handler.peer = s.channel.Peers().Add(servers[s.otherPeer].channel.PeerInfo().HostPort)\n\t}\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\tctx = WithHeaders(ctx, map[string]string{\"hdr\": \"initial\"})\n\tassert.Nil(t, tchannel.CurrentCall(ctx))\n\n\tsc := servers[\"serv3\"].channel.GetSubChannel(\"serv1\")\n\tresp := &Res{}\n\tif assert.NoError(t, CallSC(ctx, sc, \"forward\", rootArg, resp)) {\n\t\tassert.Equal(t, \"leaf called!\", resp.Result)\n\t\tfor s, expected := range expectedCalls {\n\t\t\tassert.Equal(t, expected.calls, servers[s].handler.calls, \"wrong calls for %v\", s)\n\t\t\tassert.Equal(t, expected.callers, servers[s].handler.callers, \"wrong callers for %v\", s)\n\t\t}\n\t}\n}\n\nfunc TestHeadersForwarded(t *testing.T) {\n\tch, err := tchannel.NewChannel(\"svc\", nil)\n\trequire.NoError(t, err)\n\n\thandler := &testHandler{t: t}\n\trequire.NoError(t, Register(ch, Handlers{\n\t\t\"forward\": handler.forward,\n\t\t\"leaf\":    handler.leaf,\n\t}, handler.onError))\n\tassert.NoError(t, ch.ListenAndServe(\"127.0.0.1:0\"))\n\n\trootArg := &ForwardArgs{\n\t\tService:   \"svc\",\n\t\tMethod:    \"leaf\",\n\t\tHeaderVal: \"\",\n\t}\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\tctx = WithHeaders(ctx, map[string]string{\"hdr\": \"copy\"})\n\n\tassert.Nil(t, tchannel.CurrentCall(ctx))\n\tresp := &Res{}\n\thandler.peer = ch.Peers().Add(ch.PeerInfo().HostPort)\n\tif assert.NoError(t, CallPeer(ctx, handler.peer, \"svc\", \"forward\", rootArg, resp)) {\n\t\t// Verify that the header is copied when ctx is not changed.\n\t\tassert.Equal(t, handler.calls, []string{\"forward-copy\", \"leaf-copy\"})\n\t}\n}\n\nfunc TestEmptyRequestHeader(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tch, err := tchannel.NewChannel(\"server\", nil)\n\trequire.NoError(t, err)\n\trequire.NoError(t, ch.ListenAndServe(\"127.0.0.1:0\"))\n\n\thandler := func(ctx Context, _ *struct{}) (*struct{}, error) {\n\t\tassert.Equal(t, map[string]string(nil), ctx.Headers())\n\t\treturn nil, nil\n\t}\n\tonError := func(ctx context.Context, err error) {\n\t\tt.Errorf(\"onError: %v\", err)\n\t}\n\trequire.NoError(t, Register(ch, Handlers{\"handle\": handler}, onError))\n\n\tcall, err := ch.BeginCall(ctx, ch.PeerInfo().HostPort, \"server\", \"handle\", &tchannel.CallOptions{\n\t\tFormat: tchannel.JSON,\n\t})\n\trequire.NoError(t, err)\n\n\trequire.NoError(t, tchannel.NewArgWriter(call.Arg2Writer()).Write(nil))\n\trequire.NoError(t, tchannel.NewArgWriter(call.Arg3Writer()).WriteJSON(nil))\n\n\tresp := call.Response()\n\tvar data interface{}\n\trequire.NoError(t, tchannel.NewArgReader(resp.Arg2Reader()).ReadJSON(&data))\n\trequire.NoError(t, tchannel.NewArgReader(resp.Arg3Reader()).ReadJSON(&data))\n}\n\nfunc TestMapInputOutput(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tch, err := tchannel.NewChannel(\"server\", nil)\n\trequire.NoError(t, err)\n\trequire.NoError(t, ch.ListenAndServe(\"127.0.0.1:0\"))\n\n\thandler := func(ctx Context, args map[string]interface{}) (map[string]interface{}, error) {\n\t\treturn args, nil\n\t}\n\tonError := func(ctx context.Context, err error) {\n\t\tt.Errorf(\"onError: %v\", err)\n\t}\n\trequire.NoError(t, Register(ch, Handlers{\"handle\": handler}, onError))\n\n\tcall, err := ch.BeginCall(ctx, ch.PeerInfo().HostPort, \"server\", \"handle\", &tchannel.CallOptions{\n\t\tFormat: tchannel.JSON,\n\t})\n\trequire.NoError(t, err)\n\n\targ := map[string]interface{}{\n\t\t\"v1\": \"value1\",\n\t\t\"v2\": 2.0,\n\t\t\"v3\": map[string]interface{}{\"k\": \"v\", \"k2\": \"v2\"},\n\t}\n\trequire.NoError(t, tchannel.NewArgWriter(call.Arg2Writer()).Write(nil))\n\trequire.NoError(t, tchannel.NewArgWriter(call.Arg3Writer()).WriteJSON(arg))\n\n\tresp := call.Response()\n\tvar data interface{}\n\trequire.NoError(t, tchannel.NewArgReader(resp.Arg2Reader()).ReadJSON(&data))\n\trequire.NoError(t, tchannel.NewArgReader(resp.Arg3Reader()).ReadJSON(&data))\n\tassert.Equal(t, arg, data.(map[string]interface{}), \"result does not match arg\")\n}\n"
  },
  {
    "path": "json/retry_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage json\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestRetryJSONCall(t *testing.T) {\n\tch := testutils.NewServer(t, nil)\n\tch.Peers().Add(ch.PeerInfo().HostPort)\n\n\tcount := 0\n\thandler := func(ctx Context, req map[string]string) (map[string]string, error) {\n\t\tcount++\n\t\tif count > 4 {\n\t\t\treturn req, nil\n\t\t}\n\t\treturn nil, tchannel.ErrServerBusy\n\t}\n\tRegister(ch, Handlers{\"test\": handler}, nil)\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tclient := NewClient(ch, ch.ServiceName(), nil)\n\n\tvar res map[string]string\n\terr := client.Call(ctx, \"test\", nil, &res)\n\tassert.NoError(t, err, \"Call should succeed\")\n\tassert.Equal(t, 5, count, \"Handler should have been invoked 5 times\")\n}\n\nfunc TestRetryJSONNoConnect(t *testing.T) {\n\tch := testutils.NewClient(t, nil)\n\tch.Peers().Add(\"0.0.0.0:0\")\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tvar res map[string]interface{}\n\tclient := NewClient(ch, ch.ServiceName(), nil)\n\terr := client.Call(ctx, \"test\", nil, &res)\n\trequire.Error(t, err, \"Call should fail\")\n\tassert.True(t, strings.HasPrefix(err.Error(), \"connect: \"), \"Error does not contain expected prefix: %v\", err.Error())\n}\n"
  },
  {
    "path": "json/tracing_test.go",
    "content": "package json_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/json\"\n\n\t. \"github.com/uber/tchannel-go/testutils/testtracing\"\n\t\"golang.org/x/net/context\"\n)\n\n// JSONHandler tests tracing over JSON encoding\ntype JSONHandler struct {\n\tTraceHandler\n\tt *testing.T\n}\n\nfunc (h *JSONHandler) firstCall(ctx context.Context, req *TracingRequest) (*TracingResponse, error) {\n\tjctx := json.Wrap(ctx)\n\tresponse := new(TracingResponse)\n\tpeer := h.Ch.Peers().GetOrAdd(h.Ch.PeerInfo().HostPort)\n\tif err := json.CallPeer(jctx, peer, h.Ch.PeerInfo().ServiceName, \"call\", req, response); err != nil {\n\t\treturn nil, err\n\t}\n\treturn response, nil\n}\n\nfunc (h *JSONHandler) callJSON(ctx json.Context, req *TracingRequest) (*TracingResponse, error) {\n\treturn h.HandleCall(ctx, req,\n\t\tfunc(ctx context.Context, req *TracingRequest) (*TracingResponse, error) {\n\t\t\tjctx := ctx.(json.Context)\n\t\t\tpeer := h.Ch.Peers().GetOrAdd(h.Ch.PeerInfo().HostPort)\n\t\t\tchildResp := new(TracingResponse)\n\t\t\tif err := json.CallPeer(jctx, peer, h.Ch.PeerInfo().ServiceName, \"call\", req, childResp); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn childResp, nil\n\t\t})\n}\n\nfunc (h *JSONHandler) onError(ctx context.Context, err error) { h.t.Errorf(\"onError %v\", err) }\n\nfunc TestJSONTracingPropagation(t *testing.T) {\n\tsuite := &PropagationTestSuite{\n\t\tEncoding: EncodingInfo{Format: tchannel.JSON, HeadersSupported: true},\n\t\tRegister: func(t *testing.T, ch *tchannel.Channel) TracingCall {\n\t\t\thandler := &JSONHandler{TraceHandler: TraceHandler{Ch: ch}, t: t}\n\t\t\tjson.Register(ch, json.Handlers{\"call\": handler.callJSON}, handler.onError)\n\t\t\treturn handler.firstCall\n\t\t},\n\t\tTestCases: map[TracerType][]PropagationTestCase{\n\t\t\tNoop: {\n\t\t\t\t{ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: \"\", ExpectedSpanCount: 0},\n\t\t\t\t{ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: \"\", ExpectedSpanCount: 0},\n\t\t\t},\n\t\t\tMock: {\n\t\t\t\t{ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 0},\n\t\t\t\t{ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 6},\n\t\t\t},\n\t\t\tJaeger: {\n\t\t\t\t{ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 0},\n\t\t\t\t{ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 6},\n\t\t\t},\n\t\t},\n\t}\n\tsuite.Run(t)\n}\n"
  },
  {
    "path": "largereq_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestLargeRequest(t *testing.T) {\n\tCheckStress(t)\n\n\tconst (\n\t\tKB = 1024\n\t\tMB = 1024 * KB\n\t\tGB = 1024 * MB\n\n\t\tmaxRequestSize = 1 * GB\n\t)\n\n\tWithVerifiedServer(t, nil, func(serverCh *Channel, hostPort string) {\n\t\tserverCh.Register(raw.Wrap(newTestHandler(t)), \"echo\")\n\n\t\tfor reqSize := 2; reqSize <= maxRequestSize; reqSize *= 2 {\n\t\t\tlog.Printf(\"reqSize = %v\", reqSize)\n\t\t\targ3 := testutils.RandBytes(reqSize)\n\t\t\targ2 := testutils.RandBytes(reqSize / 2)\n\n\t\t\tclientCh := testutils.NewClient(t, nil)\n\t\t\tctx, cancel := NewContext(time.Second * 30)\n\t\t\trArg2, rArg3, _, err := raw.Call(ctx, clientCh, hostPort, serverCh.PeerInfo().ServiceName, \"echo\", arg2, arg3)\n\t\t\trequire.NoError(t, err, \"Call failed\")\n\n\t\t\tif !bytes.Equal(arg2, rArg2) {\n\t\t\t\tt.Errorf(\"echo arg2 mismatch\")\n\t\t\t}\n\t\t\tif !bytes.Equal(arg3, rArg3) {\n\t\t\t\tt.Errorf(\"echo arg3 mismatch\")\n\t\t\t}\n\t\t\tcancel()\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "localip.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"errors\"\n\t\"net\"\n)\n\n// scoreAddr scores how likely the given addr is to be a remote address and returns the\n// IP to use when listening. Any address which receives a negative score should not be used.\n// Scores are calculated as:\n// -1 for any unknown IP addreseses.\n// +300 for IPv4 addresses\n// +100 for non-local addresses, extra +100 for \"up\" interaces.\nfunc scoreAddr(iface net.Interface, addr net.Addr) (int, net.IP) {\n\tvar ip net.IP\n\tif netAddr, ok := addr.(*net.IPNet); ok {\n\t\tip = netAddr.IP\n\t} else if netIP, ok := addr.(*net.IPAddr); ok {\n\t\tip = netIP.IP\n\t} else {\n\t\treturn -1, nil\n\t}\n\n\tvar score int\n\tif ip.To4() != nil {\n\t\tscore += 300\n\t}\n\tif iface.Flags&net.FlagLoopback == 0 && !ip.IsLoopback() {\n\t\tscore += 100\n\t\tif iface.Flags&net.FlagUp != 0 {\n\t\t\tscore += 100\n\t\t}\n\t}\n\tif isLocalMacAddr(iface.HardwareAddr) {\n\t\tscore -= 50\n\t}\n\treturn score, ip\n}\n\nfunc listenIP(interfaces []net.Interface) (net.IP, error) {\n\tbestScore := -1\n\tvar bestIP net.IP\n\t// Select the highest scoring IP as the best IP.\n\tfor _, iface := range interfaces {\n\t\taddrs, err := iface.Addrs()\n\t\tif err != nil {\n\t\t\t// Skip this interface if there is an error.\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, addr := range addrs {\n\t\t\tscore, ip := scoreAddr(iface, addr)\n\t\t\tif score > bestScore {\n\t\t\t\tbestScore = score\n\t\t\t\tbestIP = ip\n\t\t\t}\n\t\t}\n\t}\n\n\tif bestScore == -1 {\n\t\treturn nil, errors.New(\"no addresses to listen on\")\n\t}\n\n\treturn bestIP, nil\n}\n\n// ListenIP returns the IP to bind to in Listen. It tries to find an IP that can be used\n// by other machines to reach this machine.\nfunc ListenIP() (net.IP, error) {\n\tinterfaces, err := net.Interfaces()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn listenIP(interfaces)\n}\n\nfunc mustParseMAC(s string) net.HardwareAddr {\n\taddr, err := net.ParseMAC(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn addr\n}\n\n// If the first octet's second least-significant-bit is set, then it's local.\n// https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local\nfunc isLocalMacAddr(addr net.HardwareAddr) bool {\n\tif len(addr) == 0 {\n\t\treturn false\n\t}\n\treturn addr[0]&2 == 2\n}\n"
  },
  {
    "path": "localip_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestScoreAddr(t *testing.T) {\n\tipv4 := net.ParseIP(\"10.0.1.2\")\n\tipv6 := net.ParseIP(\"2001:db8:a0b:12f0::1\")\n\n\ttests := []struct {\n\t\tmsg    string\n\t\tiface  net.Interface\n\t\taddr   net.Addr\n\t\twant   int\n\t\twantIP net.IP\n\t}{\n\t\t{\n\t\t\tmsg:    \"non-local up ipv4 IPNet address\",\n\t\t\tiface:  net.Interface{Flags: net.FlagUp},\n\t\t\taddr:   &net.IPNet{IP: ipv4},\n\t\t\twant:   500,\n\t\t\twantIP: ipv4,\n\t\t},\n\t\t{\n\t\t\tmsg:    \"non-local up ipv4 IPAddr address\",\n\t\t\tiface:  net.Interface{Flags: net.FlagUp},\n\t\t\taddr:   &net.IPAddr{IP: ipv4},\n\t\t\twant:   500,\n\t\t\twantIP: ipv4,\n\t\t},\n\t\t{\n\t\t\tmsg: \"non-local up ipv4 IPAddr address, docker interface\",\n\t\t\tiface: net.Interface{\n\t\t\t\tFlags:        net.FlagUp,\n\t\t\t\tHardwareAddr: mustParseMAC(\"02:42:ac:11:56:af\"),\n\t\t\t},\n\t\t\taddr:   &net.IPNet{IP: ipv4},\n\t\t\twant:   450,\n\t\t\twantIP: ipv4,\n\t\t},\n\t\t{\n\t\t\tmsg: \"non-local up ipv4 address, local MAC address\",\n\t\t\tiface: net.Interface{\n\t\t\t\tFlags:        net.FlagUp,\n\t\t\t\tHardwareAddr: mustParseMAC(\"02:42:9c:52:fc:86\"),\n\t\t\t},\n\t\t\taddr:   &net.IPNet{IP: ipv4},\n\t\t\twant:   450,\n\t\t\twantIP: ipv4,\n\t\t},\n\t\t{\n\t\t\tmsg:    \"non-local down ipv4 address\",\n\t\t\tiface:  net.Interface{},\n\t\t\taddr:   &net.IPNet{IP: ipv4},\n\t\t\twant:   400,\n\t\t\twantIP: ipv4,\n\t\t},\n\t\t{\n\t\t\tmsg:    \"non-local down ipv6 address\",\n\t\t\tiface:  net.Interface{},\n\t\t\taddr:   &net.IPAddr{IP: ipv6},\n\t\t\twant:   100,\n\t\t\twantIP: ipv6,\n\t\t},\n\t\t{\n\t\t\tmsg:   \"unknown address type\",\n\t\t\tiface: net.Interface{},\n\t\t\taddr:  &net.UnixAddr{Name: \"/tmp/socket\"},\n\t\t\twant:  -1,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tgotScore, gotIP := scoreAddr(tt.iface, tt.addr)\n\t\tassert.Equal(t, tt.want, gotScore, tt.msg)\n\t\tassert.Equal(t, tt.wantIP, gotIP, tt.msg)\n\t}\n}\n"
  },
  {
    "path": "logger.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n)\n\nimport (\n\t\"os\"\n)\n\n// Logger provides an abstract interface for logging from TChannel.\n// Applications can provide their own implementation of this interface to adapt\n// TChannel logging to whatever logging library they prefer (stdlib log,\n// logrus, go-logging, etc).  The SimpleLogger adapts to the standard go log\n// package.\ntype Logger interface {\n\t// Enabled returns whether the given level is enabled.\n\tEnabled(level LogLevel) bool\n\n\t// Fatal logs a message, then exits with os.Exit(1).\n\tFatal(msg string)\n\n\t// Error logs a message at error priority.\n\tError(msg string)\n\n\t// Warn logs a message at warning priority.\n\tWarn(msg string)\n\n\t// Infof logs a message at info priority.\n\tInfof(msg string, args ...interface{})\n\n\t// Info logs a message at info priority.\n\tInfo(msg string)\n\n\t// Debugf logs a message at debug priority.\n\tDebugf(msg string, args ...interface{})\n\n\t// Debug logs a message at debug priority.\n\tDebug(msg string)\n\n\t// Fields returns the fields that this logger contains.\n\tFields() LogFields\n\n\t// WithFields returns a logger with the current logger's fields and fields.\n\tWithFields(fields ...LogField) Logger\n}\n\n// LogField is a single field of additional information passed to the logger.\ntype LogField struct {\n\tKey   string\n\tValue interface{}\n}\n\n// ErrField wraps an error string as a LogField named \"error\"\nfunc ErrField(err error) LogField {\n\treturn LogField{\"error\", err.Error()}\n}\n\n// LogFields is a list of LogFields used to pass additional information to the logger.\ntype LogFields []LogField\n\n// NullLogger is a logger that emits nowhere\nvar NullLogger Logger = nullLogger{}\n\ntype nullLogger struct {\n\tfields LogFields\n}\n\nfunc (nullLogger) Enabled(_ LogLevel) bool                { return false }\nfunc (nullLogger) Fatal(msg string)                       { os.Exit(1) }\nfunc (nullLogger) Error(msg string)                       {}\nfunc (nullLogger) Warn(msg string)                        {}\nfunc (nullLogger) Infof(msg string, args ...interface{})  {}\nfunc (nullLogger) Info(msg string)                        {}\nfunc (nullLogger) Debugf(msg string, args ...interface{}) {}\nfunc (nullLogger) Debug(msg string)                       {}\nfunc (l nullLogger) Fields() LogFields                    { return l.fields }\n\nfunc (l nullLogger) WithFields(fields ...LogField) Logger {\n\tnewFields := make([]LogField, len(l.Fields())+len(fields))\n\tn := copy(newFields, l.Fields())\n\tcopy(newFields[n:], fields)\n\treturn nullLogger{newFields}\n}\n\n// SimpleLogger prints logging information to standard out.\nvar SimpleLogger = NewLogger(os.Stdout)\n\ntype writerLogger struct {\n\twriter io.Writer\n\tfields LogFields\n}\n\nconst writerLoggerStamp = \"15:04:05.000000\"\n\n// NewLogger returns a Logger that writes to the given writer.\nfunc NewLogger(writer io.Writer, fields ...LogField) Logger {\n\treturn &writerLogger{writer, fields}\n}\n\nfunc (l writerLogger) Fatal(msg string) {\n\tl.printfn(\"F\", msg)\n\tos.Exit(1)\n}\n\nfunc (l writerLogger) Enabled(_ LogLevel) bool                { return true }\nfunc (l writerLogger) Error(msg string)                       { l.printfn(\"E\", msg) }\nfunc (l writerLogger) Warn(msg string)                        { l.printfn(\"W\", msg) }\nfunc (l writerLogger) Infof(msg string, args ...interface{})  { l.printfn(\"I\", msg, args...) }\nfunc (l writerLogger) Info(msg string)                        { l.printfn(\"I\", msg) }\nfunc (l writerLogger) Debugf(msg string, args ...interface{}) { l.printfn(\"D\", msg, args...) }\nfunc (l writerLogger) Debug(msg string)                       { l.printfn(\"D\", msg) }\nfunc (l writerLogger) printfn(prefix, msg string, args ...interface{}) {\n\tfmt.Fprintf(l.writer, \"%s [%s] %s tags: %v\\n\", time.Now().Format(writerLoggerStamp), prefix, fmt.Sprintf(msg, args...), l.fields)\n}\n\nfunc (l writerLogger) Fields() LogFields {\n\treturn l.fields\n}\n\nfunc (l writerLogger) WithFields(newFields ...LogField) Logger {\n\texistingFields := l.Fields()\n\tfields := make(LogFields, 0, len(existingFields)+1)\n\tfields = append(fields, existingFields...)\n\tfields = append(fields, newFields...)\n\treturn writerLogger{l.writer, fields}\n}\n\n// LogLevel is the level of logging used by LevelLogger.\ntype LogLevel int\n\n// The minimum level that will be logged. e.g. LogLevelError only logs errors and fatals.\nconst (\n\tLogLevelAll LogLevel = iota\n\tLogLevelDebug\n\tLogLevelInfo\n\tLogLevelWarn\n\tLogLevelError\n\tLogLevelFatal\n)\n\ntype levelLogger struct {\n\tlogger Logger\n\tlevel  LogLevel\n}\n\n// NewLevelLogger returns a logger that only logs messages with a minimum of level.\nfunc NewLevelLogger(logger Logger, level LogLevel) Logger {\n\treturn levelLogger{logger, level}\n}\n\nfunc (l levelLogger) Enabled(level LogLevel) bool {\n\treturn l.level <= level\n}\n\nfunc (l levelLogger) Fatal(msg string) {\n\tif l.level <= LogLevelFatal {\n\t\tl.logger.Fatal(msg)\n\t}\n}\n\nfunc (l levelLogger) Error(msg string) {\n\tif l.level <= LogLevelError {\n\t\tl.logger.Error(msg)\n\t}\n}\n\nfunc (l levelLogger) Warn(msg string) {\n\tif l.level <= LogLevelWarn {\n\t\tl.logger.Warn(msg)\n\t}\n}\n\nfunc (l levelLogger) Infof(msg string, args ...interface{}) {\n\tif l.level <= LogLevelInfo {\n\t\tl.logger.Infof(msg, args...)\n\t}\n}\n\nfunc (l levelLogger) Info(msg string) {\n\tif l.level <= LogLevelInfo {\n\t\tl.logger.Info(msg)\n\t}\n}\n\nfunc (l levelLogger) Debugf(msg string, args ...interface{}) {\n\tif l.level <= LogLevelDebug {\n\t\tl.logger.Debugf(msg, args...)\n\t}\n}\n\nfunc (l levelLogger) Debug(msg string) {\n\tif l.level <= LogLevelDebug {\n\t\tl.logger.Debug(msg)\n\t}\n}\n\nfunc (l levelLogger) Fields() LogFields {\n\treturn l.logger.Fields()\n}\n\nfunc (l levelLogger) WithFields(fields ...LogField) Logger {\n\treturn levelLogger{\n\t\tlogger: l.logger.WithFields(fields...),\n\t\tlevel:  l.level,\n\t}\n}\n"
  },
  {
    "path": "logger_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"testing\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc field(k string, v interface{}) LogField {\n\treturn LogField{Key: k, Value: v}\n}\n\nfunc TestErrField(t *testing.T) {\n\tassert.Equal(t, field(\"error\", \"foo\"), ErrField(errors.New(\"foo\")))\n}\n\nfunc TestWriterLogger(t *testing.T) {\n\tvar buf bytes.Buffer\n\tvar bufLogger = NewLogger(&buf)\n\n\tdebugf := func(logger Logger, msg string, args ...interface{}) { logger.Debugf(msg, args...) }\n\tinfof := func(logger Logger, msg string, args ...interface{}) { logger.Infof(msg, args...) }\n\n\tlevels := []struct {\n\t\tlevelFunc   func(logger Logger, msg string, args ...interface{})\n\t\tlevelPrefix string\n\t}{\n\t\t{debugf, \"D\"},\n\t\t{infof, \"I\"},\n\t}\n\n\tfor _, level := range levels {\n\t\ttagLogger1 := bufLogger.WithFields(field(\"key1\", \"value1\"))\n\t\ttagLogger2 := bufLogger.WithFields(field(\"key2\", \"value2\"), field(\"key3\", \"value3\"))\n\n\t\tverifyMsgAndPrefix := func(logger Logger) {\n\t\t\tbuf.Reset()\n\t\t\tlevel.levelFunc(logger, \"mes%v\", \"sage\")\n\n\t\t\tout := buf.String()\n\t\t\tassert.Contains(t, out, \"message\")\n\t\t\tassert.Contains(t, out, \"[\"+level.levelPrefix+\"]\")\n\t\t}\n\n\t\tverifyMsgAndPrefix(bufLogger)\n\n\t\tverifyMsgAndPrefix(tagLogger1)\n\t\tassert.Contains(t, buf.String(), \"{key1 value1}\")\n\t\tassert.NotContains(t, buf.String(), \"{key2 value2}\")\n\t\tassert.NotContains(t, buf.String(), \"{key3 value3}\")\n\n\t\tverifyMsgAndPrefix(tagLogger2)\n\t\tassert.Contains(t, buf.String(), \"{key2 value2}\")\n\t\tassert.Contains(t, buf.String(), \"{key3 value3}\")\n\t\tassert.NotContains(t, buf.String(), \"{key1 value1}\")\n\t}\n}\n\nfunc TestWriterLoggerNoSubstitution(t *testing.T) {\n\tvar buf bytes.Buffer\n\tvar bufLogger = NewLogger(&buf)\n\n\tlogDebug := func(logger Logger, msg string) { logger.Debug(msg) }\n\tlogInfo := func(logger Logger, msg string) { logger.Info(msg) }\n\tlogWarn := func(logger Logger, msg string) { logger.Warn(msg) }\n\tlogError := func(logger Logger, msg string) { logger.Error(msg) }\n\n\tlevels := []struct {\n\t\tlevelFunc   func(logger Logger, msg string)\n\t\tlevelPrefix string\n\t}{\n\t\t{logDebug, \"D\"},\n\t\t{logInfo, \"I\"},\n\t\t{logWarn, \"W\"},\n\t\t{logError, \"E\"},\n\t}\n\n\tfor _, level := range levels {\n\t\ttagLogger1 := bufLogger.WithFields(field(\"key1\", \"value1\"))\n\t\ttagLogger2 := bufLogger.WithFields(field(\"key2\", \"value2\"), field(\"key3\", \"value3\"))\n\n\t\tverifyMsgAndPrefix := func(logger Logger) {\n\t\t\tbuf.Reset()\n\t\t\tlevel.levelFunc(logger, \"test-msg\")\n\n\t\t\tout := buf.String()\n\t\t\tassert.Contains(t, out, \"test-msg\")\n\t\t\tassert.Contains(t, out, \"[\"+level.levelPrefix+\"]\")\n\t\t}\n\n\t\tverifyMsgAndPrefix(bufLogger)\n\n\t\tverifyMsgAndPrefix(tagLogger1)\n\t\tassert.Contains(t, buf.String(), \"{key1 value1}\")\n\t\tassert.NotContains(t, buf.String(), \"{key2 value2}\")\n\t\tassert.NotContains(t, buf.String(), \"{key3 value3}\")\n\n\t\tverifyMsgAndPrefix(tagLogger2)\n\t\tassert.Contains(t, buf.String(), \"{key2 value2}\")\n\t\tassert.Contains(t, buf.String(), \"{key3 value3}\")\n\t\tassert.NotContains(t, buf.String(), \"{key1 value1}\")\n\t}\n}\n\nfunc TestLevelLogger(t *testing.T) {\n\tvar buf bytes.Buffer\n\tvar bufLogger = NewLogger(&buf)\n\n\texpectedLines := map[LogLevel]int{\n\t\tLogLevelAll:   6,\n\t\tLogLevelDebug: 6,\n\t\tLogLevelInfo:  4,\n\t\tLogLevelWarn:  2,\n\t\tLogLevelError: 1,\n\t\tLogLevelFatal: 0,\n\t}\n\tfor level := LogLevelFatal; level >= LogLevelAll; level-- {\n\t\tbuf.Reset()\n\t\tlevelLogger := NewLevelLogger(bufLogger, level)\n\n\t\tfor l := LogLevel(0); l <= LogLevelFatal; l++ {\n\t\t\tassert.Equal(t, level <= l, levelLogger.Enabled(l), \"levelLogger.Enabled(%v) at %v\", l, level)\n\t\t}\n\n\t\tlevelLogger.Debug(\"debug\")\n\t\tlevelLogger.Debugf(\"debu%v\", \"g\")\n\t\tlevelLogger.Info(\"info\")\n\t\tlevelLogger.Infof(\"inf%v\", \"o\")\n\t\tlevelLogger.Warn(\"warn\")\n\t\tlevelLogger.Error(\"error\")\n\n\t\tassert.Equal(t, expectedLines[level], bytes.Count(buf.Bytes(), []byte{'\\n'}))\n\t}\n}\n"
  },
  {
    "path": "messages.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\n// messageType defines a type of message\ntype messageType byte\n\nconst (\n\tmessageTypeInitReq         messageType = 0x01\n\tmessageTypeInitRes         messageType = 0x02\n\tmessageTypeCallReq         messageType = 0x03\n\tmessageTypeCallRes         messageType = 0x04\n\tmessageTypeCallReqContinue messageType = 0x13\n\tmessageTypeCallResContinue messageType = 0x14\n\tmessageTypeCancel          messageType = 0xC0\n\tmessageTypePingReq         messageType = 0xd0\n\tmessageTypePingRes         messageType = 0xd1\n\tmessageTypeError           messageType = 0xFF\n)\n\n//go:generate stringer -type=messageType\n\n// message is the base interface for messages.  Has an id and type, and knows\n// how to read and write onto a binary stream\ntype message interface {\n\t// ID returns the id of the message\n\tID() uint32\n\n\t// messageType returns the type of the message\n\tmessageType() messageType\n\n\t// read reads the message from a binary stream\n\tread(r *typed.ReadBuffer) error\n\n\t// write writes the message to a binary stream\n\twrite(w *typed.WriteBuffer) error\n}\n\ntype noBodyMsg struct{}\n\nfunc (noBodyMsg) read(r *typed.ReadBuffer) error   { return nil }\nfunc (noBodyMsg) write(w *typed.WriteBuffer) error { return nil }\n\n// initParams are parameters to an initReq/InitRes\ntype initParams map[string]string\n\nconst (\n\t// InitParamHostPort contains the host and port of the peer process\n\tInitParamHostPort = \"host_port\"\n\t// InitParamProcessName contains the name of the peer process\n\tInitParamProcessName = \"process_name\"\n\t// InitParamTChannelLanguage contains the library language.\n\tInitParamTChannelLanguage = \"tchannel_language\"\n\t// InitParamTChannelLanguageVersion contains the language build/runtime version.\n\tInitParamTChannelLanguageVersion = \"tchannel_language_version\"\n\t// InitParamTChannelVersion contains the library version.\n\tInitParamTChannelVersion = \"tchannel_version\"\n)\n\n// initMessage is the base for messages in the initialization handshake\ntype initMessage struct {\n\tid         uint32\n\tVersion    uint16\n\tinitParams initParams\n}\n\nfunc (m *initMessage) read(r *typed.ReadBuffer) error {\n\tm.Version = r.ReadUint16()\n\n\tm.initParams = initParams{}\n\tnp := r.ReadUint16()\n\tfor i := 0; i < int(np); i++ {\n\t\tk := r.ReadLen16String()\n\t\tv := r.ReadLen16String()\n\t\tm.initParams[k] = v\n\t}\n\n\treturn r.Err()\n}\n\nfunc (m *initMessage) write(w *typed.WriteBuffer) error {\n\tw.WriteUint16(m.Version)\n\tw.WriteUint16(uint16(len(m.initParams)))\n\n\tfor k, v := range m.initParams {\n\t\tw.WriteLen16String(k)\n\t\tw.WriteLen16String(v)\n\t}\n\n\treturn w.Err()\n}\n\nfunc (m *initMessage) ID() uint32 {\n\treturn m.id\n}\n\n// An initReq contains context information sent from an initiating peer\ntype initReq struct {\n\tinitMessage\n}\n\nfunc (m *initReq) messageType() messageType { return messageTypeInitReq }\n\n// An initRes contains context information returned to an initiating peer\ntype initRes struct {\n\tinitMessage\n}\n\nfunc (m *initRes) messageType() messageType { return messageTypeInitRes }\n\n// TransportHeaderName is a type for transport header names.\ntype TransportHeaderName string\n\nfunc (cn TransportHeaderName) String() string { return string(cn) }\n\n// Known transport header keys for call requests.\n// Note: transport header names must be <= 16 bytes:\n// https://tchannel.readthedocs.io/en/latest/protocol/#transport-headers\nconst (\n\t// ArgScheme header specifies the format of the args.\n\tArgScheme TransportHeaderName = \"as\"\n\n\t// CallerName header specifies the name of the service making the call.\n\tCallerName TransportHeaderName = \"cn\"\n\n\t// ClaimAtFinish header value is host:port specifying the instance to send a claim message\n\t// to when response is being sent.\n\tClaimAtFinish TransportHeaderName = \"caf\"\n\n\t// ClaimAtStart header value is host:port specifying another instance to send a claim message\n\t// to when work is started.\n\tClaimAtStart TransportHeaderName = \"cas\"\n\n\t// FailureDomain header describes a group of related requests to the same service that are\n\t// likely to fail in the same way if they were to fail.\n\tFailureDomain TransportHeaderName = \"fd\"\n\n\t// ShardKey header value is used by ringpop to deliver calls to a specific tchannel instance.\n\tShardKey TransportHeaderName = \"sk\"\n\n\t// RetryFlags header specifies whether retry policies.\n\tRetryFlags TransportHeaderName = \"re\"\n\n\t// SpeculativeExecution header specifies the number of nodes on which to run the request.\n\tSpeculativeExecution TransportHeaderName = \"se\"\n\n\t// RoutingDelegate header identifies an intermediate service which knows\n\t// how to route the request to the intended recipient.\n\tRoutingDelegate TransportHeaderName = \"rd\"\n\n\t// RoutingKey header identifies a traffic group containing instances of the\n\t// requested service. A relay may use the routing key over the service if\n\t// it knows about traffic groups.\n\tRoutingKey TransportHeaderName = \"rk\"\n)\n\n// transportHeaders are passed as part of a CallReq/CallRes\ntype transportHeaders map[TransportHeaderName]string\n\nfunc (ch transportHeaders) read(r *typed.ReadBuffer) {\n\tnh := r.ReadSingleByte()\n\tfor i := 0; i < int(nh); i++ {\n\t\tk := r.ReadLen8String()\n\t\tv := r.ReadLen8String()\n\t\tch[TransportHeaderName(k)] = v\n\t}\n}\n\nfunc (ch transportHeaders) write(w *typed.WriteBuffer) {\n\tw.WriteSingleByte(byte(len(ch)))\n\n\tfor k, v := range ch {\n\t\tw.WriteLen8String(k.String())\n\t\tw.WriteLen8String(v)\n\t}\n}\n\n// A callReq for service\ntype callReq struct {\n\tid         uint32\n\tTimeToLive time.Duration\n\tTracing    Span\n\tHeaders    transportHeaders\n\tService    string\n}\n\nfunc (m *callReq) ID() uint32               { return m.id }\nfunc (m *callReq) messageType() messageType { return messageTypeCallReq }\nfunc (m *callReq) read(r *typed.ReadBuffer) error {\n\tm.TimeToLive = time.Duration(r.ReadUint32()) * time.Millisecond\n\tm.Tracing.read(r)\n\tm.Service = r.ReadLen8String()\n\tm.Headers = transportHeaders{}\n\tm.Headers.read(r)\n\treturn r.Err()\n}\n\nfunc (m *callReq) write(w *typed.WriteBuffer) error {\n\tw.WriteUint32(uint32(m.TimeToLive / time.Millisecond))\n\tm.Tracing.write(w)\n\tw.WriteLen8String(m.Service)\n\tm.Headers.write(w)\n\treturn w.Err()\n}\n\n// A callReqContinue is continuation of a previous callReq\ntype callReqContinue struct {\n\tnoBodyMsg\n\tid uint32\n}\n\nfunc (c *callReqContinue) ID() uint32               { return c.id }\nfunc (c *callReqContinue) messageType() messageType { return messageTypeCallReqContinue }\n\n// ResponseCode to a CallReq\ntype ResponseCode byte\n\nconst (\n\tresponseOK               ResponseCode = 0x00\n\tresponseApplicationError ResponseCode = 0x01\n)\n\n// callRes is a response to a CallReq\ntype callRes struct {\n\tid           uint32\n\tResponseCode ResponseCode\n\tTracing      Span\n\tHeaders      transportHeaders\n}\n\nfunc (m *callRes) ID() uint32               { return m.id }\nfunc (m *callRes) messageType() messageType { return messageTypeCallRes }\n\nfunc (m *callRes) read(r *typed.ReadBuffer) error {\n\tm.ResponseCode = ResponseCode(r.ReadSingleByte())\n\tm.Tracing.read(r)\n\tm.Headers = transportHeaders{}\n\tm.Headers.read(r)\n\treturn r.Err()\n}\n\nfunc (m *callRes) write(w *typed.WriteBuffer) error {\n\tw.WriteSingleByte(byte(m.ResponseCode))\n\tm.Tracing.write(w)\n\tm.Headers.write(w)\n\treturn w.Err()\n}\n\n// callResContinue is a continuation of a previous CallRes\ntype callResContinue struct {\n\tid uint32\n}\n\nfunc (c *callResContinue) ID() uint32                       { return c.id }\nfunc (c *callResContinue) messageType() messageType         { return messageTypeCallResContinue }\nfunc (c *callResContinue) read(r *typed.ReadBuffer) error   { return nil }\nfunc (c *callResContinue) write(w *typed.WriteBuffer) error { return nil }\n\n// An errorMessage is a system-level error response to a request or a protocol level error\ntype errorMessage struct {\n\tid      uint32\n\terrCode SystemErrCode\n\ttracing Span\n\tmessage string\n}\n\nfunc (m *errorMessage) ID() uint32               { return m.id }\nfunc (m *errorMessage) messageType() messageType { return messageTypeError }\nfunc (m *errorMessage) read(r *typed.ReadBuffer) error {\n\tm.errCode = SystemErrCode(r.ReadSingleByte())\n\tm.tracing.read(r)\n\tm.message = r.ReadLen16String()\n\treturn r.Err()\n}\n\nfunc (m *errorMessage) write(w *typed.WriteBuffer) error {\n\tw.WriteSingleByte(byte(m.errCode))\n\tm.tracing.write(w)\n\tw.WriteLen16String(m.message)\n\treturn w.Err()\n}\n\nfunc (m errorMessage) AsSystemError() error {\n\t// TODO(mmihic): Might be nice to return one of the well defined error types\n\treturn NewSystemError(m.errCode, m.message)\n}\n\n// Error returns the error message from the converted\nfunc (m errorMessage) Error() string {\n\treturn m.AsSystemError().Error()\n}\n\ntype cancelMessage struct {\n\tid      uint32\n\tttl     uint32 // unused by tchannel-go, but part of the protocol.\n\ttracing Span\n\tmessage string\n}\n\nfunc (m *cancelMessage) ID() uint32               { return m.id }\nfunc (m *cancelMessage) messageType() messageType { return messageTypeCancel }\nfunc (m *cancelMessage) read(r *typed.ReadBuffer) error {\n\tm.ttl = r.ReadUint32()\n\tm.tracing.read(r)\n\tm.message = r.ReadLen16String()\n\treturn r.Err()\n}\n\nfunc (m *cancelMessage) write(w *typed.WriteBuffer) error {\n\tw.WriteUint32(m.ttl)\n\tm.tracing.write(w)\n\tw.WriteLen16String(m.message)\n\treturn w.Err()\n}\n\nfunc (m *cancelMessage) AsSystemError() error {\n\treturn NewSystemError(ErrCodeCancelled, m.message)\n}\n\ntype pingReq struct {\n\tnoBodyMsg\n\tid uint32\n}\n\nfunc (c *pingReq) ID() uint32               { return c.id }\nfunc (c *pingReq) messageType() messageType { return messageTypePingReq }\n\n// pingRes is a ping response to a protocol level ping request.\ntype pingRes struct {\n\tnoBodyMsg\n\tid uint32\n}\n\nfunc (c *pingRes) ID() uint32               { return c.id }\nfunc (c *pingRes) messageType() messageType { return messageTypePingRes }\n\nfunc callReqSpan(f *Frame) Span {\n\trdr := typed.NewReadBuffer(f.Payload[_spanIndex : _spanIndex+_spanLength])\n\tvar s Span\n\ts.read(rdr)\n\treturn s\n}\n"
  },
  {
    "path": "messages_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestInitReq(t *testing.T) {\n\treq := initReq{\n\t\tinitMessage{\n\t\t\tid:      0xDEADBEEF,\n\t\t\tVersion: 0x02,\n\t\t\tinitParams: initParams{\n\t\t\t\t\"lang\": \"en_US\",\n\t\t\t\t\"tz\":   \"GMT\",\n\t\t\t},\n\t\t},\n\t}\n\n\tassert.Equal(t, uint32(0xDEADBEEF), req.ID(), \"ids do not match\")\n\tassert.Equal(t, messageTypeInitReq, req.messageType(), \"types do not match\")\n\tassertRoundTrip(t, &req, &initReq{initMessage{id: 0xDEADBEEF}})\n}\n\nfunc TestInitRes(t *testing.T) {\n\tres := initRes{\n\t\tinitMessage{\n\t\t\tid:      0xDEADBEEF,\n\t\t\tVersion: 0x04,\n\t\t\tinitParams: initParams{\n\t\t\t\t\"lang\": \"en_US\",\n\t\t\t\t\"tz\":   \"GMT\",\n\t\t\t},\n\t\t},\n\t}\n\n\tassert.Equal(t, uint32(0xDEADBEEF), res.ID(), \"ids do not match\")\n\tassert.Equal(t, messageTypeInitRes, res.messageType(), \"types do not match\")\n\tassertRoundTrip(t, &res, &initRes{initMessage{id: 0xDEADBEEF}})\n}\n\nfunc TestCallReq(t *testing.T) {\n\tr := callReq{\n\t\tid:         0xDEADBEEF,\n\t\tTimeToLive: time.Second * 45,\n\t\tTracing: Span{\n\t\t\ttraceID:  294390430934,\n\t\t\tparentID: 398348934,\n\t\t\tspanID:   12762782,\n\t\t\tflags:    0x01,\n\t\t},\n\t\tHeaders: transportHeaders{\n\t\t\t\"r\": \"c\",\n\t\t\t\"f\": \"d\",\n\t\t},\n\t\tService: \"udr\",\n\t}\n\n\tassert.Equal(t, uint32(0xDEADBEEF), r.ID())\n\tassert.Equal(t, messageTypeCallReq, r.messageType())\n\tassertRoundTrip(t, &r, &callReq{id: 0xDEADBEEF})\n}\n\nfunc TestCallReqContinue(t *testing.T) {\n\tr := callReqContinue{\n\t\tid: 0xDEADBEEF,\n\t}\n\n\tassert.Equal(t, uint32(0xDEADBEEF), r.ID())\n\tassert.Equal(t, messageTypeCallReqContinue, r.messageType())\n\tassertRoundTrip(t, &r, &callReqContinue{id: 0xDEADBEEF})\n}\n\nfunc TestCallRes(t *testing.T) {\n\tr := callRes{\n\t\tid:           0xDEADBEEF,\n\t\tResponseCode: responseApplicationError,\n\t\tHeaders: transportHeaders{\n\t\t\t\"r\": \"c\",\n\t\t\t\"f\": \"d\",\n\t\t},\n\t\tTracing: Span{\n\t\t\ttraceID:  294390430934,\n\t\t\tparentID: 398348934,\n\t\t\tspanID:   12762782,\n\t\t\tflags:    0x04,\n\t\t},\n\t}\n\n\tassert.Equal(t, uint32(0xDEADBEEF), r.ID())\n\tassert.Equal(t, messageTypeCallRes, r.messageType())\n\tassertRoundTrip(t, &r, &callRes{id: 0xDEADBEEF})\n}\n\nfunc TestCallResContinue(t *testing.T) {\n\tr := callResContinue{\n\t\tid: 0xDEADBEEF,\n\t}\n\n\tassert.Equal(t, uint32(0xDEADBEEF), r.ID())\n\tassert.Equal(t, messageTypeCallResContinue, r.messageType())\n\tassertRoundTrip(t, &r, &callResContinue{id: 0xDEADBEEF})\n}\n\nfunc TestErrorMessage(t *testing.T) {\n\tm := errorMessage{\n\t\terrCode: ErrCodeBusy,\n\t\tmessage: \"go away\",\n\t}\n\n\tassert.Equal(t, messageTypeError, m.messageType())\n\tassertRoundTrip(t, &m, &errorMessage{})\n}\n\nfunc assertRoundTrip(t *testing.T, expected message, actual message) {\n\tw := typed.NewWriteBufferWithSize(1024)\n\trequire.Nil(t, expected.write(w), fmt.Sprintf(\"error writing message %v\", expected.messageType()))\n\n\tvar b bytes.Buffer\n\tw.FlushTo(&b)\n\n\tr := typed.NewReadBuffer(b.Bytes())\n\trequire.Nil(t, actual.read(r), fmt.Sprintf(\"error reading message %v\", expected.messageType()))\n\n\tassert.Equal(t, expected, actual, fmt.Sprintf(\"pre- and post-marshal %v do not match\", expected.messageType()))\n}\n"
  },
  {
    "path": "messagetype_string.go",
    "content": "// Code generated by \"stringer -type=messageType\"; DO NOT EDIT.\n\npackage tchannel\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[messageTypeInitReq-1]\n\t_ = x[messageTypeInitRes-2]\n\t_ = x[messageTypeCallReq-3]\n\t_ = x[messageTypeCallRes-4]\n\t_ = x[messageTypeCallReqContinue-19]\n\t_ = x[messageTypeCallResContinue-20]\n\t_ = x[messageTypeCancel-192]\n\t_ = x[messageTypePingReq-208]\n\t_ = x[messageTypePingRes-209]\n\t_ = x[messageTypeError-255]\n}\n\nconst (\n\t_messageType_name_0 = \"messageTypeInitReqmessageTypeInitResmessageTypeCallReqmessageTypeCallRes\"\n\t_messageType_name_1 = \"messageTypeCallReqContinuemessageTypeCallResContinue\"\n\t_messageType_name_2 = \"messageTypeCancel\"\n\t_messageType_name_3 = \"messageTypePingReqmessageTypePingRes\"\n\t_messageType_name_4 = \"messageTypeError\"\n)\n\nvar (\n\t_messageType_index_0 = [...]uint8{0, 18, 36, 54, 72}\n\t_messageType_index_1 = [...]uint8{0, 26, 52}\n\t_messageType_index_3 = [...]uint8{0, 18, 36}\n)\n\nfunc (i messageType) String() string {\n\tswitch {\n\tcase 1 <= i && i <= 4:\n\t\ti -= 1\n\t\treturn _messageType_name_0[_messageType_index_0[i]:_messageType_index_0[i+1]]\n\tcase 19 <= i && i <= 20:\n\t\ti -= 19\n\t\treturn _messageType_name_1[_messageType_index_1[i]:_messageType_index_1[i+1]]\n\tcase i == 192:\n\t\treturn _messageType_name_2\n\tcase 208 <= i && i <= 209:\n\t\ti -= 208\n\t\treturn _messageType_name_3[_messageType_index_3[i]:_messageType_index_3[i+1]]\n\tcase i == 255:\n\t\treturn _messageType_name_4\n\tdefault:\n\t\treturn \"messageType(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n}\n"
  },
  {
    "path": "mex.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\nvar (\n\terrDuplicateMex        = errors.New(\"multiple attempts to use the message id\")\n\terrMexShutdown         = errors.New(\"mex has been shutdown\")\n\terrMexSetShutdown      = errors.New(\"mexset has been shutdown\")\n\terrMexChannelFull      = NewSystemError(ErrCodeBusy, \"cannot send frame to message exchange channel\")\n\terrUnexpectedFrameType = errors.New(\"unexpected frame received\")\n)\n\nconst (\n\tmessageExchangeSetInbound  = \"inbound\"\n\tmessageExchangeSetOutbound = \"outbound\"\n\n\t// mexChannelBufferSize is the size of the message exchange channel buffer.\n\tmexChannelBufferSize = 2\n)\n\ntype errNotifier struct {\n\tc        chan struct{}\n\terr      error\n\tnotified atomic.Bool\n}\n\nfunc newErrNotifier() errNotifier {\n\treturn errNotifier{c: make(chan struct{})}\n}\n\n// Notify will store the error and notify all waiters on c that there's an error.\nfunc (e *errNotifier) Notify(err error) error {\n\t// The code should never try to Notify(nil).\n\tif err == nil {\n\t\tpanic(\"cannot Notify with no error\")\n\t}\n\n\t// There may be some sort of race where we try to notify the mex twice.\n\tif !e.notified.CAS(false, true) {\n\t\treturn fmt.Errorf(\"cannot broadcast error: %v, already have: %v\", err, e.err)\n\t}\n\n\te.err = err\n\tclose(e.c)\n\treturn nil\n}\n\n// checkErr returns previously notified errors (if any).\nfunc (e *errNotifier) checkErr() error {\n\tselect {\n\tcase <-e.c:\n\t\treturn e.err\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// A messageExchange tracks this Connections's side of a message exchange with a\n// peer.  Each message exchange has a channel that can be used to receive\n// frames from the peer, and a Context that can controls when the exchange has\n// timed out or been cancelled.\ntype messageExchange struct {\n\trecvCh    chan *Frame\n\terrCh     errNotifier\n\tctx       context.Context\n\tctxCancel context.CancelFunc\n\tmsgID     uint32\n\tmsgType   messageType\n\tmexset    *messageExchangeSet\n\tframePool FramePool\n\n\tshutdownAtomic atomic.Bool\n\terrChNotified  atomic.Bool\n}\n\n// checkError is called before waiting on the mex channels.\n// It returns any existing errors (timeout, cancellation, connection errors).\nfunc (mex *messageExchange) checkError() error {\n\tif err := mex.ctx.Err(); err != nil {\n\t\treturn GetContextError(err)\n\t}\n\n\treturn mex.errCh.checkErr()\n}\n\n// forwardPeerFrame forwards a frame from a peer to the message exchange, where\n// it can be pulled by whatever application thread is handling the exchange\nfunc (mex *messageExchange) forwardPeerFrame(frame *Frame) error {\n\t// We want a very specific priority here:\n\t// 1. Timeouts/cancellation (mex.ctx errors)\n\t// 2. Whether recvCh has buffer space (non-blocking select over mex.recvCh)\n\t// 3. Other mex errors (mex.errCh)\n\t// Which is why we check the context error only (instead of mex.checkError).\n\t// In the mex.errCh case, we do a non-blocking write to recvCh to prioritize it.\n\tif err := mex.ctx.Err(); err != nil {\n\t\treturn GetContextError(err)\n\t}\n\n\tselect {\n\tcase mex.recvCh <- frame:\n\t\treturn nil\n\tcase <-mex.ctx.Done():\n\t\t// Note: One slow reader processing a large request could stall the connection.\n\t\t// If we see this, we need to increase the recvCh buffer size.\n\t\treturn GetContextError(mex.ctx.Err())\n\tcase <-mex.errCh.c:\n\t\t// Select will randomly choose a case, but we want to prioritize\n\t\t// sending a frame over the errCh. Try a non-blocking write.\n\t\tselect {\n\t\tcase mex.recvCh <- frame:\n\t\t\treturn nil\n\t\tdefault:\n\t\t}\n\t\treturn mex.errCh.err\n\t}\n}\n\nfunc (mex *messageExchange) handleCancel(_ *Frame) {\n\tif mex.ctxCancel != nil {\n\t\tmex.ctxCancel()\n\t}\n}\n\nfunc (mex *messageExchange) checkFrame(frame *Frame) error {\n\tif frame.Header.ID != mex.msgID {\n\t\tmex.mexset.log.WithFields(\n\t\t\tLogField{\"msgId\", mex.msgID},\n\t\t\tLogField{\"header\", frame.Header},\n\t\t).Error(\"recvPeerFrame received msg with unexpected ID.\")\n\t\treturn errUnexpectedFrameType\n\t}\n\treturn nil\n}\n\n// recvPeerFrame waits for a new frame from the peer, or until the context\n// expires or is cancelled\nfunc (mex *messageExchange) recvPeerFrame() (*Frame, error) {\n\t// We have to check frames/errors in a very specific order here:\n\t// 1. Timeouts/cancellation (mex.ctx errors)\n\t// 2. Any pending frames (non-blocking select over mex.recvCh)\n\t// 3. Other mex errors (mex.errCh)\n\t// Which is why we check the context error only (instead of mex.checkError)e\n\t// In the mex.errCh case, we do a non-blocking read from recvCh to prioritize it.\n\tif err := mex.ctx.Err(); err != nil {\n\t\tmex.onCtxErr(err)\n\t\treturn nil, GetContextError(err)\n\t}\n\n\tselect {\n\tcase frame := <-mex.recvCh:\n\t\tif err := mex.checkFrame(frame); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn frame, nil\n\tcase <-mex.ctx.Done():\n\t\tmex.onCtxErr(mex.ctx.Err())\n\t\treturn nil, GetContextError(mex.ctx.Err())\n\tcase <-mex.errCh.c:\n\t\t// Select will randomly choose a case, but we want to prioritize\n\t\t// receiving a frame over errCh. Try a non-blocking read.\n\t\tselect {\n\t\tcase frame := <-mex.recvCh:\n\t\t\tif err := mex.checkFrame(frame); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn frame, nil\n\t\tdefault:\n\t\t}\n\t\treturn nil, mex.errCh.err\n\t}\n}\n\n// recvPeerFrameOfType waits for a new frame of a given type from the peer, failing\n// if the next frame received is not of that type.\n// If an error frame is returned, then the errorMessage is returned as the error.\nfunc (mex *messageExchange) recvPeerFrameOfType(msgType messageType) (*Frame, error) {\n\tframe, err := mex.recvPeerFrame()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch frame.Header.messageType {\n\tcase msgType:\n\t\treturn frame, nil\n\n\tcase messageTypeError:\n\t\t// If we read an error frame, we can release it once we deserialize it.\n\t\tdefer mex.framePool.Release(frame)\n\n\t\terrMsg := errorMessage{\n\t\t\tid: frame.Header.ID,\n\t\t}\n\t\tvar rbuf typed.ReadBuffer\n\t\trbuf.Wrap(frame.SizedPayload())\n\t\tif err := errMsg.read(&rbuf); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn nil, errMsg\n\n\tdefault:\n\t\t// TODO(mmihic): Should be treated as a protocol error\n\t\tmex.mexset.log.WithFields(\n\t\t\tLogField{\"header\", frame.Header},\n\t\t\tLogField{\"expectedType\", msgType},\n\t\t\tLogField{\"expectedID\", mex.msgID},\n\t\t).Warn(\"Received unexpected frame.\")\n\t\treturn nil, errUnexpectedFrameType\n\t}\n}\n\nfunc (mex *messageExchange) onCtxErr(err error) {\n\t// On canceled contexts, we may need to send a cancel message.\n\tif err != context.Canceled {\n\t\treturn\n\t}\n\n\tif onCancel := mex.mexset.onCancel; onCancel != nil {\n\t\tonCancel(mex.msgID)\n\t}\n}\n\n// shutdown shuts down the message exchange, removing it from the message\n// exchange set so  that it cannot receive more messages from the peer.  The\n// receive channel remains open, however, in case there are concurrent\n// goroutines sending to it.\nfunc (mex *messageExchange) shutdown() {\n\t// The reader and writer side can both hit errors and try to shutdown the mex,\n\t// so we ensure that it's only shut down once.\n\tif !mex.shutdownAtomic.CAS(false, true) {\n\t\treturn\n\t}\n\n\tif mex.errChNotified.CAS(false, true) {\n\t\tmex.errCh.Notify(errMexShutdown)\n\t}\n\n\tmex.mexset.removeExchange(mex.msgID)\n\treturn\n}\n\n// inboundExpired is called when an exchange is canceled or it times out,\n// but a handler may still be running in the background. Since the handler may\n// still write to the exchange, we cannot shutdown the exchange, but we should\n// remove it from the connection's exchange list.\nfunc (mex *messageExchange) inboundExpired() {\n\tmex.mexset.expireExchange(mex.msgID)\n}\n\n// A messageExchangeSet manages a set of active message exchanges.  It is\n// mainly used to route frames from a peer to the appropriate messageExchange,\n// or to cancel or mark a messageExchange as being in error.  Each Connection\n// maintains two messageExchangeSets, one to manage exchanges that it has\n// initiated (outbound), and another to manage exchanges that the peer has\n// initiated (inbound).  The message-type specific handlers are responsible for\n// ensuring that their message exchanges are properly registered and removed\n// from the corresponding exchange set.\ntype messageExchangeSet struct {\n\tsync.RWMutex\n\n\tlog       Logger\n\tname      string\n\tonCancel  func(id uint32)\n\tonRemoved func()\n\tonAdded   func()\n\n\t// maps are mutable, and are protected by the mutex.\n\texchanges        map[uint32]*messageExchange\n\texpiredExchanges map[uint32]struct{}\n\tshutdown         bool\n}\n\n// newMessageExchangeSet creates a new messageExchangeSet with a given name.\nfunc newMessageExchangeSet(log Logger, name string) *messageExchangeSet {\n\treturn &messageExchangeSet{\n\t\tname:             name,\n\t\tlog:              log.WithFields(LogField{\"exchange\", name}),\n\t\texchanges:        make(map[uint32]*messageExchange),\n\t\texpiredExchanges: make(map[uint32]struct{}),\n\t}\n}\n\n// addExchange adds an exchange, it must be called with the mexset locked.\nfunc (mexset *messageExchangeSet) addExchange(mex *messageExchange) error {\n\tif mexset.shutdown {\n\t\treturn errMexSetShutdown\n\t}\n\n\tif _, ok := mexset.exchanges[mex.msgID]; ok {\n\t\treturn errDuplicateMex\n\t}\n\n\tmexset.exchanges[mex.msgID] = mex\n\treturn nil\n}\n\n// newExchange creates and adds a new message exchange to this set\nfunc (mexset *messageExchangeSet) newExchange(ctx context.Context, ctxCancel context.CancelFunc, framePool FramePool,\n\tmsgType messageType, msgID uint32, bufferSize int) (*messageExchange, error) {\n\tif mexset.log.Enabled(LogLevelDebug) {\n\t\tmexset.log.Debugf(\"Creating new %s message exchange for [%v:%d]\", mexset.name, msgType, msgID)\n\t}\n\n\tmex := &messageExchange{\n\t\tmsgType:   msgType,\n\t\tmsgID:     msgID,\n\t\tctx:       ctx,\n\t\tctxCancel: ctxCancel,\n\t\trecvCh:    make(chan *Frame, bufferSize),\n\t\terrCh:     newErrNotifier(),\n\t\tmexset:    mexset,\n\t\tframePool: framePool,\n\t}\n\n\tmexset.Lock()\n\taddErr := mexset.addExchange(mex)\n\tmexset.Unlock()\n\n\tif addErr != nil {\n\t\tlogger := mexset.log.WithFields(\n\t\t\tLogField{\"msgID\", mex.msgID},\n\t\t\tLogField{\"msgType\", mex.msgType},\n\t\t\tLogField{\"exchange\", mexset.name},\n\t\t)\n\t\tif addErr == errMexSetShutdown {\n\t\t\tlogger.Warn(\"Attempted to create new mex after mexset shutdown.\")\n\t\t} else if addErr == errDuplicateMex {\n\t\t\tlogger.Warn(\"Duplicate msg ID for active and new mex.\")\n\t\t}\n\n\t\treturn nil, addErr\n\t}\n\n\tmexset.onAdded()\n\n\t// TODO(mmihic): Put into a deadline ordered heap so we can garbage collected expired exchanges\n\treturn mex, nil\n}\n\n// deleteExchange will delete msgID, and return whether it was found or whether it was\n// timed out. This method must be called with the lock.\nfunc (mexset *messageExchangeSet) deleteExchange(msgID uint32) (found, timedOut bool) {\n\tif _, found := mexset.exchanges[msgID]; found {\n\t\tdelete(mexset.exchanges, msgID)\n\t\treturn true, false\n\t}\n\n\tif _, expired := mexset.expiredExchanges[msgID]; expired {\n\t\tdelete(mexset.expiredExchanges, msgID)\n\t\treturn false, true\n\t}\n\n\treturn false, false\n}\n\n// removeExchange removes a message exchange from the set, if it exists.\nfunc (mexset *messageExchangeSet) removeExchange(msgID uint32) {\n\tif mexset.log.Enabled(LogLevelDebug) {\n\t\tmexset.log.Debugf(\"Removing %s message exchange %d\", mexset.name, msgID)\n\t}\n\n\tmexset.Lock()\n\tfound, expired := mexset.deleteExchange(msgID)\n\tmexset.Unlock()\n\n\tif !found && !expired {\n\t\tmexset.log.WithFields(\n\t\t\tLogField{\"msgID\", msgID},\n\t\t).Error(\"Tried to remove exchange multiple times\")\n\t\treturn\n\t}\n\n\t// If the message exchange was found, then we perform clean up actions.\n\t// These clean up actions can only be run once per exchange.\n\tmexset.onRemoved()\n}\n\n// expireExchange is similar to removeExchange, but it marks the exchange as\n// expired.\nfunc (mexset *messageExchangeSet) expireExchange(msgID uint32) {\n\tmexset.log.Debugf(\n\t\t\"Removing %s message exchange %d due to timeout, cancellation or blackhole\",\n\t\tmexset.name,\n\t\tmsgID,\n\t)\n\n\tmexset.Lock()\n\t// TODO(aniketp): explore if cancel can be called everytime we expire an exchange\n\tfound, expired := mexset.deleteExchange(msgID)\n\tif found || expired {\n\t\t// Record in expiredExchanges if we deleted the exchange.\n\t\tmexset.expiredExchanges[msgID] = struct{}{}\n\t}\n\tmexset.Unlock()\n\n\tif expired {\n\t\tmexset.log.WithFields(LogField{\"msgID\", msgID}).Info(\"Exchange expired already\")\n\t}\n\n\tmexset.onRemoved()\n}\n\nfunc (mexset *messageExchangeSet) count() int {\n\tmexset.RLock()\n\tcount := len(mexset.exchanges)\n\tmexset.RUnlock()\n\n\treturn count\n}\n\n// forwardPeerFrame forwards a frame from the peer to the appropriate message\n// exchange\nfunc (mexset *messageExchangeSet) forwardPeerFrame(frame *Frame) error {\n\tif mexset.log.Enabled(LogLevelDebug) {\n\t\tmexset.log.Debugf(\"forwarding %s %s\", mexset.name, frame.Header)\n\t}\n\n\tmexset.RLock()\n\tmex := mexset.exchanges[frame.Header.ID]\n\tmexset.RUnlock()\n\n\tif mex == nil {\n\t\t// This is ok since the exchange might have expired or been cancelled\n\t\tmexset.log.WithFields(\n\t\t\tLogField{\"frameHeader\", frame.Header.String()},\n\t\t\tLogField{\"exchange\", mexset.name},\n\t\t).Info(\"Received frame for unknown message exchange.\")\n\t\treturn nil\n\t}\n\n\tif err := mex.forwardPeerFrame(frame); err != nil {\n\t\tmexset.log.WithFields(\n\t\t\tLogField{\"frameHeader\", frame.Header.String()},\n\t\t\tLogField{\"frameSize\", frame.Header.FrameSize()},\n\t\t\tLogField{\"exchange\", mexset.name},\n\t\t\tErrField(err),\n\t\t).Info(\"Failed to forward frame.\")\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (mexset *messageExchangeSet) handleCancel(frame *Frame) {\n\tif mexset.log.Enabled(LogLevelDebug) {\n\t\tmexset.log.Debugf(\"handling cancel for %s\", mexset.name, frame.Header)\n\t}\n\n\tmexset.RLock()\n\tmex := mexset.exchanges[frame.Header.ID]\n\tmexset.RUnlock()\n\n\tif mex == nil {\n\t\t// This is ok since the exchange might have expired.\n\t\tmexset.log.WithFields(\n\t\t\tLogField{\"frameHeader\", frame.Header.String()},\n\t\t\tLogField{\"exchange\", mexset.name},\n\t\t).Info(\"Received cancel frame for unknown message exchange.\")\n\t\treturn\n\t}\n\n\tmex.handleCancel(frame)\n}\n\n// copyExchanges returns a copy of the exchanges if the exchange is active.\n// The caller must lock the mexset.\nfunc (mexset *messageExchangeSet) copyExchanges() (shutdown bool, exchanges map[uint32]*messageExchange) {\n\tif mexset.shutdown {\n\t\treturn true, nil\n\t}\n\n\texchangesCopy := make(map[uint32]*messageExchange, len(mexset.exchanges))\n\tfor k, mex := range mexset.exchanges {\n\t\texchangesCopy[k] = mex\n\t}\n\n\treturn false, exchangesCopy\n}\n\n// stopExchanges stops all message exchanges to unblock all waiters on the mex.\n// This should only be called on connection failures.\nfunc (mexset *messageExchangeSet) stopExchanges(err error) {\n\tif mexset.log.Enabled(LogLevelDebug) {\n\t\tmexset.log.Debugf(\"stopping %v exchanges due to error: %v\", mexset.count(), err)\n\t}\n\n\tmexset.Lock()\n\tshutdown, exchanges := mexset.copyExchanges()\n\tmexset.shutdown = true\n\tmexset.Unlock()\n\n\tif shutdown {\n\t\tmexset.log.Debugf(\"mexset has already been shutdown\")\n\t\treturn\n\t}\n\n\tfor _, mex := range exchanges {\n\t\t// When there's a connection failure, we want to notify blocked callers that the\n\t\t// call will fail, but we don't want to shutdown the exchange as only the\n\t\t// arg reader/writer should shutdown the exchange. Otherwise, our guarantee\n\t\t// on sendChRefs that there's no references to sendCh is violated since\n\t\t// readers/writers could still have a reference to sendCh even though\n\t\t// we shutdown the exchange and called Done on sendChRefs.\n\t\tif mex.errChNotified.CAS(false, true) {\n\t\t\tmex.errCh.Notify(err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "mex_utils_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// CheckEmptyExchangesConn checks whether all exchanges for the given connection are empty.\n// If there are exchanges, a string with information about leftover exchanges is returned.\nfunc CheckEmptyExchangesConn(c *ConnectionRuntimeState) string {\n\tvar errors []string\n\tcheckExchange := func(e ExchangeSetRuntimeState) {\n\t\tif e.Count > 0 {\n\t\t\terrors = append(errors, fmt.Sprintf(\" %v leftover %v exchanges\", e.Name, e.Count))\n\t\t\tfor _, v := range e.Exchanges {\n\t\t\t\terrors = append(errors, fmt.Sprintf(\"  exchanges: %+v\", v))\n\t\t\t}\n\t\t}\n\t}\n\tcheckExchange(c.InboundExchange)\n\tcheckExchange(c.OutboundExchange)\n\tif len(errors) == 0 {\n\t\treturn \"\"\n\t}\n\n\treturn fmt.Sprintf(\"Connection %d has leftover exchanges:\\n\\t%v\", c.ID, strings.Join(errors, \"\\n\\t\"))\n}\n\n// CheckEmptyExchangesConns checks that all exchanges for the given connections are empty.\nfunc CheckEmptyExchangesConns(connections []*ConnectionRuntimeState) string {\n\tvar errors []string\n\tfor _, c := range connections {\n\t\tif v := CheckEmptyExchangesConn(c); v != \"\" {\n\t\t\terrors = append(errors, v)\n\t\t}\n\t}\n\treturn strings.Join(errors, \"\\n\")\n}\n\n// CheckEmptyExchanges checks that all exchanges for the given channel are empty.\n//\n// TODO: Remove CheckEmptyExchanges and friends in favor of\n// testutils.TestServer's verification.\nfunc CheckEmptyExchanges(ch *Channel) string {\n\tstate := ch.IntrospectState(&IntrospectionOptions{IncludeExchanges: true})\n\tvar connections []*ConnectionRuntimeState\n\tfor _, peer := range state.RootPeers {\n\t\tfor _, conn := range peer.InboundConnections {\n\t\t\tconnections = append(connections, &conn)\n\t\t}\n\t\tfor _, conn := range peer.OutboundConnections {\n\t\t\tconnections = append(connections, &conn)\n\t\t}\n\t}\n\treturn CheckEmptyExchangesConns(connections)\n}\n"
  },
  {
    "path": "outbound.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/ext\"\n\t\"golang.org/x/net/context\"\n)\n\n// maxMethodSize is the maximum size of arg1.\nconst maxMethodSize = 16 * 1024\n\n// beginCall begins an outbound call on the connection\nfunc (c *Connection) beginCall(ctx context.Context, serviceName, methodName string, callOptions *CallOptions) (*OutboundCall, error) {\n\tnow := c.timeNow()\n\n\tswitch state := c.readState(); state {\n\tcase connectionActive:\n\t\tbreak\n\tcase connectionStartClose, connectionInboundClosed, connectionClosed:\n\t\treturn nil, ErrConnectionClosed\n\tdefault:\n\t\treturn nil, errConnectionUnknownState{\"beginCall\", state}\n\t}\n\n\tdeadline, ok := ctx.Deadline()\n\tif !ok {\n\t\t// This case is handled by validateCall, so we should\n\t\t// never get here.\n\t\treturn nil, ErrTimeoutRequired\n\t}\n\n\t// If the timeToLive is less than a millisecond, it will be encoded as 0 on\n\t// the wire, hence we return a timeout immediately.\n\ttimeToLive := deadline.Sub(now)\n\tif timeToLive < time.Millisecond {\n\t\treturn nil, ErrTimeout\n\t}\n\n\tif err := ctx.Err(); err != nil {\n\t\treturn nil, GetContextError(err)\n\t}\n\n\trequestID := c.NextMessageID()\n\tmex, err := c.outbound.newExchange(ctx, c.outboundCtxCancel, c.opts.FramePool, messageTypeCallReq, requestID, mexChannelBufferSize)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Close may have been called between the time we checked the state and us creating the exchange.\n\tif state := c.readState(); state != connectionActive {\n\t\tmex.shutdown()\n\t\treturn nil, ErrConnectionClosed\n\t}\n\n\t// Note: We don't verify number of transport headers as the library doesn't\n\t// allow adding arbitrary headers. Ensure we never add >= 256 headers here.\n\theaders := transportHeaders{\n\t\tCallerName: c.localPeerInfo.ServiceName,\n\t}\n\tcallOptions.setHeaders(headers)\n\tif opts := currentCallOptions(ctx); opts != nil {\n\t\topts.overrideHeaders(headers)\n\t}\n\n\tcall := new(OutboundCall)\n\tcall.mex = mex\n\tcall.conn = c\n\tcall.callReq = callReq{\n\t\tid:         requestID,\n\t\tHeaders:    headers,\n\t\tService:    serviceName,\n\t\tTimeToLive: timeToLive,\n\t}\n\tcall.statsReporter = c.statsReporter\n\tcall.createStatsTags(c.commonStatsTags, callOptions, methodName)\n\tcall.log = c.log.WithFields(LogField{\"Out-Call\", requestID})\n\n\t// TODO(mmihic): It'd be nice to do this without an fptr\n\tcall.messageForFragment = func(initial bool) message {\n\t\tif initial {\n\t\t\treturn &call.callReq\n\t\t}\n\n\t\treturn new(callReqContinue)\n\t}\n\n\tcall.contents = newFragmentingWriter(call.log, call, c.opts.ChecksumType.New())\n\n\tresponse := new(OutboundCallResponse)\n\tresponse.startedAt = now\n\tresponse.timeNow = c.timeNow\n\tresponse.requestState = callOptions.RequestState\n\tresponse.mex = mex\n\tresponse.log = c.log.WithFields(LogField{\"Out-Response\", requestID})\n\tresponse.span = c.startOutboundSpan(ctx, serviceName, methodName, call, now)\n\tresponse.messageForFragment = func(initial bool) message {\n\t\tif initial {\n\t\t\treturn &response.callRes\n\t\t}\n\n\t\treturn new(callResContinue)\n\t}\n\tresponse.contents = newFragmentingReader(response.log, response)\n\tresponse.statsReporter = call.statsReporter\n\tresponse.commonStatsTags = call.commonStatsTags\n\n\tcall.response = response\n\n\tif err := call.writeMethod([]byte(methodName)); err != nil {\n\t\treturn nil, err\n\t}\n\treturn call, nil\n}\n\nfunc (c *Connection) outboundCtxCancel() {\n\t// outbound contexts are created by callers, can't cancel them.\n\t// However, we shouldn't be trying to cancel them, so log.\n\tc.log.Debug(\"unexpected cancel of outbound context\")\n}\n\n// handleCallRes handles an incoming call req message, forwarding the\n// frame to the response channel waiting for it\nfunc (c *Connection) handleCallRes(frame *Frame) bool {\n\tif err := c.outbound.forwardPeerFrame(frame); err != nil {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// handleCallResContinue handles an incoming call res continue message,\n// forwarding the frame to the response channel waiting for it\nfunc (c *Connection) handleCallResContinue(frame *Frame) bool {\n\tif err := c.outbound.forwardPeerFrame(frame); err != nil {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// An OutboundCall is an active call to a remote peer.  A client makes a call\n// by calling BeginCall on the Channel, writing argument content via\n// ArgWriter2() ArgWriter3(), and then reading reading response data via the\n// ArgReader2() and ArgReader3() methods on the Response() object.\ntype OutboundCall struct {\n\treqResWriter\n\n\tcallReq         callReq\n\tresponse        *OutboundCallResponse\n\tstatsReporter   StatsReporter\n\tcommonStatsTags map[string]string\n}\n\n// Response provides access to the call's response object, which can be used to\n// read response arguments\nfunc (call *OutboundCall) Response() *OutboundCallResponse {\n\treturn call.response\n}\n\n// createStatsTags creates the common stats tags, if they are not already created.\nfunc (call *OutboundCall) createStatsTags(connectionTags map[string]string, callOptions *CallOptions, method string) {\n\tcall.commonStatsTags = map[string]string{\n\t\t\"target-service\": call.callReq.Service,\n\t}\n\tfor k, v := range connectionTags {\n\t\tcall.commonStatsTags[k] = v\n\t}\n\tif callOptions.Format != HTTP {\n\t\tcall.commonStatsTags[\"target-endpoint\"] = string(method)\n\t}\n}\n\n// writeMethod writes the method (arg1) to the call\nfunc (call *OutboundCall) writeMethod(method []byte) error {\n\tcall.statsReporter.IncCounter(\"outbound.calls.send\", call.commonStatsTags, 1)\n\treturn NewArgWriter(call.arg1Writer()).Write(method)\n}\n\n// Arg2Writer returns a WriteCloser that can be used to write the second argument.\n// The returned writer must be closed once the write is complete.\nfunc (call *OutboundCall) Arg2Writer() (ArgWriter, error) {\n\treturn call.arg2Writer()\n}\n\n// Arg3Writer returns a WriteCloser that can be used to write the last argument.\n// The returned writer must be closed once the write is complete.\nfunc (call *OutboundCall) Arg3Writer() (ArgWriter, error) {\n\treturn call.arg3Writer()\n}\n\n// LocalPeer returns the local peer information for this call.\nfunc (call *OutboundCall) LocalPeer() LocalPeerInfo {\n\treturn call.conn.localPeerInfo\n}\n\n// RemotePeer returns the remote peer information for this call.\nfunc (call *OutboundCall) RemotePeer() PeerInfo {\n\treturn call.conn.RemotePeerInfo()\n}\n\nfunc (call *OutboundCall) doneSending() {}\n\n// An OutboundCallResponse is the response to an outbound call\ntype OutboundCallResponse struct {\n\treqResReader\n\n\tcallRes callRes\n\n\trequestState *RequestState\n\t// startedAt is the time at which the outbound call was started.\n\tstartedAt       time.Time\n\ttimeNow         func() time.Time\n\tspan            opentracing.Span\n\tstatsReporter   StatsReporter\n\tcommonStatsTags map[string]string\n}\n\n// ApplicationError returns true if the call resulted in an application level error\n// TODO(mmihic): In current implementation, you must have called Arg2Reader before this\n// method returns the proper value.  We should instead have this block until the first\n// fragment is available, if the first fragment hasn't been received.\nfunc (response *OutboundCallResponse) ApplicationError() bool {\n\t// TODO(mmihic): Wait for first fragment\n\treturn response.callRes.ResponseCode == responseApplicationError\n}\n\n// Format the format of the request from the ArgScheme transport header.\nfunc (response *OutboundCallResponse) Format() Format {\n\treturn Format(response.callRes.Headers[ArgScheme])\n}\n\n// Arg2Reader returns an ArgReader to read the second argument.\n// The ReadCloser must be closed once the argument has been read.\nfunc (response *OutboundCallResponse) Arg2Reader() (ArgReader, error) {\n\tvar method []byte\n\tif err := NewArgReader(response.arg1Reader()).Read(&method); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn response.arg2Reader()\n}\n\n// Arg3Reader returns an ArgReader to read the last argument.\n// The ReadCloser must be closed once the argument has been read.\nfunc (response *OutboundCallResponse) Arg3Reader() (ArgReader, error) {\n\treturn response.arg3Reader()\n}\n\n// handleError handles an error coming back from the peer. If the error is a\n// protocol level error, the entire connection will be closed.  If the error is\n// a request specific error, it will be written to the request's response\n// channel and converted into a SystemError returned from the next reader or\n// access call.\n// The return value is whether the frame should be released immediately.\nfunc (c *Connection) handleError(frame *Frame) bool {\n\terrMsg := errorMessage{\n\t\tid: frame.Header.ID,\n\t}\n\trbuf := typed.NewReadBuffer(frame.SizedPayload())\n\tif err := errMsg.read(rbuf); err != nil {\n\t\tc.log.WithFields(\n\t\t\tLogField{\"remotePeer\", c.remotePeerInfo},\n\t\t\tErrField(err),\n\t\t).Warn(\"Unable to read error frame.\")\n\t\tc.connectionError(\"parsing error frame\", err)\n\t\treturn true\n\t}\n\n\tif errMsg.errCode == ErrCodeProtocol {\n\t\tc.log.WithFields(\n\t\t\tLogField{\"remotePeer\", c.remotePeerInfo},\n\t\t\tLogField{\"error\", errMsg.message},\n\t\t).Warn(\"Peer reported protocol error.\")\n\t\tc.connectionError(\"received protocol error\", errMsg.AsSystemError())\n\t\treturn true\n\t}\n\n\tif err := c.outbound.forwardPeerFrame(frame); err != nil {\n\t\tc.log.WithFields(\n\t\t\tLogField{\"frameHeader\", frame.Header.String()},\n\t\t\tLogField{\"id\", errMsg.id},\n\t\t\tLogField{\"errorMessage\", errMsg.message},\n\t\t\tLogField{\"errorCode\", errMsg.errCode},\n\t\t\tErrField(err),\n\t\t).Info(\"Failed to forward error frame.\")\n\t\treturn true\n\t}\n\n\t// If the frame was forwarded, then the other side is responsible for releasing the frame.\n\treturn false\n}\n\nfunc cloneTags(tags map[string]string) map[string]string {\n\tnewTags := make(map[string]string, len(tags))\n\tfor k, v := range tags {\n\t\tnewTags[k] = v\n\t}\n\treturn newTags\n}\n\n// doneReading shuts down the message exchange for this call.\n// For outgoing calls, the last message is reading the call response.\nfunc (response *OutboundCallResponse) doneReading(unexpected error) {\n\tnow := response.timeNow()\n\n\tisSuccess := unexpected == nil && !response.ApplicationError()\n\tlastAttempt := isSuccess || !response.requestState.HasRetries(unexpected)\n\n\t// TODO how should this work with retries?\n\tif span := response.span; span != nil {\n\t\tif unexpected != nil {\n\t\t\tspan.LogEventWithPayload(\"error\", unexpected)\n\t\t}\n\t\tif !isSuccess && lastAttempt {\n\t\t\text.Error.Set(span, true)\n\t\t}\n\t\tspan.FinishWithOptions(opentracing.FinishOptions{FinishTime: now})\n\t}\n\n\tlatency := now.Sub(response.startedAt)\n\tresponse.statsReporter.RecordTimer(\"outbound.calls.per-attempt.latency\", response.commonStatsTags, latency)\n\tif lastAttempt {\n\t\trequestLatency := response.requestState.SinceStart(now, latency)\n\t\tresponse.statsReporter.RecordTimer(\"outbound.calls.latency\", response.commonStatsTags, requestLatency)\n\t}\n\tif retryCount := response.requestState.RetryCount(); retryCount > 0 {\n\t\tretryTags := cloneTags(response.commonStatsTags)\n\t\tretryTags[\"retry-count\"] = fmt.Sprint(retryCount)\n\t\tresponse.statsReporter.IncCounter(\"outbound.calls.retries\", retryTags, 1)\n\t}\n\n\tif unexpected != nil {\n\t\t// TODO(prashant): Report the error code type as per metrics doc and enable.\n\t\t// response.statsReporter.IncCounter(\"outbound.calls.system-errors\", response.commonStatsTags, 1)\n\t} else if response.ApplicationError() {\n\t\t// TODO(prashant): Figure out how to add \"type\" to tags, which TChannel does not know about.\n\t\tresponse.statsReporter.IncCounter(\"outbound.calls.per-attempt.app-errors\", response.commonStatsTags, 1)\n\t\tif lastAttempt {\n\t\t\tresponse.statsReporter.IncCounter(\"outbound.calls.app-errors\", response.commonStatsTags, 1)\n\t\t}\n\t} else {\n\t\tresponse.statsReporter.IncCounter(\"outbound.calls.success\", response.commonStatsTags, 1)\n\t}\n\n\tresponse.mex.shutdown()\n}\n\nfunc validateCall(ctx context.Context, serviceName, methodName string, callOpts *CallOptions) error {\n\tif serviceName == \"\" {\n\t\treturn ErrNoServiceName\n\t}\n\n\tif len(methodName) > maxMethodSize {\n\t\treturn ErrMethodTooLarge\n\t}\n\n\tif _, ok := ctx.Deadline(); !ok {\n\t\treturn ErrTimeoutRequired\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "peer.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"container/heap\"\n\t\"errors\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/trand\"\n\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\nvar (\n\t// ErrInvalidConnectionState indicates that the connection is not in a valid state.\n\t// This may be due to a race between selecting the connection and it closing, so\n\t// it is a network failure that can be retried.\n\tErrInvalidConnectionState = NewSystemError(ErrCodeNetwork, \"connection is in an invalid state\")\n\n\t// ErrNoPeers indicates that there are no peers.\n\tErrNoPeers = errors.New(\"no peers available\")\n\n\t// ErrPeerNotFound indicates that the specified peer was not found.\n\tErrPeerNotFound = errors.New(\"peer not found\")\n\n\t// ErrNoNewPeers indicates that no previously unselected peer is available.\n\tErrNoNewPeers = errors.New(\"no new peer available\")\n\n\tpeerRng = trand.NewSeeded()\n)\n\n// Connectable is the interface used by peers to create connections.\ntype Connectable interface {\n\t// Connect tries to connect to the given hostPort.\n\tConnect(ctx context.Context, hostPort string) (*Connection, error)\n\t// Logger returns the logger to use.\n\tLogger() Logger\n}\n\n// PeerList maintains a list of Peers.\ntype PeerList struct {\n\tsync.RWMutex\n\n\tparent          *RootPeerList\n\tpeersByHostPort map[string]*peerScore\n\tpeerHeap        *peerHeap\n\tscoreCalculator ScoreCalculator\n\tlastSelected    uint64\n}\n\nfunc newPeerList(root *RootPeerList) *PeerList {\n\treturn &PeerList{\n\t\tparent:          root,\n\t\tpeersByHostPort: make(map[string]*peerScore),\n\t\tscoreCalculator: newPreferIncomingCalculator(),\n\t\tpeerHeap:        newPeerHeap(),\n\t}\n}\n\n// SetStrategy sets customized peer selection strategy.\nfunc (l *PeerList) SetStrategy(sc ScoreCalculator) {\n\tl.Lock()\n\tdefer l.Unlock()\n\n\tl.scoreCalculator = sc\n\tfor _, ps := range l.peersByHostPort {\n\t\tnewScore := l.scoreCalculator.GetScore(ps.Peer)\n\t\tl.updatePeer(ps, newScore)\n\t}\n}\n\n// Siblings don't share peer lists (though they take care not to double-connect\n// to the same hosts).\nfunc (l *PeerList) newSibling() *PeerList {\n\tsib := newPeerList(l.parent)\n\treturn sib\n}\n\n// Add adds a peer to the list if it does not exist, or returns any existing peer.\nfunc (l *PeerList) Add(hostPort string) *Peer {\n\tif ps, ok := l.exists(hostPort); ok {\n\t\treturn ps.Peer\n\t}\n\tl.Lock()\n\tdefer l.Unlock()\n\n\tif p, ok := l.peersByHostPort[hostPort]; ok {\n\t\treturn p.Peer\n\t}\n\n\tp := l.parent.Add(hostPort)\n\tp.addSC()\n\tps := newPeerScore(p, l.scoreCalculator.GetScore(p))\n\n\tl.peersByHostPort[hostPort] = ps\n\tl.peerHeap.addPeer(ps)\n\treturn p\n}\n\n// GetNew returns a new, previously unselected peer from the peer list, or nil,\n// if no new unselected peer can be found.\nfunc (l *PeerList) GetNew(prevSelected map[string]struct{}) (*Peer, error) {\n\tl.Lock()\n\tdefer l.Unlock()\n\tif l.peerHeap.Len() == 0 {\n\t\treturn nil, ErrNoPeers\n\t}\n\n\t// Select a peer, avoiding previously selected peers. If all peers have been previously\n\t// selected, then it's OK to repick them.\n\tpeer := l.choosePeer(prevSelected, true /* avoidHost */)\n\tif peer == nil {\n\t\tpeer = l.choosePeer(prevSelected, false /* avoidHost */)\n\t}\n\tif peer == nil {\n\t\treturn nil, ErrNoNewPeers\n\t}\n\treturn peer, nil\n}\n\n// Get returns a peer from the peer list, or nil if none can be found,\n// will avoid previously selected peers if possible.\nfunc (l *PeerList) Get(prevSelected map[string]struct{}) (*Peer, error) {\n\tpeer, err := l.GetNew(prevSelected)\n\tif err == ErrNoNewPeers {\n\t\tl.Lock()\n\t\tpeer = l.choosePeer(nil, false /* avoidHost */)\n\t\tl.Unlock()\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\tif peer == nil {\n\t\treturn nil, ErrNoPeers\n\t}\n\treturn peer, nil\n}\n\n// Remove removes a peer from the peer list. It returns an error if the peer cannot be found.\n// Remove does not affect connections to the peer in any way.\nfunc (l *PeerList) Remove(hostPort string) error {\n\tl.Lock()\n\tdefer l.Unlock()\n\n\tp, ok := l.peersByHostPort[hostPort]\n\tif !ok {\n\t\treturn ErrPeerNotFound\n\t}\n\n\tp.delSC()\n\tdelete(l.peersByHostPort, hostPort)\n\tl.peerHeap.removePeer(p)\n\n\treturn nil\n}\nfunc (l *PeerList) choosePeer(prevSelected map[string]struct{}, avoidHost bool) *Peer {\n\tvar psPopList []*peerScore\n\tvar ps *peerScore\n\n\tcanChoosePeer := func(hostPort string) bool {\n\t\tif _, ok := prevSelected[hostPort]; ok {\n\t\t\treturn false\n\t\t}\n\t\tif avoidHost {\n\t\t\tif _, ok := prevSelected[getHost(hostPort)]; ok {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\n\tsize := l.peerHeap.Len()\n\tfor i := 0; i < size; i++ {\n\t\tpopped := l.peerHeap.popPeer()\n\n\t\tif canChoosePeer(popped.HostPort()) {\n\t\t\tps = popped\n\t\t\tbreak\n\t\t}\n\t\tpsPopList = append(psPopList, popped)\n\t}\n\n\tfor _, p := range psPopList {\n\t\theap.Push(l.peerHeap, p)\n\t}\n\n\tif ps == nil {\n\t\treturn nil\n\t}\n\n\tl.peerHeap.pushPeer(ps)\n\tps.chosenCount.Inc()\n\treturn ps.Peer\n}\n\n// GetOrAdd returns a peer for the given hostPort, creating one if it doesn't yet exist.\nfunc (l *PeerList) GetOrAdd(hostPort string) *Peer {\n\t// TODO: remove calls to GetOrAdd, use Add instead\n\treturn l.Add(hostPort)\n}\n\n// Copy returns a copy of the PeerList as a map from hostPort to peer.\nfunc (l *PeerList) Copy() map[string]*Peer {\n\tl.RLock()\n\tdefer l.RUnlock()\n\n\tlistCopy := make(map[string]*Peer)\n\tfor k, v := range l.peersByHostPort {\n\t\tlistCopy[k] = v.Peer\n\t}\n\treturn listCopy\n}\n\n// Len returns the length of the PeerList.\nfunc (l *PeerList) Len() int {\n\tl.RLock()\n\tdefer l.RUnlock()\n\treturn l.peerHeap.Len()\n}\n\n// exists checks if a hostport exists in the peer list.\nfunc (l *PeerList) exists(hostPort string) (*peerScore, bool) {\n\tl.RLock()\n\tps, ok := l.peersByHostPort[hostPort]\n\tl.RUnlock()\n\n\treturn ps, ok\n}\n\n// getPeerScore is called to find the peer and its score from a host port key.\n// Note that at least a Read lock must be held to call this function.\nfunc (l *PeerList) getPeerScore(hostPort string) (*peerScore, uint64, bool) {\n\tps, ok := l.peersByHostPort[hostPort]\n\tif !ok {\n\t\treturn nil, 0, false\n\t}\n\treturn ps, ps.score, ok\n}\n\n// onPeerChange is called when there is a change that may cause the peer's score to change.\n// The new score is calculated, and the peer heap is updated with the new score if the score changes.\nfunc (l *PeerList) onPeerChange(p *Peer) {\n\tl.RLock()\n\tps, psScore, ok := l.getPeerScore(p.hostPort)\n\tsc := l.scoreCalculator\n\tl.RUnlock()\n\tif !ok {\n\t\treturn\n\t}\n\n\tnewScore := sc.GetScore(ps.Peer)\n\tif newScore == psScore {\n\t\treturn\n\t}\n\n\tl.Lock()\n\tl.updatePeer(ps, newScore)\n\tl.Unlock()\n}\n\n// updatePeer is called to update the score of the peer given the existing score.\n// Note that a Write lock must be held to call this function.\nfunc (l *PeerList) updatePeer(ps *peerScore, newScore uint64) {\n\tif ps.score == newScore {\n\t\treturn\n\t}\n\n\tps.score = newScore\n\tl.peerHeap.updatePeer(ps)\n}\n\n// peerScore represents a peer and scoring for the peer heap.\n// It is not safe for concurrent access, it should only be used through the PeerList.\ntype peerScore struct {\n\t*Peer\n\n\t// score according to the current peer list's ScoreCalculator.\n\tscore uint64\n\t// index of the peerScore in the peerHeap. Used to interact with container/heap.\n\tindex int\n\t// order is the tiebreaker for when score is equal. It is set when a peer\n\t// is pushed to the heap based on peerHeap.order with jitter.\n\torder uint64\n}\n\nfunc newPeerScore(p *Peer, score uint64) *peerScore {\n\treturn &peerScore{\n\t\tPeer:  p,\n\t\tscore: score,\n\t\tindex: -1,\n\t}\n}\n\n// Peer represents a single autobahn service or client with a unique host:port.\ntype Peer struct {\n\tsync.RWMutex\n\n\tchannel             Connectable\n\thostPort            string\n\tonStatusChanged     func(*Peer)\n\tonClosedConnRemoved func(*Peer)\n\n\t// scCount is the number of subchannels that this peer is added to.\n\tscCount uint32\n\n\t// connections are mutable, and are protected by the mutex.\n\tnewConnLock         sync.Mutex\n\tinboundConnections  []*Connection\n\toutboundConnections []*Connection\n\tchosenCount         atomic.Uint64\n\n\t// onUpdate is a test-only hook.\n\tonUpdate func(*Peer)\n}\n\nfunc newPeer(channel Connectable, hostPort string, onStatusChanged func(*Peer), onClosedConnRemoved func(*Peer)) *Peer {\n\tif hostPort == \"\" {\n\t\tpanic(\"Cannot create peer with blank hostPort\")\n\t}\n\tif onStatusChanged == nil {\n\t\tonStatusChanged = noopOnStatusChanged\n\t}\n\treturn &Peer{\n\t\tchannel:             channel,\n\t\thostPort:            hostPort,\n\t\tonStatusChanged:     onStatusChanged,\n\t\tonClosedConnRemoved: onClosedConnRemoved,\n\t}\n}\n\n// HostPort returns the host:port used to connect to this peer.\nfunc (p *Peer) HostPort() string {\n\treturn p.hostPort\n}\n\n// getConn treats inbound and outbound connections as a single virtual list\n// that can be indexed. The peer must be read-locked.\nfunc (p *Peer) getConn(i int) *Connection {\n\tinboundLen := len(p.inboundConnections)\n\tif i < inboundLen {\n\t\treturn p.inboundConnections[i]\n\t}\n\n\treturn p.outboundConnections[i-inboundLen]\n}\n\nfunc (p *Peer) getActiveConnLocked() (*Connection, bool) {\n\tallConns := len(p.inboundConnections) + len(p.outboundConnections)\n\tif allConns == 0 {\n\t\treturn nil, false\n\t}\n\n\t// We cycle through the connection list, starting at a random point\n\t// to avoid always choosing the same connection.\n\tvar startOffset int\n\tif allConns > 1 {\n\t\tstartOffset = peerRng.Intn(allConns)\n\t}\n\tfor i := 0; i < allConns; i++ {\n\t\tconnIndex := (i + startOffset) % allConns\n\t\tif conn := p.getConn(connIndex); conn.IsActive() {\n\t\t\treturn conn, true\n\t\t}\n\t}\n\n\treturn nil, false\n}\n\n// getActiveConn will randomly select an active connection.\n// TODO(prashant): Should we clear inactive connections?\n// TODO(prashant): Do we want some sort of scoring for connections?\nfunc (p *Peer) getActiveConn() (*Connection, bool) {\n\tp.RLock()\n\tconn, ok := p.getActiveConnLocked()\n\tp.RUnlock()\n\n\treturn conn, ok\n}\n\n// GetConnection returns an active connection to this peer. If no active connections\n// are found, it will create a new outbound connection and return it.\nfunc (p *Peer) GetConnection(ctx context.Context) (*Connection, error) {\n\tif activeConn, ok := p.getActiveConn(); ok {\n\t\treturn activeConn, nil\n\t}\n\n\t// Lock here to restrict new connection creation attempts to one goroutine\n\tp.newConnLock.Lock()\n\tdefer p.newConnLock.Unlock()\n\n\t// Check active connections again in case someone else got ahead of us.\n\tif activeConn, ok := p.getActiveConn(); ok {\n\t\treturn activeConn, nil\n\t}\n\n\t// No active connections, make a new outgoing connection.\n\treturn p.Connect(ctx)\n}\n\n// getConnectionRelay gets a connection, and uses the given timeout to lazily\n// create a context if a new connection is required.\nfunc (p *Peer) getConnectionRelay(callTimeout, relayMaxConnTimeout time.Duration) (*Connection, error) {\n\tif conn, ok := p.getActiveConn(); ok {\n\t\treturn conn, nil\n\t}\n\n\t// Lock here to restrict new connection creation attempts to one goroutine\n\tp.newConnLock.Lock()\n\tdefer p.newConnLock.Unlock()\n\n\t// Check active connections again in case someone else got ahead of us.\n\tif activeConn, ok := p.getActiveConn(); ok {\n\t\treturn activeConn, nil\n\t}\n\n\t// Use the lower timeout value of the call timeout and the relay connection timeout.\n\ttimeout := callTimeout\n\tif timeout > relayMaxConnTimeout && relayMaxConnTimeout > 0 {\n\t\ttimeout = relayMaxConnTimeout\n\t}\n\n\t// When the relay creates outbound connections, we don't want those services\n\t// to ever connect back to us and send us traffic. We hide the host:port\n\t// so that service instances on remote machines don't try to connect back\n\t// and don't try to send Hyperbahn traffic on this connection.\n\tctx, cancel := NewContextBuilder(timeout).HideListeningOnOutbound().Build()\n\tdefer cancel()\n\n\treturn p.Connect(ctx)\n}\n\n// addSC adds a reference to a peer from a subchannel (e.g. peer list).\nfunc (p *Peer) addSC() {\n\tp.Lock()\n\tp.scCount++\n\tp.Unlock()\n}\n\n// delSC removes a reference to a peer from a subchannel (e.g. peer list).\nfunc (p *Peer) delSC() {\n\tp.Lock()\n\tp.scCount--\n\tp.Unlock()\n}\n\n// canRemove returns whether this peer can be safely removed from the root peer list.\nfunc (p *Peer) canRemove() bool {\n\tp.RLock()\n\tcount := len(p.inboundConnections) + len(p.outboundConnections) + int(p.scCount)\n\tp.RUnlock()\n\treturn count == 0\n}\n\n// addConnection adds an active connection to the peer's connection list.\n// If a connection is not active, returns ErrInvalidConnectionState.\nfunc (p *Peer) addConnection(c *Connection, direction connectionDirection) error {\n\tconns := p.connectionsFor(direction)\n\n\tif c.readState() != connectionActive {\n\t\treturn ErrInvalidConnectionState\n\t}\n\n\tp.Lock()\n\t*conns = append(*conns, c)\n\tp.Unlock()\n\n\t// Inform third parties that a peer gained a connection.\n\tp.onStatusChanged(p)\n\n\treturn nil\n}\n\nfunc (p *Peer) connectionsFor(direction connectionDirection) *[]*Connection {\n\tif direction == inbound {\n\t\treturn &p.inboundConnections\n\t}\n\treturn &p.outboundConnections\n}\n\n// removeConnection will check remove the connection if it exists on connsPtr\n// and returns whether it removed the connection.\nfunc (p *Peer) removeConnection(connsPtr *[]*Connection, changed *Connection) bool {\n\tconns := *connsPtr\n\tfor i, c := range conns {\n\t\tif c == changed {\n\t\t\t// Remove the connection by moving the last item forward, and slicing the list.\n\t\t\tlast := len(conns) - 1\n\t\t\tconns[i], conns[last] = conns[last], nil\n\t\t\t*connsPtr = conns[:last]\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// connectionStateChanged is called when one of the peers' connections states changes.\n// All non-active connections are removed from the peer. The connection will\n// still be tracked by the channel until it's completely closed.\nfunc (p *Peer) connectionCloseStateChange(changed *Connection) {\n\tif changed.IsActive() {\n\t\treturn\n\t}\n\n\tp.Lock()\n\tfound := p.removeConnection(&p.inboundConnections, changed)\n\tif !found {\n\t\tfound = p.removeConnection(&p.outboundConnections, changed)\n\t}\n\tp.Unlock()\n\n\tif found {\n\t\tp.onClosedConnRemoved(p)\n\t\t// Inform third parties that a peer lost a connection.\n\t\tp.onStatusChanged(p)\n\t}\n}\n\n// Connect adds a new outbound connection to the peer.\nfunc (p *Peer) Connect(ctx context.Context) (*Connection, error) {\n\treturn p.channel.Connect(ctx, p.hostPort)\n}\n\n// BeginCall starts a new call to this specific peer, returning an OutboundCall that can\n// be used to write the arguments of the call.\nfunc (p *Peer) BeginCall(ctx context.Context, serviceName, methodName string, callOptions *CallOptions) (*OutboundCall, error) {\n\tif callOptions == nil {\n\t\tcallOptions = defaultCallOptions\n\t}\n\tcallOptions.RequestState.AddSelectedPeer(p.HostPort())\n\n\tif err := validateCall(ctx, serviceName, methodName, callOptions); err != nil {\n\t\treturn nil, err\n\t}\n\n\tconn, err := p.GetConnection(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcall, err := conn.beginCall(ctx, serviceName, methodName, callOptions)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn call, err\n}\n\n// NumConnections returns the number of inbound and outbound connections for this peer.\nfunc (p *Peer) NumConnections() (inbound int, outbound int) {\n\tp.RLock()\n\tinbound = len(p.inboundConnections)\n\toutbound = len(p.outboundConnections)\n\tp.RUnlock()\n\treturn inbound, outbound\n}\n\n// NumPendingOutbound returns the number of pending outbound calls.\nfunc (p *Peer) NumPendingOutbound() int {\n\tcount := 0\n\tp.RLock()\n\tfor _, c := range p.outboundConnections {\n\t\tcount += c.outbound.count()\n\t}\n\n\tfor _, c := range p.inboundConnections {\n\t\tcount += c.outbound.count()\n\t}\n\tp.RUnlock()\n\treturn count\n}\n\nfunc (p *Peer) runWithConnections(f func(*Connection)) {\n\tp.RLock()\n\tfor _, c := range p.inboundConnections {\n\t\tf(c)\n\t}\n\n\tfor _, c := range p.outboundConnections {\n\t\tf(c)\n\t}\n\tp.RUnlock()\n}\n\nfunc (p *Peer) callOnUpdateComplete() {\n\tp.RLock()\n\tf := p.onUpdate\n\tp.RUnlock()\n\n\tif f != nil {\n\t\tf(p)\n\t}\n}\n\nfunc noopOnStatusChanged(*Peer) {}\n\n// isEphemeralHostPort returns if hostPort is the default ephemeral hostPort.\nfunc isEphemeralHostPort(hostPort string) bool {\n\treturn hostPort == \"\" || hostPort == ephemeralHostPort || strings.HasSuffix(hostPort, \":0\")\n}\n"
  },
  {
    "path": "peer_bench_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc benchmarkGetConnection(b *testing.B, numIncoming, numOutgoing int) {\n\tctx, cancel := NewContext(10 * time.Second)\n\tdefer cancel()\n\n\ts1 := testutils.NewServer(b, nil)\n\ts2 := testutils.NewServer(b, nil)\n\tdefer s1.Close()\n\tdefer s2.Close()\n\n\tfor i := 0; i < numOutgoing; i++ {\n\t\t_, err := s1.Connect(ctx, s2.PeerInfo().HostPort)\n\t\trequire.NoError(b, err, \"Connect from s1 -> s2 failed\")\n\t}\n\tfor i := 0; i < numIncoming; i++ {\n\t\t_, err := s2.Connect(ctx, s1.PeerInfo().HostPort)\n\t\trequire.NoError(b, err, \"Connect from s2 -> s1 failed\")\n\t}\n\n\tpeer := s1.Peers().GetOrAdd(s2.PeerInfo().HostPort)\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tpeer.GetConnection(ctx)\n\t}\n}\n\nfunc BenchmarkGetConnection0In1Out(b *testing.B) { benchmarkGetConnection(b, 0, 1) }\nfunc BenchmarkGetConnection1In0Out(b *testing.B) { benchmarkGetConnection(b, 1, 0) }\nfunc BenchmarkGetConnection5In5Out(b *testing.B) { benchmarkGetConnection(b, 5, 5) }\n"
  },
  {
    "path": "peer_heap.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"container/heap\"\n\t\"math/rand\"\n\n\t\"github.com/uber/tchannel-go/trand\"\n)\n\n// peerHeap maintains a min-heap of peers based on the peers' score. All method\n// calls must be serialized externally.\ntype peerHeap struct {\n\tpeerScores []*peerScore\n\trng        *rand.Rand\n\torder      uint64\n}\n\nfunc newPeerHeap() *peerHeap {\n\treturn &peerHeap{rng: trand.NewSeeded()}\n}\n\nfunc (ph peerHeap) Len() int { return len(ph.peerScores) }\n\nfunc (ph *peerHeap) Less(i, j int) bool {\n\tif ph.peerScores[i].score == ph.peerScores[j].score {\n\t\treturn ph.peerScores[i].order < ph.peerScores[j].order\n\t}\n\treturn ph.peerScores[i].score < ph.peerScores[j].score\n}\n\nfunc (ph peerHeap) Swap(i, j int) {\n\tph.peerScores[i], ph.peerScores[j] = ph.peerScores[j], ph.peerScores[i]\n\tph.peerScores[i].index = i\n\tph.peerScores[j].index = j\n}\n\n// Push implements heap Push interface\nfunc (ph *peerHeap) Push(x interface{}) {\n\tn := len(ph.peerScores)\n\titem := x.(*peerScore)\n\titem.index = n\n\tph.peerScores = append(ph.peerScores, item)\n}\n\n// Pop implements heap Pop interface\nfunc (ph *peerHeap) Pop() interface{} {\n\told := *ph\n\tn := len(old.peerScores)\n\titem := old.peerScores[n-1]\n\titem.index = -1 // for safety\n\tph.peerScores = old.peerScores[:n-1]\n\treturn item\n}\n\n// updatePeer updates the score for the given peer.\nfunc (ph *peerHeap) updatePeer(peerScore *peerScore) {\n\theap.Fix(ph, peerScore.index)\n}\n\n// removePeer remove peer at specific index.\nfunc (ph *peerHeap) removePeer(peerScore *peerScore) {\n\theap.Remove(ph, peerScore.index)\n}\n\n// popPeer pops the top peer of the heap.\nfunc (ph *peerHeap) popPeer() *peerScore {\n\treturn heap.Pop(ph).(*peerScore)\n}\n\n// pushPeer pushes the new peer into the heap.\nfunc (ph *peerHeap) pushPeer(peerScore *peerScore) {\n\tph.order++\n\tnewOrder := ph.order\n\t// randRange will affect the deviation of peer's chosenCount\n\trandRange := ph.Len()/2 + 1\n\tpeerScore.order = newOrder + uint64(ph.rng.Intn(randRange))\n\theap.Push(ph, peerScore)\n}\n\nfunc (ph *peerHeap) swapOrder(i, j int) {\n\tif i == j {\n\t\treturn\n\t}\n\n\tph.peerScores[i].order, ph.peerScores[j].order = ph.peerScores[j].order, ph.peerScores[i].order\n\theap.Fix(ph, i)\n\theap.Fix(ph, j)\n}\n\n// AddPeer adds a peer to the peer heap.\nfunc (ph *peerHeap) addPeer(peerScore *peerScore) {\n\tph.pushPeer(peerScore)\n\n\t// Pick a random element, and swap the order with that peerScore.\n\tr := ph.rng.Intn(ph.Len())\n\tph.swapOrder(peerScore.index, r)\n}\n\n// Exposed for testing purposes.\nfunc (ph *peerHeap) peek() *peerScore {\n\treturn ph.peerScores[0]\n}\n"
  },
  {
    "path": "peer_heap_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"math\"\n\t\"math/rand\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestPeerHeap(t *testing.T) {\n\tconst numPeers = 10\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\n\tpeerHeap := newPeerHeap()\n\n\tpeerScores := make([]*peerScore, numPeers)\n\tminScore := uint64(math.MaxInt64)\n\tfor i := 0; i < numPeers; i++ {\n\t\tps := newPeerScore(&Peer{}, uint64(r.Intn(numPeers*5)))\n\t\tpeerScores[i] = ps\n\t\tif ps.score < minScore {\n\t\t\tminScore = ps.score\n\t\t}\n\t}\n\n\tfor i := 0; i < numPeers; i++ {\n\t\tpeerHeap.pushPeer(peerScores[i])\n\t}\n\n\tassert.Equal(t, numPeers, peerHeap.Len(), \"Incorrect peer heap numPeers\")\n\tassert.Equal(t, minScore, peerHeap.peek().score, \"peerHeap top peer is not minimum\")\n\n\tlastScore := peerHeap.popPeer().score\n\tfor i := 1; i < numPeers; i++ {\n\t\tassert.Equal(t, numPeers-i, peerHeap.Len(), \"Incorrect peer heap numPeers\")\n\t\tscore := peerHeap.popPeer().score\n\t\tassert.True(t, score >= lastScore, \"The order of the heap is invalid\")\n\t\tlastScore = score\n\t}\n}\n"
  },
  {
    "path": "peer_internal_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestIsEphemeralHostPort(t *testing.T) {\n\ttests := []struct {\n\t\thostPort string\n\t\twant     bool\n\t}{\n\t\t{\"\", true},\n\t\t{ephemeralHostPort, true},\n\t\t{\"127.0.0.1:0\", true},\n\t\t{\"10.1.1.1:0\", true},\n\t\t{\"127.0.0.1:1\", false},\n\t\t{\"10.1.1.1:1\", false},\n\t}\n\n\tfor _, tt := range tests {\n\t\tgot := isEphemeralHostPort(tt.hostPort)\n\t\tassert.Equal(t, tt.want, got, \"Unexpected result for %q\", tt.hostPort)\n\t}\n}\n"
  },
  {
    "path": "peer_strategies.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport \"math\"\n\n// ScoreCalculator defines the interface to calculate the score.\ntype ScoreCalculator interface {\n\tGetScore(p *Peer) uint64\n}\n\n// ScoreCalculatorFunc is an adapter that allows functions to be used as ScoreCalculator\ntype ScoreCalculatorFunc func(p *Peer) uint64\n\n// GetScore calls the underlying function.\nfunc (f ScoreCalculatorFunc) GetScore(p *Peer) uint64 {\n\treturn f(p)\n}\n\ntype zeroCalculator struct{}\n\nfunc (zeroCalculator) GetScore(p *Peer) uint64 {\n\treturn 0\n}\n\nfunc newZeroCalculator() zeroCalculator {\n\treturn zeroCalculator{}\n}\n\ntype leastPendingCalculator struct{}\n\nfunc (leastPendingCalculator) GetScore(p *Peer) uint64 {\n\tinbound, outbound := p.NumConnections()\n\tif inbound+outbound == 0 {\n\t\treturn math.MaxUint64\n\t}\n\n\treturn uint64(p.NumPendingOutbound())\n}\n\n// newLeastPendingCalculator returns a strategy prefers any connected peer.\n// Within connected peers, least pending calls is used. Peers with less pending outbound calls\n// get a smaller score.\nfunc newLeastPendingCalculator() leastPendingCalculator {\n\treturn leastPendingCalculator{}\n}\n\ntype preferIncomingCalculator struct{}\n\nfunc (preferIncomingCalculator) GetScore(p *Peer) uint64 {\n\tinbound, outbound := p.NumConnections()\n\tif inbound+outbound == 0 {\n\t\treturn math.MaxUint64\n\t}\n\n\tnumPendingOutbound := uint64(p.NumPendingOutbound())\n\tif inbound == 0 {\n\t\treturn math.MaxInt32 + numPendingOutbound\n\t}\n\n\treturn numPendingOutbound\n}\n\n// newPreferIncomingCalculator returns a strategy that prefers peers with incoming connections.\n// The scoring tiers are:\n// Peers with incoming connections, peers with any connections, unconnected peers.\n// Within each tier, least pending calls is used. Peers with less pending outbound calls\n// get a smaller score.\nfunc newPreferIncomingCalculator() preferIncomingCalculator {\n\treturn preferIncomingCalculator{}\n}\n"
  },
  {
    "path": "peer_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/benchmark\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/atomic\"\n)\n\nfunc fakePeer(t *testing.T, ch *Channel, hostPort string) *Peer {\n\tch.Peers().Add(hostPort)\n\n\tpeer, err := ch.Peers().Get(nil)\n\trequire.NoError(t, err, \"Unexpected error getting peer from heap.\")\n\trequire.Equal(t, hostPort, peer.HostPort(), \"Got unexpected peer.\")\n\n\tin, out := peer.NumConnections()\n\trequire.Equal(t, 0, in, \"Expected new peer to have no incoming connections.\")\n\trequire.Equal(t, 0, out, \"Expected new peer to have no outgoing connections.\")\n\n\treturn peer\n}\n\nfunc assertNumConnections(t *testing.T, peer *Peer, in, out int) {\n\tactualIn, actualOut := peer.NumConnections()\n\tassert.Equal(t, actualIn, in, \"Expected %v incoming connection.\", in)\n\tassert.Equal(t, actualOut, out, \"Expected %v outgoing connection.\", out)\n}\n\nfunc TestGetPeerNoPeer(t *testing.T) {\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\tpeer, err := ch.Peers().Get(nil)\n\tassert.Equal(t, ErrNoPeers, err, \"Empty peer list should return error\")\n\tassert.Nil(t, peer, \"should not return peer\")\n}\n\nfunc TestGetPeerSinglePeer(t *testing.T) {\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\tch.Peers().Add(\"1.1.1.1:1234\")\n\n\tpeer, err := ch.Peers().Get(nil)\n\tassert.NoError(t, err, \"peer list should return contained element\")\n\tassert.Equal(t, \"1.1.1.1:1234\", peer.HostPort(), \"returned peer mismatch\")\n}\n\nfunc TestPeerUpdatesLen(t *testing.T) {\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\tassert.Zero(t, ch.Peers().Len())\n\tfor i := 1; i < 5; i++ {\n\t\tch.Peers().Add(fmt.Sprintf(\"1.1.1.1:%d\", i))\n\t\tassert.Equal(t, ch.Peers().Len(), i)\n\t}\n\tfor i := 4; i > 0; i-- {\n\t\tassert.Equal(t, ch.Peers().Len(), i)\n\t\tch.Peers().Remove(fmt.Sprintf(\"1.1.1.1:%d\", i))\n\t}\n\tassert.Zero(t, ch.Peers().Len())\n}\n\nfunc TestGetPeerAvoidPrevSelected(t *testing.T) {\n\tconst (\n\t\tpeer1 = \"1.1.1.1:1\"\n\t\tpeer2 = \"2.2.2.2:2\"\n\t\tpeer3 = \"3.3.3.3:3\"\n\t\tpeer4 = \"3.3.3.3:4\"\n\t)\n\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\ta, m := testutils.StrArray, testutils.StrMap\n\ttests := []struct {\n\t\tmsg          string\n\t\tpeers        []string\n\t\tprevSelected []string\n\t\texpected     map[string]struct{}\n\t}{\n\t\t{\n\t\t\tmsg:      \"no prevSelected\",\n\t\t\tpeers:    a(peer1),\n\t\t\texpected: m(peer1),\n\t\t},\n\t\t{\n\t\t\tmsg:          \"ignore single hostPort in prevSelected\",\n\t\t\tpeers:        a(peer1, peer2),\n\t\t\tprevSelected: a(peer1),\n\t\t\texpected:     m(peer2),\n\t\t},\n\t\t{\n\t\t\tmsg:          \"ignore multiple hostPorts in prevSelected\",\n\t\t\tpeers:        a(peer1, peer2, peer3),\n\t\t\tprevSelected: a(peer1, peer2),\n\t\t\texpected:     m(peer3),\n\t\t},\n\t\t{\n\t\t\tmsg:          \"only peer is in prevSelected\",\n\t\t\tpeers:        a(peer1),\n\t\t\tprevSelected: a(peer1),\n\t\t\texpected:     m(peer1),\n\t\t},\n\t\t{\n\t\t\tmsg:          \"all peers are in prevSelected\",\n\t\t\tpeers:        a(peer1, peer2, peer3),\n\t\t\tprevSelected: a(peer1, peer2, peer3),\n\t\t\texpected:     m(peer1, peer2, peer3),\n\t\t},\n\t\t{\n\t\t\tmsg:          \"prevSelected host should be ignored\",\n\t\t\tpeers:        a(peer1, peer3, peer4),\n\t\t\tprevSelected: a(peer3),\n\t\t\texpected:     m(peer1),\n\t\t},\n\t\t{\n\t\t\tmsg:          \"prevSelected only has single host\",\n\t\t\tpeers:        a(peer3, peer4),\n\t\t\tprevSelected: a(peer3),\n\t\t\texpected:     m(peer4),\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tpeers := ch.GetSubChannel(fmt.Sprintf(\"test%d\", i), Isolated).Peers()\n\t\tfor _, p := range tt.peers {\n\t\t\tpeers.Add(p)\n\t\t}\n\n\t\trs := &RequestState{}\n\t\tfor _, selected := range tt.prevSelected {\n\t\t\trs.AddSelectedPeer(selected)\n\t\t}\n\n\t\tgotPeer, err := peers.Get(rs.PrevSelectedPeers())\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Got unexpected error selecting peer: %v\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tnewPeer, err := peers.GetNew(rs.PrevSelectedPeers())\n\t\tif len(tt.peers) == len(tt.prevSelected) {\n\t\t\tif newPeer != nil || err != ErrNoNewPeers {\n\t\t\t\tt.Errorf(\"%s: newPeer should not have been found %v: %v\\n\", tt.msg, newPeer, err)\n\t\t\t}\n\t\t} else {\n\t\t\tif gotPeer != newPeer || err != nil {\n\t\t\t\tt.Errorf(\"%s: expected equal peers, got %v new %v: %v\\n\",\n\t\t\t\t\ttt.msg, gotPeer, newPeer, err)\n\t\t\t}\n\t\t}\n\n\t\tgot := gotPeer.HostPort()\n\t\tif _, ok := tt.expected[got]; !ok {\n\t\t\tt.Errorf(\"%s: got unexpected peer, expected one of %v got %v\\n  Peers = %v PrevSelected = %v\",\n\t\t\t\ttt.msg, tt.expected, got, tt.peers, tt.prevSelected)\n\t\t}\n\t}\n}\n\nfunc TestPeerRemoveClosedConnection(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tWithVerifiedServer(t, nil, func(ch *Channel, hostPort string) {\n\t\tclient := testutils.NewClient(t, nil)\n\t\tdefer client.Close()\n\n\t\tp := client.Peers().Add(hostPort)\n\n\t\tc1, err := p.Connect(ctx)\n\t\trequire.NoError(t, err, \"Failed to connect\")\n\t\trequire.NoError(t, err, c1.Ping(ctx))\n\n\t\tc2, err := p.Connect(ctx)\n\t\trequire.NoError(t, err, \"Failed to connect\")\n\t\trequire.NoError(t, err, c2.Ping(ctx))\n\n\t\trequire.NoError(t, c1.Close(), \"Failed to close first connection\")\n\t\t_, outConns := p.NumConnections()\n\t\tassert.Equal(t, 1, outConns, \"Expected 1 remaining outgoing connection\")\n\n\t\tc, err := p.GetConnection(ctx)\n\t\trequire.NoError(t, err, \"GetConnection failed\")\n\t\tassert.Equal(t, c2, c, \"Expected second active connection\")\n\t})\n}\n\nfunc TestPeerConnectCancelled(t *testing.T) {\n\tWithVerifiedServer(t, nil, func(ch *Channel, hostPort string) {\n\t\tctx, cancel := NewContext(100 * time.Millisecond)\n\t\tcancel()\n\n\t\t_, err := ch.Connect(ctx, \"10.255.255.1:1\")\n\t\trequire.Error(t, err, \"Connect should fail\")\n\t\tassert.EqualError(t, err, ErrRequestCancelled.Error(), \"Unexpected error\")\n\t})\n}\n\nfunc TestPeerGetConnectionWithNoActiveConnections(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tWithVerifiedServer(t, nil, func(ch *Channel, hostPort string) {\n\t\tclient := testutils.NewClient(t, nil)\n\t\tdefer client.Close()\n\n\t\tvar (\n\t\t\twg          sync.WaitGroup\n\t\t\tlock        sync.Mutex\n\t\t\tconn        *Connection\n\t\t\tconcurrency = 10\n\t\t\tp           = client.Peers().Add(hostPort)\n\t\t)\n\n\t\tfor i := 0; i < concurrency; i++ {\n\t\t\twg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tc, err := p.GetConnection(ctx)\n\t\t\t\trequire.NoError(t, err, \"GetConnection failed\")\n\n\t\t\t\tlock.Lock()\n\t\t\t\tdefer lock.Unlock()\n\n\t\t\t\tif conn == nil {\n\t\t\t\t\tconn = c\n\t\t\t\t} else {\n\t\t\t\t\tassert.Equal(t, conn, c, \"Expected the same active connection\")\n\t\t\t\t}\n\n\t\t\t}()\n\t\t}\n\n\t\twg.Wait()\n\n\t\t_, outbound := p.NumConnections()\n\t\tassert.Equal(t, 1, outbound, \"Expected 1 active outbound connetion\")\n\t})\n}\n\nfunc TestInboundEphemeralPeerRemoved(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\t// No relay, since we look for the exact host:port in peer lists.\n\topts := testutils.NewOpts().NoRelay()\n\tWithVerifiedServer(t, opts, func(ch *Channel, hostPort string) {\n\t\tclient := testutils.NewClient(t, nil)\n\t\tassert.NoError(t, client.Ping(ctx, hostPort), \"Ping to server failed\")\n\n\t\t// Server should have a host:port in the root peers for the client.\n\t\tvar clientHP string\n\t\tpeers := ch.RootPeers().Copy()\n\t\tfor k := range peers {\n\t\t\tclientHP = k\n\t\t}\n\n\t\twaitTillInboundEmpty(t, ch, clientHP, func() {\n\t\t\tclient.Close()\n\t\t})\n\t\tassert.Equal(t, ChannelClosed, client.State(), \"Client should be closed\")\n\n\t\t_, ok := ch.RootPeers().Get(clientHP)\n\t\tassert.False(t, ok, \"server's root peers should remove peer for client on close\")\n\t})\n}\n\nfunc TestOutboundEphemeralPeerRemoved(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tWithVerifiedServer(t, nil, func(ch *Channel, hostPort string) {\n\t\toutbound := testutils.NewServer(t, testutils.NewOpts().SetServiceName(\"asd\t\"))\n\t\tassert.NoError(t, ch.Ping(ctx, outbound.PeerInfo().HostPort), \"Ping to outbound failed\")\n\t\toutboundHP := outbound.PeerInfo().HostPort\n\n\t\t// Server should have a peer for hostPort that should be gone.\n\t\twaitTillNConnections(t, ch, outboundHP, 0, 0, func() {\n\t\t\toutbound.Close()\n\t\t})\n\t\tassert.Equal(t, ChannelClosed, outbound.State(), \"Outbound should be closed\")\n\n\t\t_, ok := ch.RootPeers().Get(outboundHP)\n\t\tassert.False(t, ok, \"server's root peers should remove outbound peer\")\n\t})\n}\n\nfunc TestOutboundPeerNotAdded(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tWithVerifiedServer(t, nil, func(server *Channel, hostPort string) {\n\t\tserver.Register(raw.Wrap(newTestHandler(t)), \"echo\")\n\n\t\tch := testutils.NewClient(t, nil)\n\t\tdefer ch.Close()\n\n\t\tch.Ping(ctx, hostPort)\n\t\traw.Call(ctx, ch, hostPort, server.PeerInfo().ServiceName, \"echo\", nil, nil)\n\n\t\tpeer, err := ch.Peers().Get(nil)\n\t\tassert.Equal(t, ErrNoPeers, err, \"Ping should not add peers\")\n\t\tassert.Nil(t, peer, \"Expected no peer to be returned\")\n\t})\n}\n\nfunc TestRemovePeerNotFound(t *testing.T) {\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\n\tpeers := ch.Peers()\n\tpeers.Add(\"1.1.1.1:1\")\n\tassert.Error(t, peers.Remove(\"not-found\"), \"Remove should fa\")\n\tassert.NoError(t, peers.Remove(\"1.1.1.1:1\"), \"Remove shouldn't fail for existing peer\")\n}\n\nfunc TestPeerRemovedFromRootPeers(t *testing.T) {\n\ttests := []struct {\n\t\taddHostPort    bool\n\t\tremoveHostPort bool\n\t\texpectFound    bool\n\t}{\n\t\t{\n\t\t\taddHostPort: true,\n\t\t\texpectFound: true,\n\t\t},\n\t\t{\n\t\t\taddHostPort:    true,\n\t\t\tremoveHostPort: true,\n\t\t\texpectFound:    false,\n\t\t},\n\t\t{\n\t\t\taddHostPort: false,\n\t\t\texpectFound: false,\n\t\t},\n\t}\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tfor _, tt := range tests {\n\t\topts := testutils.NewOpts().NoRelay()\n\t\tWithVerifiedServer(t, opts, func(server *Channel, hostPort string) {\n\t\t\tch := testutils.NewServer(t, nil)\n\t\t\tclientHP := ch.PeerInfo().HostPort\n\n\t\t\tif tt.addHostPort {\n\t\t\t\tserver.Peers().Add(clientHP)\n\t\t\t}\n\n\t\t\tassert.NoError(t, ch.Ping(ctx, hostPort), \"Ping failed\")\n\n\t\t\tif tt.removeHostPort {\n\t\t\t\trequire.NoError(t, server.Peers().Remove(clientHP), \"Failed to remove peer\")\n\t\t\t}\n\n\t\t\twaitTillInboundEmpty(t, server, clientHP, func() {\n\t\t\t\tch.Close()\n\t\t\t})\n\n\t\t\trootPeers := server.RootPeers()\n\t\t\t_, found := rootPeers.Get(clientHP)\n\t\t\tassert.Equal(t, tt.expectFound, found, \"Peer found mismatch, addHostPort: %v\", tt.addHostPort)\n\t\t})\n\t}\n}\n\nfunc TestPeerSelectionConnClosed(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tWithVerifiedServer(t, nil, func(server *Channel, hostPort string) {\n\t\tclient := testutils.NewServer(t, nil)\n\t\tdefer client.Close()\n\n\t\t// Ping will create an outbound connection from client -> server.\n\t\trequire.NoError(t, testutils.Ping(client, server), \"Ping failed\")\n\n\t\twaitTillInboundEmpty(t, server, client.PeerInfo().HostPort, func() {\n\t\t\tpeer, ok := client.RootPeers().Get(server.PeerInfo().HostPort)\n\t\t\trequire.True(t, ok, \"Client has no peer for %v\", server.PeerInfo())\n\n\t\t\tconn, err := peer.GetConnection(ctx)\n\t\t\trequire.NoError(t, err, \"Failed to get a connection\")\n\t\t\tconn.Close()\n\t\t})\n\n\t\t// Make sure the closed connection is not used.\n\t\tfor i := 0; i < 10; i++ {\n\t\t\trequire.NoError(t, testutils.Ping(client, server), \"Ping failed\")\n\t\t}\n\t})\n}\n\nfunc TestPeerSelectionPreferIncoming(t *testing.T) {\n\ttests := []struct {\n\t\tnumIncoming, numOutgoing, numUnconnected int\n\t\tisolated                                 bool\n\t\texpectedIncoming                         int\n\t\texpectedOutgoing                         int\n\t\texpectedUnconnected                      int\n\t}{\n\t\t{\n\t\t\tnumIncoming:      5,\n\t\t\tnumOutgoing:      5,\n\t\t\tnumUnconnected:   5,\n\t\t\texpectedIncoming: 5,\n\t\t},\n\t\t{\n\t\t\tnumOutgoing:      5,\n\t\t\tnumUnconnected:   5,\n\t\t\texpectedOutgoing: 5,\n\t\t},\n\t\t{\n\t\t\tnumUnconnected:      5,\n\t\t\texpectedUnconnected: 5,\n\t\t},\n\t\t{\n\t\t\tnumIncoming:      5,\n\t\t\tnumOutgoing:      5,\n\t\t\tnumUnconnected:   5,\n\t\t\tisolated:         true,\n\t\t\texpectedIncoming: 5,\n\t\t\texpectedOutgoing: 5,\n\t\t},\n\t\t{\n\t\t\tnumOutgoing:      5,\n\t\t\tnumUnconnected:   5,\n\t\t\tisolated:         true,\n\t\t\texpectedOutgoing: 5,\n\t\t},\n\t\t{\n\t\t\tnumIncoming:      5,\n\t\t\tnumUnconnected:   5,\n\t\t\tisolated:         true,\n\t\t\texpectedIncoming: 5,\n\t\t},\n\t\t{\n\t\t\tnumUnconnected:      5,\n\t\t\tisolated:            true,\n\t\t\texpectedUnconnected: 5,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\t// We need to directly connect from the server to the client and verify\n\t\t// the exact peers.\n\t\topts := testutils.NewOpts().NoRelay()\n\t\tWithVerifiedServer(t, opts, func(ch *Channel, hostPort string) {\n\t\t\tctx, cancel := NewContext(time.Second)\n\t\t\tdefer cancel()\n\n\t\t\tselectedIncoming := make(map[string]int)\n\t\t\tselectedOutgoing := make(map[string]int)\n\t\t\tselectedUnconnected := make(map[string]int)\n\n\t\t\tpeers := ch.Peers()\n\t\t\tif tt.isolated {\n\t\t\t\tpeers = ch.GetSubChannel(\"isolated\", Isolated).Peers()\n\t\t\t}\n\n\t\t\t// 5 peers that make incoming connections to ch.\n\t\t\tfor i := 0; i < tt.numIncoming; i++ {\n\t\t\t\tincoming, _, incomingHP := NewServer(t, &testutils.ChannelOpts{ServiceName: fmt.Sprintf(\"incoming%d\", i)})\n\t\t\t\tdefer incoming.Close()\n\t\t\t\tassert.NoError(t, incoming.Ping(ctx, ch.PeerInfo().HostPort), \"Ping failed\")\n\t\t\t\tpeers.Add(incomingHP)\n\t\t\t\tselectedIncoming[incomingHP] = 0\n\t\t\t}\n\n\t\t\t// 5 random peers that don't have any connections.\n\t\t\tfor i := 0; i < tt.numUnconnected; i++ {\n\t\t\t\thp := fmt.Sprintf(\"1.1.1.1:1%d\", i)\n\t\t\t\tpeers.Add(hp)\n\t\t\t\tselectedUnconnected[hp] = 0\n\t\t\t}\n\n\t\t\t// 5 random peers that we have outgoing connections to.\n\t\t\tfor i := 0; i < tt.numOutgoing; i++ {\n\t\t\t\toutgoing, _, outgoingHP := NewServer(t, &testutils.ChannelOpts{ServiceName: fmt.Sprintf(\"outgoing%d\", i)})\n\t\t\t\tdefer outgoing.Close()\n\t\t\t\tassert.NoError(t, ch.Ping(ctx, outgoingHP), \"Ping failed\")\n\t\t\t\tpeers.Add(outgoingHP)\n\t\t\t\tselectedOutgoing[outgoingHP] = 0\n\t\t\t}\n\n\t\t\tvar mu sync.Mutex\n\t\t\tcheckMap := func(m map[string]int, peer string) bool {\n\t\t\t\tmu.Lock()\n\t\t\t\tdefer mu.Unlock()\n\n\t\t\t\tif _, ok := m[peer]; !ok {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\n\t\t\t\tm[peer]++\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tnumSelectedPeers := func(m map[string]int) int {\n\t\t\t\tcount := 0\n\t\t\t\tfor _, v := range m {\n\t\t\t\t\tif v > 0 {\n\t\t\t\t\t\tcount++\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn count\n\t\t\t}\n\n\t\t\tpeerCheck := func() {\n\t\t\t\tfor i := 0; i < 100; i++ {\n\t\t\t\t\tpeer, err := peers.Get(nil)\n\t\t\t\t\tif assert.NoError(t, err, \"Peers.Get failed\") {\n\t\t\t\t\t\tpeerHP := peer.HostPort()\n\t\t\t\t\t\tinMap := checkMap(selectedIncoming, peerHP) ||\n\t\t\t\t\t\t\tcheckMap(selectedOutgoing, peerHP) ||\n\t\t\t\t\t\t\tcheckMap(selectedUnconnected, peerHP)\n\t\t\t\t\t\tassert.True(t, inMap, \"Couldn't find peer %v in any of our maps\", peerHP)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Now select peers in parallel\n\t\t\tvar wg sync.WaitGroup\n\t\t\tfor i := 0; i < 10; i++ {\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func() {\n\t\t\t\t\tdefer wg.Done()\n\t\t\t\t\tpeerCheck()\n\t\t\t\t}()\n\t\t\t}\n\t\t\twg.Wait()\n\n\t\t\tassert.Equal(t, tt.expectedIncoming, numSelectedPeers(selectedIncoming),\n\t\t\t\t\"Selected incoming mismatch: %v\", selectedIncoming)\n\t\t\tassert.Equal(t, tt.expectedOutgoing, numSelectedPeers(selectedOutgoing),\n\t\t\t\t\"Selected outgoing mismatch: %v\", selectedOutgoing)\n\t\t\tassert.Equal(t, tt.expectedUnconnected, numSelectedPeers(selectedUnconnected),\n\t\t\t\t\"Selected unconnected mismatch: %v\", selectedUnconnected)\n\t\t})\n\t}\n}\n\ntype peerTest struct {\n\tt        testing.TB\n\tchannels []*Channel\n}\n\n// NewService will return a new server channel and the host port.\nfunc (pt *peerTest) NewService(t testing.TB, svcName, processName string) (*Channel, string) {\n\topts := testutils.NewOpts().SetServiceName(svcName).SetProcessName(processName)\n\tch := testutils.NewServer(t, opts)\n\tpt.channels = append(pt.channels, ch)\n\treturn ch, ch.PeerInfo().HostPort\n}\n\n// CleanUp will clean up all channels started as part of the peer test.\nfunc (pt *peerTest) CleanUp() {\n\tfor _, ch := range pt.channels {\n\t\tch.Close()\n\t}\n}\n\nfunc TestPeerSelection(t *testing.T) {\n\tpt := &peerTest{t: t}\n\tdefer pt.CleanUp()\n\tWithVerifiedServer(t, &testutils.ChannelOpts{ServiceName: \"S1\"}, func(ch *Channel, hostPort string) {\n\t\tdoPing := func(ch *Channel) {\n\t\t\tctx, cancel := NewContext(time.Second)\n\t\t\tdefer cancel()\n\t\t\tassert.NoError(t, ch.Ping(ctx, hostPort), \"Ping failed\")\n\t\t}\n\n\t\tstrategy, count := createScoreStrategy(0, 1)\n\t\ts2, _ := pt.NewService(t, \"S2\", \"S2\")\n\t\tdefer s2.Close()\n\n\t\ts2.GetSubChannel(\"S1\").Peers().SetStrategy(strategy)\n\t\ts2.GetSubChannel(\"S1\").Peers().Add(hostPort)\n\t\tdoPing(s2)\n\t\tassert.EqualValues(t, 4, count.Load(),\n\t\t\t\"Expect 4 exchange updates: peer add, new conn, ping, pong\")\n\t})\n}\n\nfunc getAllPeers(t *testing.T, pl *PeerList) []string {\n\tprevSelected := make(map[string]struct{})\n\tvar got []string\n\n\tfor {\n\t\tpeer, err := pl.Get(prevSelected)\n\t\trequire.NoError(t, err, \"Peer.Get failed\")\n\n\t\thp := peer.HostPort()\n\t\tif _, ok := prevSelected[hp]; ok {\n\t\t\tbreak\n\t\t}\n\n\t\tprevSelected[hp] = struct{}{}\n\t\tgot = append(got, hp)\n\t}\n\n\treturn got\n}\n\nfunc reverse(s []string) {\n\tfor i := 0; i < len(s)/2; i++ {\n\t\tj := len(s) - i - 1\n\t\ts[i], s[j] = s[j], s[i]\n\t}\n}\n\nfunc TestIsolatedPeerHeap(t *testing.T) {\n\tconst numPeers = 10\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\n\tps1 := createSubChannelWNewStrategy(ch, \"S1\", numPeers, 1)\n\tps2 := createSubChannelWNewStrategy(ch, \"S2\", numPeers, -1, Isolated)\n\n\thostports := make([]string, numPeers)\n\tfor i := 0; i < numPeers; i++ {\n\t\thostports[i] = fmt.Sprintf(\"127.0.0.1:%d\", i)\n\t\tps1.Add(hostports[i])\n\t\tps2.Add(hostports[i])\n\t}\n\n\tps1Expected := append([]string(nil), hostports...)\n\tassert.Equal(t, ps1Expected, getAllPeers(t, ps1), \"Unexpected peer order\")\n\n\tps2Expected := append([]string(nil), hostports...)\n\treverse(ps2Expected)\n\tassert.Equal(t, ps2Expected, getAllPeers(t, ps2), \"Unexpected peer order\")\n}\n\nfunc TestPeerSelectionRanking(t *testing.T) {\n\tconst numPeers = 10\n\tconst numIterations = 1000\n\n\t// Selected is a map from rank -> [peer, count]\n\t// It tracks how often a peer gets selected at a specific rank.\n\tselected := make([]map[string]int, numPeers)\n\tfor i := 0; i < numPeers; i++ {\n\t\tselected[i] = make(map[string]int)\n\t}\n\n\tfor i := 0; i < numIterations; i++ {\n\t\tch := testutils.NewClient(t, nil)\n\t\tdefer ch.Close()\n\t\tch.SetRandomSeed(int64(i * 100))\n\n\t\tfor i := 0; i < numPeers; i++ {\n\t\t\thp := fmt.Sprintf(\"127.0.0.1:60%v\", i)\n\t\t\tch.Peers().Add(hp)\n\t\t}\n\n\t\tfor i := 0; i < numPeers; i++ {\n\t\t\tpeer, err := ch.Peers().Get(nil)\n\t\t\trequire.NoError(t, err, \"Peers.Get failed\")\n\t\t\tselected[i][peer.HostPort()]++\n\t\t}\n\t}\n\n\tfor _, m := range selected {\n\t\ttestDistribution(t, m, 50, 150)\n\t}\n}\n\nfunc createScoreStrategy(initial, delta int64) (calc ScoreCalculator, retCount *atomic.Uint64) {\n\tvar (\n\t\tcount atomic.Uint64\n\t\tscore atomic.Uint64\n\t)\n\n\treturn ScoreCalculatorFunc(func(p *Peer) uint64 {\n\t\tcount.Add(1)\n\t\treturn score.Add(uint64(delta))\n\t}), &count\n}\n\nfunc createSubChannelWNewStrategy(ch *Channel, name string, initial, delta int64, opts ...SubChannelOption) *PeerList {\n\tstrategy, _ := createScoreStrategy(initial, delta)\n\tsc := ch.GetSubChannel(name, opts...)\n\tps := sc.Peers()\n\tps.SetStrategy(strategy)\n\treturn ps\n}\n\nfunc testDistribution(t testing.TB, counts map[string]int, min, max float64) {\n\tfor k, v := range counts {\n\t\tif float64(v) < min || float64(v) > max {\n\t\t\tt.Errorf(\"Key %v has value %v which is out of range %v-%v\", k, v, min, max)\n\t\t}\n\t}\n}\n\n// waitTillNConnetions will run f which should end up causing the peer with hostPort in ch\n// to have the specified number of inbound and outbound connections.\n// If the number of connections does not match after a second, the test is failed.\nfunc waitTillNConnections(t *testing.T, ch *Channel, hostPort string, inbound, outbound int, f func()) {\n\tpeer, ok := ch.RootPeers().Get(hostPort)\n\tif !ok {\n\t\treturn\n\t}\n\n\tvar (\n\t\ti = -1\n\t\to = -1\n\t)\n\n\tinboundEmpty := make(chan struct{})\n\tvar onUpdateOnce sync.Once\n\tonUpdate := func(p *Peer) {\n\t\tif i, o = p.NumConnections(); (i == inbound || inbound == -1) &&\n\t\t\t(o == outbound || outbound == -1) {\n\t\t\tonUpdateOnce.Do(func() {\n\t\t\t\tclose(inboundEmpty)\n\t\t\t})\n\t\t}\n\t}\n\tpeer.SetOnUpdate(onUpdate)\n\n\tf()\n\n\tselect {\n\tcase <-inboundEmpty:\n\t\treturn\n\tcase <-time.After(time.Second):\n\t\tt.Errorf(\"Timed out waiting for peer %v to have (in: %v, out: %v) connections, got (in: %v, out: %v)\",\n\t\t\thostPort, inbound, outbound, i, o)\n\t}\n}\n\n// waitTillInboundEmpty will run f which should end up causing the peer with hostPort in ch\n// to have 0 inbound connections. It will fail the test after a second.\nfunc waitTillInboundEmpty(t *testing.T, ch *Channel, hostPort string, f func()) {\n\twaitTillNConnections(t, ch, hostPort, 0, -1, f)\n}\n\ntype peerSelectionTest struct {\n\tpeerTest\n\n\t// numPeers is the number of peers added to the client channel.\n\tnumPeers int\n\t// numAffinity is the number of affinity nodes.\n\tnumAffinity int\n\t// numAffinityWithNoCall is the number of affinity nodes which doesn't send call req to client.\n\tnumAffinityWithNoCall int\n\t// numConcurrent is the number of concurrent goroutine to make outbound calls.\n\tnumConcurrent int\n\t// hasInboundCall is the bool flag to tell whether to have inbound calls from affinity nodes\n\thasInboundCall bool\n\n\tservers            []*Channel\n\taffinity           []*Channel\n\taffinityWithNoCall []*Channel\n\tclient             *Channel\n}\n\nfunc (pt *peerSelectionTest) setup(t testing.TB) {\n\tpt.setupServers(t)\n\tpt.setupClient(t)\n\tpt.setupAffinity(t)\n}\n\n// setupServers will create numPeer servers, and register handlers on them.\nfunc (pt *peerSelectionTest) setupServers(t testing.TB) {\n\tpt.servers = make([]*Channel, pt.numPeers)\n\n\t// Set up numPeers servers.\n\tfor i := 0; i < pt.numPeers; i++ {\n\t\tpt.servers[i], _ = pt.NewService(t, \"server\", fmt.Sprintf(\"server-%v\", i))\n\t\tpt.servers[i].Register(raw.Wrap(newTestHandler(pt.t)), \"echo\")\n\t}\n}\n\nfunc (pt *peerSelectionTest) setupAffinity(t testing.TB) {\n\tpt.affinity = make([]*Channel, pt.numAffinity)\n\tfor i := range pt.affinity {\n\t\tpt.affinity[i] = pt.servers[i]\n\t}\n\n\tpt.affinityWithNoCall = make([]*Channel, pt.numAffinityWithNoCall)\n\tfor i := range pt.affinityWithNoCall {\n\t\tpt.affinityWithNoCall[i] = pt.servers[i+pt.numAffinity]\n\t}\n\n\tvar wg sync.WaitGroup\n\twg.Add(pt.numAffinity)\n\t// Connect from the affinity nodes to the service.\n\thostport := pt.client.PeerInfo().HostPort\n\tserviceName := pt.client.PeerInfo().ServiceName\n\tfor _, affinity := range pt.affinity {\n\t\tgo func(affinity *Channel) {\n\t\t\taffinity.Peers().Add(hostport)\n\t\t\tpt.makeCall(affinity.GetSubChannel(serviceName))\n\t\t\twg.Done()\n\t\t}(affinity)\n\t}\n\twg.Wait()\n\n\twg.Add(pt.numAffinityWithNoCall)\n\tfor _, p := range pt.affinityWithNoCall {\n\t\tgo func(p *Channel) {\n\t\t\t// use ping to build connection without sending call req.\n\t\t\tpt.sendPing(p, hostport)\n\t\t\twg.Done()\n\t\t}(p)\n\t}\n\twg.Wait()\n}\n\nfunc (pt *peerSelectionTest) setupClient(t testing.TB) {\n\tpt.client, _ = pt.NewService(t, \"client\", \"client\")\n\tpt.client.Register(raw.Wrap(newTestHandler(pt.t)), \"echo\")\n\tfor _, server := range pt.servers {\n\t\tpt.client.Peers().Add(server.PeerInfo().HostPort)\n\t}\n}\n\nfunc (pt *peerSelectionTest) makeCall(sc *SubChannel) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\t_, _, _, err := raw.CallSC(ctx, sc, \"echo\", nil, nil)\n\tassert.NoError(pt.t, err, \"raw.Call failed\")\n}\n\nfunc (pt *peerSelectionTest) sendPing(ch *Channel, hostport string) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\terr := ch.Ping(ctx, hostport)\n\tassert.NoError(pt.t, err, \"ping failed\")\n}\n\nfunc (pt *peerSelectionTest) runStressSimple(b *testing.B) {\n\tvar wg sync.WaitGroup\n\twg.Add(pt.numConcurrent)\n\n\t// server outbound request\n\tsc := pt.client.GetSubChannel(\"server\")\n\tfor i := 0; i < pt.numConcurrent; i++ {\n\t\tgo func(sc *SubChannel) {\n\t\t\tdefer wg.Done()\n\t\t\tfor j := 0; j < b.N; j++ {\n\t\t\t\tpt.makeCall(sc)\n\t\t\t}\n\t\t}(sc)\n\t}\n\n\twg.Wait()\n}\n\nfunc (pt *peerSelectionTest) runStress() {\n\tnumClock := pt.numConcurrent + pt.numAffinity\n\tclocks := make([]chan struct{}, numClock)\n\tfor i := 0; i < numClock; i++ {\n\t\tclocks[i] = make(chan struct{})\n\t}\n\n\tvar wg sync.WaitGroup\n\twg.Add(numClock)\n\n\t// helper that will make a request every n ticks.\n\treqEveryNTicks := func(n int, sc *SubChannel, clock <-chan struct{}) {\n\t\tdefer wg.Done()\n\t\tfor {\n\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\t_, ok := <-clock\n\t\t\t\tif !ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tpt.makeCall(sc)\n\t\t}\n\t}\n\n\t// server outbound request\n\tsc := pt.client.GetSubChannel(\"server\")\n\tfor i := 0; i < pt.numConcurrent; i++ {\n\t\tgo reqEveryNTicks(1, sc, clocks[i])\n\t}\n\t// affinity incoming requests\n\tif pt.hasInboundCall {\n\t\tserviceName := pt.client.PeerInfo().ServiceName\n\t\tfor i, affinity := range pt.affinity {\n\t\t\tgo reqEveryNTicks(1, affinity.GetSubChannel(serviceName), clocks[i+pt.numConcurrent])\n\t\t}\n\t}\n\n\ttickAllClocks := func() {\n\t\tfor i := 0; i < numClock; i++ {\n\t\t\tclocks[i] <- struct{}{}\n\t\t}\n\t}\n\n\tconst tickNum = 10000\n\tfor i := 0; i < tickNum; i++ {\n\t\tif i%(tickNum/10) == 0 {\n\t\t\tfmt.Printf(\"Stress test progress: %v\\n\", 100*i/tickNum)\n\t\t}\n\t\ttickAllClocks()\n\t}\n\n\tfor i := 0; i < numClock; i++ {\n\t\tclose(clocks[i])\n\t}\n\twg.Wait()\n}\n\n// Run these commands before run the benchmark.\n// sudo sysctl w kern.maxfiles=50000\n// ulimit n 50000\nfunc BenchmarkSimplePeerHeapPerf(b *testing.B) {\n\tpt := &peerSelectionTest{\n\t\tpeerTest:      peerTest{t: b},\n\t\tnumPeers:      1000,\n\t\tnumConcurrent: 100,\n\t}\n\tdefer pt.CleanUp()\n\tpt.setup(b)\n\tb.ResetTimer()\n\tpt.runStressSimple(b)\n}\n\nfunc TestPeerHeapPerf(t *testing.T) {\n\tCheckStress(t)\n\n\ttests := []struct {\n\t\tnumserver      int\n\t\taffinityRatio  float64\n\t\tnumConcurrent  int\n\t\thasInboundCall bool\n\t}{\n\t\t{\n\t\t\tnumserver:      1000,\n\t\t\taffinityRatio:  0.1,\n\t\t\tnumConcurrent:  5,\n\t\t\thasInboundCall: true,\n\t\t},\n\t\t{\n\t\t\tnumserver:      1000,\n\t\t\taffinityRatio:  0.1,\n\t\t\tnumConcurrent:  1,\n\t\t\thasInboundCall: true,\n\t\t},\n\t\t{\n\t\t\tnumserver:      100,\n\t\t\taffinityRatio:  0.1,\n\t\t\tnumConcurrent:  1,\n\t\t\thasInboundCall: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tpeerHeapStress(t, tt.numserver, tt.affinityRatio, tt.numConcurrent, tt.hasInboundCall)\n\t}\n}\n\nfunc peerHeapStress(t testing.TB, numserver int, affinityRatio float64, numConcurrent int, hasInboundCall bool) {\n\tpt := &peerSelectionTest{\n\t\tpeerTest:              peerTest{t: t},\n\t\tnumPeers:              numserver,\n\t\tnumConcurrent:         numConcurrent,\n\t\thasInboundCall:        hasInboundCall,\n\t\tnumAffinity:           int(float64(numserver) * affinityRatio),\n\t\tnumAffinityWithNoCall: 3,\n\t}\n\tdefer pt.CleanUp()\n\tpt.setup(t)\n\tpt.runStress()\n\tvalidateStressTest(t, pt.client, pt.numAffinity, pt.numAffinityWithNoCall)\n}\n\nfunc validateStressTest(t testing.TB, server *Channel, numAffinity int, numAffinityWithNoCall int) {\n\tstate := server.IntrospectState(&IntrospectionOptions{IncludeEmptyPeers: true})\n\n\tcountsByPeer := make(map[string]int)\n\tvar counts []int\n\tfor _, peer := range state.Peers {\n\t\tp, ok := state.RootPeers[peer.HostPort]\n\t\tassert.True(t, ok, \"Missing peer.\")\n\t\tif p.ChosenCount != 0 {\n\t\t\tcountsByPeer[p.HostPort] = int(p.ChosenCount)\n\t\t\tcounts = append(counts, int(p.ChosenCount))\n\t\t}\n\t}\n\t// when number of affinity is zero, all peer suppose to be chosen.\n\tif numAffinity == 0 && numAffinityWithNoCall == 0 {\n\t\tnumAffinity = len(state.Peers)\n\t}\n\tassert.EqualValues(t, len(countsByPeer), numAffinity+numAffinityWithNoCall, \"Number of affinities nodes mismatch.\")\n\tsort.Ints(counts)\n\tmedian := counts[len(counts)/2]\n\ttestDistribution(t, countsByPeer, float64(median)*0.9, float64(median)*1.1)\n}\n\nfunc TestPeerSelectionAfterClosed(t *testing.T) {\n\tpt := &peerSelectionTest{\n\t\tpeerTest:    peerTest{t: t},\n\t\tnumPeers:    5,\n\t\tnumAffinity: 5,\n\t}\n\tdefer pt.CleanUp()\n\tpt.setup(t)\n\n\ttoClose := pt.affinity[pt.numAffinity-1]\n\tclosedHP := toClose.PeerInfo().HostPort\n\ttoClose.Logger().Debugf(\"About to Close %v\", closedHP)\n\twaitTillInboundEmpty(t, pt.client, closedHP, func() {\n\t\ttoClose.Close()\n\t})\n\n\tfor i := 0; i < 10*pt.numAffinity; i++ {\n\t\tpeer, err := pt.client.Peers().Get(nil)\n\t\tassert.NoError(t, err, \"Client failed to select a peer\")\n\t\tassert.NotEqual(pt.t, closedHP, peer.HostPort(), \"Closed peer shouldn't be chosen\")\n\t}\n}\n\nfunc TestPeerScoreOnNewConnection(t *testing.T) {\n\ttests := []struct {\n\t\tmessage string\n\t\tconnect func(s1, s2 *Channel) *Peer\n\t}{\n\t\t{\n\t\t\tmessage: \"outbound connection\",\n\t\t\tconnect: func(s1, s2 *Channel) *Peer {\n\t\t\t\treturn s1.Peers().GetOrAdd(s2.PeerInfo().HostPort)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmessage: \"inbound connection\",\n\t\t\tconnect: func(s1, s2 *Channel) *Peer {\n\t\t\t\treturn s2.Peers().GetOrAdd(s1.PeerInfo().HostPort)\n\t\t\t},\n\t\t},\n\t}\n\n\tgetScore := func(pl *PeerList) uint64 {\n\t\tpeers := pl.IntrospectList(nil)\n\t\trequire.Equal(t, 1, len(peers), \"Wrong number of peers\")\n\t\treturn peers[0].Score\n\t}\n\n\tfor _, tt := range tests {\n\t\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\tctx, cancel := NewContext(time.Second)\n\t\t\tdefer cancel()\n\n\t\t\ts1 := ts.Server()\n\t\t\ts2 := ts.NewServer(nil)\n\n\t\t\ts1.Peers().Add(s2.PeerInfo().HostPort)\n\t\t\ts2.Peers().Add(s1.PeerInfo().HostPort)\n\n\t\t\tinitialScore := getScore(s1.Peers())\n\t\t\tpeer := tt.connect(s1, s2)\n\t\t\tconn, err := peer.GetConnection(ctx)\n\t\t\trequire.NoError(t, err, \"%v: GetConnection failed\", tt.message)\n\n\t\t\t// When receiving an inbound connection, the outbound connect may return\n\t\t\t// before the inbound has updated the score, so we may need to retry.\n\t\t\tassert.True(t, testutils.WaitFor(time.Second, func() bool {\n\t\t\t\tconnectedScore := getScore(s1.Peers())\n\t\t\t\treturn connectedScore < initialScore\n\t\t\t}), \"%v: Expected connected peer score %v to be less than initial score %v\",\n\t\t\t\ttt.message, getScore(s1.Peers()), initialScore)\n\n\t\t\t// Ping to ensure the connection has been added to peers on both sides.\n\t\t\trequire.NoError(t, conn.Ping(ctx), \"%v: Ping failed\", tt.message)\n\t\t})\n\t}\n}\n\nfunc TestConnectToPeerHostPortMismatch(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\t// Set up a relay which will have a different host:port than the\n\t\t// real TChannel HostPort.\n\t\trelay, err := benchmark.NewTCPRawRelay([]string{ts.HostPort()})\n\t\trequire.NoError(t, err, \"Failed to set up TCP relay\")\n\t\tdefer relay.Close()\n\n\t\ts2 := ts.NewServer(nil)\n\t\tfor i := 0; i < 10; i++ {\n\t\t\trequire.NoError(t, s2.Ping(ctx, relay.HostPort()), \"Ping failed\")\n\t\t}\n\n\t\tassert.Equal(t, 1, s2.IntrospectNumConnections(), \"Unexpected number of connections\")\n\t})\n}\n\n// Test ensures that a closing connection does not count in NumConnections.\n// NumConnections should only include connections that be used to make calls.\nfunc TestPeerConnectionsClosing(t *testing.T) {\n\t// Disable the relay since we check the host:port directly.\n\topts := testutils.NewOpts().NoRelay()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tunblock := make(chan struct{})\n\t\tgotCall := make(chan struct{})\n\t\ttestutils.RegisterEcho(ts.Server(), func() {\n\t\t\tclose(gotCall)\n\t\t\t<-unblock\n\t\t})\n\n\t\tclient := ts.NewServer(nil)\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\t}()\n\n\t\t// Wait for the call to be received before checking connections..\n\t\t<-gotCall\n\t\tpeer := ts.Server().Peers().GetOrAdd(client.PeerInfo().HostPort)\n\t\tin, out := peer.NumConnections()\n\t\tassert.Equal(t, 1, in+out, \"Unexpected number of incoming connections\")\n\n\t\t// Now when we try to close the channel, all the connections will change\n\t\t// state, and should no longer count as active connections.\n\t\tconn, err := peer.GetConnection(nil)\n\t\trequire.NoError(t, err, \"Failed to get connection\")\n\t\trequire.True(t, conn.IsActive(), \"Connection should be active\")\n\n\t\tts.Server().Close()\n\t\trequire.False(t, conn.IsActive(), \"Connection should not be active after Close\")\n\t\tin, out = peer.NumConnections()\n\t\tassert.Equal(t, 0, in+out, \"Inactive connections should not be included in peer LAST\")\n\n\t\tclose(unblock)\n\t\twg.Wait()\n\t})\n}\n\nfunc BenchmarkAddPeers(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tch := testutils.NewClient(b, nil)\n\t\tfor i := 0; i < 1000; i++ {\n\t\t\thp := fmt.Sprintf(\"127.0.0.1:%v\", i)\n\t\t\tch.Peers().Add(hp)\n\t\t}\n\t}\n}\n\nfunc TestPeerSelectionStrategyChange(t *testing.T) {\n\tconst numPeers = 2\n\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\n\tfor i := 0; i < numPeers; i++ {\n\t\tch.Peers().Add(fmt.Sprintf(\"127.0.0.1:60%v\", i))\n\t}\n\n\tfor _, score := range []uint64{1000, 2000} {\n\t\tch.Peers().SetStrategy(createConstScoreStrategy(score))\n\t\tfor _, v := range ch.Peers().IntrospectList(nil) {\n\t\t\tassert.Equal(t, v.Score, score)\n\t\t}\n\t}\n}\n\nfunc createConstScoreStrategy(score uint64) (calc ScoreCalculator) {\n\treturn ScoreCalculatorFunc(func(p *Peer) uint64 {\n\t\treturn score\n\t})\n}\n"
  },
  {
    "path": "peers/doc.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n/*\nPackage peers provides helpers for managing TChannel peers.\n*/\npackage peers\n"
  },
  {
    "path": "peers/prefer.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage peers\n\nimport \"github.com/uber/tchannel-go\"\n\ntype hrwScoreCalc struct {\n\tclientID uint32\n}\n\n// NewHRWScorer returns a ScoreCalculator based on Rendezvous or Highest Random Weight\n// hashing.\n// It is useful for distributing load in peer-to-peer situations where we have\n// many clients picking from a set of servers with \"sticky\" semantics that will\n// spread load evenly as servers go down or new servers are added.\n// The clientID is used to score the servers, so each client should pass in\n// a unique client ID.\nfunc NewHRWScorer(clientID uint32) tchannel.ScoreCalculator {\n\treturn &hrwScoreCalc{mod2_31(clientID)}\n}\n\nfunc (s *hrwScoreCalc) GetScore(p *tchannel.Peer) uint64 {\n\tserver := mod2_31(fnv32a(p.HostPort()))\n\n\t// These constants are taken from W_rand2 in the Rendezvous paper:\n\t// http://www.eecs.umich.edu/techreports/cse/96/CSE-TR-316-96.pdf\n\tv := 1103515245*((1103515245*s.clientID+12345)^server) + 12345\n\treturn uint64(mod2_31(v))\n}\n\nfunc mod2_31(v uint32) uint32 {\n\treturn v & ((1 << 31) - 1)\n}\n\n// This is based on the standard library's fnv32a implementation.\n// We copy it for a couple of reasons:\n//  1. Avoid allocations to create a hash.Hash32\n//  2. Avoid converting the []byte to a string (another allocation) since\n//     the Hash32 interface only supports writing bytes.\nfunc fnv32a(s string) uint32 {\n\tconst (\n\t\tinitial = 2166136261\n\t\tprime   = 16777619\n\t)\n\n\thash := uint32(initial)\n\tfor i := 0; i < len(s); i++ {\n\t\thash ^= uint32(s[i])\n\t\thash *= prime\n\t}\n\treturn hash\n}\n"
  },
  {
    "path": "peers/prefer_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage peers\n\nimport (\n\t\"fmt\"\n\t\"hash/fnv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\nfunc TestHRWScorerGetScore(t *testing.T) {\n\tclient := testutils.NewClient(t, nil)\n\tpeer := client.Peers().GetOrAdd(\"1.1.1.1\")\n\n\tc1 := NewHRWScorer(1)\n\tc2 := NewHRWScorer(2)\n\n\tassert.NotEqual(t, c1.GetScore(peer), c2.GetScore(peer))\n}\n\nfunc TestHRWScorerDistribution(t *testing.T) {\n\tconst (\n\t\tnumClients = 1000\n\t\tnumServers = 10\n\t)\n\n\tch := testutils.NewClient(t, nil)\n\tservers := make([]*tchannel.Peer, numServers)\n\tfor i := range servers {\n\t\tservers[i] = ch.Peers().GetOrAdd(fmt.Sprintf(\"192.0.2.%v\", i))\n\t}\n\n\tserverSelected := make([]int, numServers)\n\tfor i := 0; i < numClients; i++ {\n\t\tclient := NewHRWScorer(uint32(i))\n\n\t\thighestScore := uint64(0)\n\t\thighestServer := -1\n\t\tfor s, server := range servers {\n\t\t\tif score := client.GetScore(server); score > highestScore {\n\t\t\t\thighestScore = score\n\t\t\t\thighestServer = s\n\t\t\t}\n\t\t}\n\t\tserverSelected[highestServer]++\n\t}\n\n\t// We can't get a perfect distribution, but should be within 20%.\n\tconst (\n\t\texpectCalls = numClients / numServers\n\t\tdelta       = expectCalls * 0.2\n\t)\n\tfor serverIdx, count := range serverSelected {\n\t\tassert.InDelta(t, expectCalls, count, delta, \"Server %v out of range\", serverIdx)\n\t}\n}\n\nfunc countingServer(t *testing.T, opts *testutils.ChannelOpts) (*tchannel.Channel, *atomic.Int32) {\n\tvar cnt atomic.Int32\n\tserver := testutils.NewServer(t, opts)\n\ttestutils.RegisterEcho(server, func() { cnt.Inc() })\n\treturn server, &cnt\n}\n\nfunc TestHRWScorerIntegration(t *testing.T) {\n\t// Client pings to the server may cause errors during Close.\n\tsOpts := testutils.NewOpts().SetServiceName(\"svc\").DisableLogVerification()\n\ts1, s1Count := countingServer(t, sOpts)\n\ts2, s2Count := countingServer(t, sOpts)\n\n\tclient := testutils.NewClient(t, testutils.NewOpts().DisableLogVerification())\n\tclient.Peers().SetStrategy(NewHRWScorer(1))\n\tclient.Peers().Add(s1.PeerInfo().HostPort)\n\tclient.Peers().Add(s2.PeerInfo().HostPort)\n\n\t// We want to call the raw echo function with TChannel retries.\n\tcallEcho := func() error {\n\t\tctx, cancel := tchannel.NewContext(time.Second)\n\t\tdefer cancel()\n\t\treturn client.RunWithRetry(ctx, func(ctx context.Context, rs *tchannel.RequestState) error {\n\t\t\t_, err := raw.CallV2(ctx, client.GetSubChannel(\"svc\"), raw.CArgs{\n\t\t\t\tMethod: \"echo\",\n\t\t\t\tCallOptions: &tchannel.CallOptions{\n\t\t\t\t\tRequestState: rs,\n\t\t\t\t},\n\t\t\t})\n\t\t\treturn err\n\t\t})\n\t}\n\n\tpreferred, err := client.Peers().Get(nil)\n\trequire.NoError(t, err, \"Failed to get peer\")\n\tif preferred.HostPort() == s2.PeerInfo().HostPort {\n\t\t// To make the test easier, we want \"s1\" to always be the preferred hostPort.\n\t\ts1, s1Count, s2, s2Count = s2, s2Count, s1, s1Count\n\t}\n\n\t// When we make 10 calls, all of them should go to s1\n\tfor i := 0; i < 10; i++ {\n\t\terr := callEcho()\n\t\trequire.NoError(t, err, \"Failed to call echo initially\")\n\t}\n\tassert.EqualValues(t, 10, s1Count.Load(), \"All calls should go to s1\")\n\n\t// Stop s1, and ensure the client notices S1 has failed.\n\ts1.Close()\n\ttestutils.WaitFor(time.Second, func() bool {\n\t\tif !s1.Closed() {\n\t\t\treturn false\n\t\t}\n\t\tctx, cancel := tchannel.NewContext(time.Second)\n\t\tdefer cancel()\n\t\treturn client.Ping(ctx, s1.PeerInfo().HostPort) != nil\n\t})\n\n\t// Since s1 is stopped, next call should go to s2.\n\terr = callEcho()\n\trequire.NoError(t, err, \"Failed to call echo after s1 close\")\n\tassert.EqualValues(t, 10, s1Count.Load(), \"s1 should not get new calls as it's down\")\n\tassert.EqualValues(t, 1, s2Count.Load(), \"New call should go to s2\")\n\n\t// And if s1 comes back, calls should resume to s1.\n\ts1Up := testutils.NewClient(t, sOpts)\n\ttestutils.RegisterEcho(s1Up, func() { s1Count.Inc() })\n\terr = s1Up.ListenAndServe(s1.PeerInfo().HostPort)\n\trequire.NoError(t, err, \"Failed to bring up a new channel as s1\")\n\n\tfor i := 0; i < 10; i++ {\n\t\trequire.NoError(t, callEcho(), \"Failed to call echo after s1 restarted\")\n\t}\n\tassert.EqualValues(t, 20, s1Count.Load(), \"Once s1 is up, calls should resume to s1\")\n\tassert.EqualValues(t, 1, s2Count.Load(), \"s2 should not receive calls after s1 restarted\")\n}\n\nfunc stdFnv32a(s string) uint32 {\n\th := fnv.New32a()\n\th.Write([]byte(s))\n\treturn h.Sum32()\n}\n\nfunc TestFnv32a(t *testing.T) {\n\ttests := []string{\n\t\t\"\",\n\t\t\"1.1.1.1\",\n\t\t\"some-other-data\",\n\t}\n\n\tfor _, tt := range tests {\n\t\tassert.Equal(t, stdFnv32a(tt), fnv32a(tt), \"Different results for %q\", tt)\n\t}\n}\n\nfunc BenchmarkHrwScoreCalc(b *testing.B) {\n\tclient := testutils.NewClient(b, nil)\n\tpeer := client.Peers().GetOrAdd(\"1.1.1.1\")\n\n\tc := NewHRWScorer(1)\n\tfor i := 0; i < b.N; i++ {\n\t\tc.GetScore(peer)\n\t}\n}\n"
  },
  {
    "path": "pprof/pprof.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage pprof\n\nimport (\n\t\"net/http\"\n\t_ \"net/http/pprof\" // So pprof endpoints are registered on DefaultServeMux.\n\n\t\"github.com/uber/tchannel-go\"\n\tthttp \"github.com/uber/tchannel-go/http\"\n\n\t\"golang.org/x/net/context\"\n)\n\nfunc serveHTTP(req *http.Request, response *tchannel.InboundCallResponse) {\n\trw, finish := thttp.ResponseWriter(response)\n\thttp.DefaultServeMux.ServeHTTP(rw, req)\n\tfinish()\n}\n\n// Register registers pprof endpoints on the given registrar under _pprof.\n// The _pprof endpoint uses as-http and is a tunnel to the default serve mux.\nfunc Register(registrar tchannel.Registrar) {\n\thandler := func(ctx context.Context, call *tchannel.InboundCall) {\n\t\treq, err := thttp.ReadRequest(call)\n\t\tif err != nil {\n\t\t\tregistrar.Logger().WithFields(\n\t\t\t\ttchannel.LogField{Key: \"err\", Value: err.Error()},\n\t\t\t).Warn(\"Failed to read HTTP request.\")\n\t\t\treturn\n\t\t}\n\n\t\tserveHTTP(req, call.Response())\n\t}\n\tregistrar.Register(tchannel.HandlerFunc(handler), \"_pprof\")\n}\n"
  },
  {
    "path": "pprof/pprof_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage pprof\n\nimport (\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\tthttp \"github.com/uber/tchannel-go/http\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestPProfEndpoint(t *testing.T) {\n\tch := testutils.NewServer(t, nil)\n\tRegister(ch)\n\n\tctx, cancel := tchannel.NewContext(time.Second)\n\tdefer cancel()\n\n\treq, err := http.NewRequest(\"GET\", \"/debug/pprof/block?debug=1\", nil)\n\trequire.NoError(t, err, \"NewRequest failed\")\n\n\tcall, err := ch.BeginCall(ctx, ch.PeerInfo().HostPort, ch.ServiceName(), \"_pprof\", nil)\n\trequire.NoError(t, err, \"BeginCall failed\")\n\trequire.NoError(t, err, thttp.WriteRequest(call, req), \"thttp.WriteRequest failed\")\n\n\tresponse, err := thttp.ReadResponse(call.Response())\n\trequire.NoError(t, err, \"ReadResponse failed\")\n\n\tassert.Equal(t, http.StatusOK, response.StatusCode)\n\tbody, err := ioutil.ReadAll(response.Body)\n\tif assert.NoError(t, err, \"Read body failed\") {\n\t\tassert.Contains(t, string(body), \"contention\", \"Response does not contain expected string\")\n\t}\n}\n"
  },
  {
    "path": "preinit_connection.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"golang.org/x/net/context\"\n)\n\nfunc (ch *Channel) outboundHandshake(ctx context.Context, c net.Conn, outboundHP string, events connectionEvents) (_ *Connection, err error) {\n\tdefer setInitDeadline(ctx, c)()\n\tdefer func() {\n\t\terr = ch.initError(c, outbound, 1, err)\n\t}()\n\n\tmsg := &initReq{initMessage: ch.getInitMessage(ctx, 1)}\n\tif err := ch.writeMessage(c, msg); err != nil {\n\t\treturn nil, err\n\t}\n\n\tres := &initRes{}\n\tid, err := ch.readMessage(c, res)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif id != msg.id {\n\t\treturn nil, NewSystemError(ErrCodeProtocol, \"received initRes with invalid ID, wanted %v, got %v\", msg.id, id)\n\t}\n\n\tif res.Version != CurrentProtocolVersion {\n\t\treturn nil, unsupportedProtocolVersion(res.Version)\n\t}\n\n\tremotePeer, remotePeerAddress, err := parseRemotePeer(res.initParams, c.RemoteAddr())\n\tif err != nil {\n\t\treturn nil, NewWrappedSystemError(ErrCodeProtocol, err)\n\t}\n\n\tbaseCtx := context.Background()\n\tif p := getTChannelParams(ctx); p != nil && p.connectBaseContext != nil {\n\t\tbaseCtx = p.connectBaseContext\n\t}\n\n\treturn ch.newConnection(baseCtx, c, 1 /* initialID */, outboundHP, remotePeer, remotePeerAddress, events), nil\n}\n\nfunc (ch *Channel) inboundHandshake(ctx context.Context, c net.Conn, events connectionEvents) (_ *Connection, err error) {\n\tid := uint32(math.MaxUint32)\n\n\tdefer setInitDeadline(ctx, c)()\n\tdefer func() {\n\t\terr = ch.initError(c, inbound, id, err)\n\t}()\n\n\treq := &initReq{}\n\tid, err = ch.readMessage(c, req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif req.Version < CurrentProtocolVersion {\n\t\treturn nil, unsupportedProtocolVersion(req.Version)\n\t}\n\n\tremotePeer, remotePeerAddress, err := parseRemotePeer(req.initParams, c.RemoteAddr())\n\tif err != nil {\n\t\treturn nil, NewWrappedSystemError(ErrCodeProtocol, err)\n\t}\n\n\tres := &initRes{initMessage: ch.getInitMessage(ctx, id)}\n\tif err := ch.writeMessage(c, res); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ch.newConnection(ctx, c, 0 /* initialID */, \"\" /* outboundHP */, remotePeer, remotePeerAddress, events), nil\n}\n\nfunc (ch *Channel) getInitParams() initParams {\n\tlocalPeer := ch.PeerInfo()\n\treturn initParams{\n\t\tInitParamHostPort:                localPeer.HostPort,\n\t\tInitParamProcessName:             localPeer.ProcessName,\n\t\tInitParamTChannelLanguage:        localPeer.Version.Language,\n\t\tInitParamTChannelLanguageVersion: localPeer.Version.LanguageVersion,\n\t\tInitParamTChannelVersion:         localPeer.Version.TChannelVersion,\n\t}\n}\n\nfunc (ch *Channel) getInitMessage(ctx context.Context, id uint32) initMessage {\n\tmsg := initMessage{\n\t\tid:         id,\n\t\tVersion:    CurrentProtocolVersion,\n\t\tinitParams: ch.getInitParams(),\n\t}\n\tif p := getTChannelParams(ctx); p != nil && p.hideListeningOnOutbound {\n\t\tmsg.initParams[InitParamHostPort] = ephemeralHostPort\n\t}\n\n\treturn msg\n}\n\nfunc (ch *Channel) initError(c net.Conn, connDir connectionDirection, id uint32, err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tch.log.WithFields(LogFields{\n\t\t{\"connectionDirection\", connDir},\n\t\t{\"localAddr\", c.LocalAddr().String()},\n\t\t{\"remoteAddr\", c.RemoteAddr().String()},\n\t\tErrField(err),\n\t}...).Error(\"Failed during connection handshake.\")\n\n\tif ne, ok := err.(net.Error); ok && ne.Timeout() {\n\t\terr = ErrTimeout\n\t}\n\tif err == io.EOF {\n\t\terr = NewWrappedSystemError(ErrCodeNetwork, io.EOF)\n\t}\n\tch.writeMessage(c, &errorMessage{\n\t\tid:      id,\n\t\terrCode: GetSystemErrorCode(err),\n\t\tmessage: err.Error(),\n\t})\n\tc.Close()\n\treturn err\n}\n\nfunc (ch *Channel) writeMessage(c net.Conn, msg message) error {\n\tframe := ch.connectionOptions.FramePool.Get()\n\tdefer ch.connectionOptions.FramePool.Release(frame)\n\n\tif err := frame.write(msg); err != nil {\n\t\treturn err\n\t}\n\treturn frame.WriteOut(c)\n}\n\nfunc (ch *Channel) readMessage(c net.Conn, msg message) (uint32, error) {\n\tframe := ch.connectionOptions.FramePool.Get()\n\tdefer ch.connectionOptions.FramePool.Release(frame)\n\n\tif err := frame.ReadIn(c); err != nil {\n\t\treturn 0, err\n\t}\n\n\tif frame.Header.messageType != msg.messageType() {\n\t\tif frame.Header.messageType == messageTypeError {\n\t\t\treturn frame.Header.ID, readError(frame)\n\t\t}\n\t\treturn frame.Header.ID, NewSystemError(ErrCodeProtocol, \"expected message type %v, got %v\", msg.messageType(), frame.Header.messageType)\n\t}\n\n\treturn frame.Header.ID, frame.read(msg)\n}\n\nfunc parseRemotePeer(p initParams, remoteAddr net.Addr) (PeerInfo, peerAddressComponents, error) {\n\tvar (\n\t\tremotePeer        PeerInfo\n\t\tremotePeerAddress peerAddressComponents\n\t\tok                bool\n\t)\n\n\tif remotePeer.HostPort, ok = p[InitParamHostPort]; !ok {\n\t\treturn remotePeer, remotePeerAddress, fmt.Errorf(\"header %v is required\", InitParamHostPort)\n\t}\n\tif remotePeer.ProcessName, ok = p[InitParamProcessName]; !ok {\n\t\treturn remotePeer, remotePeerAddress, fmt.Errorf(\"header %v is required\", InitParamProcessName)\n\t}\n\n\t// If the remote host:port is ephemeral, use the socket address as the\n\t// host:port and set IsEphemeral to true.\n\tif isEphemeralHostPort(remotePeer.HostPort) {\n\t\tremotePeer.HostPort = remoteAddr.String()\n\t\tremotePeer.IsEphemeral = true\n\t}\n\n\tremotePeer.Version.Language = p[InitParamTChannelLanguage]\n\tremotePeer.Version.LanguageVersion = p[InitParamTChannelLanguageVersion]\n\tremotePeer.Version.TChannelVersion = p[InitParamTChannelVersion]\n\n\taddress := remotePeer.HostPort\n\tif sHost, sPort, err := net.SplitHostPort(address); err == nil {\n\t\taddress = sHost\n\t\tif p, err := strconv.ParseUint(sPort, 10, 16); err == nil {\n\t\t\tremotePeerAddress.port = uint16(p)\n\t\t}\n\t}\n\tif address == \"localhost\" {\n\t\tremotePeerAddress.ipv4 = 127<<24 | 1\n\t} else if ip := net.ParseIP(address); ip != nil {\n\t\tif ip4 := ip.To4(); ip4 != nil {\n\t\t\tremotePeerAddress.ipv4 = binary.BigEndian.Uint32(ip4)\n\t\t} else {\n\t\t\tremotePeerAddress.ipv6 = address\n\t\t}\n\t} else {\n\t\tremotePeerAddress.hostname = address\n\t}\n\n\treturn remotePeer, remotePeerAddress, nil\n}\n\nfunc setInitDeadline(ctx context.Context, c net.Conn) func() {\n\tdeadline, ok := ctx.Deadline()\n\tif !ok {\n\t\tdeadline = time.Now().Add(5 * time.Second)\n\t}\n\n\tc.SetDeadline(deadline)\n\treturn func() {\n\t\tc.SetDeadline(time.Time{})\n\t}\n}\n\nfunc readError(frame *Frame) error {\n\terrMsg := &errorMessage{\n\t\tid: frame.Header.ID,\n\t}\n\tif err := frame.read(errMsg); err != nil {\n\t\treturn err\n\t}\n\n\treturn errMsg.AsSystemError()\n}\n\nfunc unsupportedProtocolVersion(got uint16) error {\n\treturn NewSystemError(ErrCodeProtocol, \"unsupported protocol version %d from peer, expected %v\", got, CurrentProtocolVersion)\n}\n"
  },
  {
    "path": "raw/call.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage raw\n\nimport (\n\t\"errors\"\n\n\t\"golang.org/x/net/context\"\n\n\t\"github.com/uber/tchannel-go\"\n)\n\n// ErrAppError is returned if the application sets an error response.\nvar ErrAppError = errors.New(\"application error\")\n\n// ReadArgsV2 reads arg2 and arg3 from a reader.\nfunc ReadArgsV2(r tchannel.ArgReadable) ([]byte, []byte, error) {\n\tvar arg2, arg3 []byte\n\n\tif err := tchannel.NewArgReader(r.Arg2Reader()).Read(&arg2); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif err := tchannel.NewArgReader(r.Arg3Reader()).Read(&arg3); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\treturn arg2, arg3, nil\n}\n\n// WriteArgs writes the given arguments to the call, and returns the response args.\nfunc WriteArgs(call *tchannel.OutboundCall, arg2, arg3 []byte) ([]byte, []byte, *tchannel.OutboundCallResponse, error) {\n\tif err := tchannel.NewArgWriter(call.Arg2Writer()).Write(arg2); err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\tif err := tchannel.NewArgWriter(call.Arg3Writer()).Write(arg3); err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\tresp := call.Response()\n\tvar respArg2 []byte\n\tif err := tchannel.NewArgReader(resp.Arg2Reader()).Read(&respArg2); err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\tvar respArg3 []byte\n\tif err := tchannel.NewArgReader(resp.Arg3Reader()).Read(&respArg3); err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\treturn respArg2, respArg3, resp, nil\n}\n\n// Call makes a call to the given hostPort with the given arguments and returns the response args.\nfunc Call(ctx context.Context, ch *tchannel.Channel, hostPort string, serviceName, method string,\n\targ2, arg3 []byte) ([]byte, []byte, *tchannel.OutboundCallResponse, error) {\n\n\tcall, err := ch.BeginCall(ctx, hostPort, serviceName, method, nil)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\treturn WriteArgs(call, arg2, arg3)\n}\n\n// CallSC makes a call using the given subcahnnel\nfunc CallSC(ctx context.Context, sc *tchannel.SubChannel, method string, arg2, arg3 []byte) (\n\t[]byte, []byte, *tchannel.OutboundCallResponse, error) {\n\n\tcall, err := sc.BeginCall(ctx, method, nil)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\treturn WriteArgs(call, arg2, arg3)\n}\n\n// CArgs are the call arguments passed to CallV2.\ntype CArgs struct {\n\tMethod      string\n\tArg2        []byte\n\tArg3        []byte\n\tCallOptions *tchannel.CallOptions\n}\n\n// CRes is the result of making a call.\ntype CRes struct {\n\tArg2     []byte\n\tArg3     []byte\n\tAppError bool\n}\n\n// CallV2 makes a call and does not attempt any retries.\nfunc CallV2(ctx context.Context, sc *tchannel.SubChannel, cArgs CArgs) (*CRes, error) {\n\tcall, err := sc.BeginCall(ctx, cArgs.Method, cArgs.CallOptions)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\targ2, arg3, res, err := WriteArgs(call, cArgs.Arg2, cArgs.Arg3)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &CRes{\n\t\tArg2:     arg2,\n\t\tArg3:     arg3,\n\t\tAppError: res.ApplicationError(),\n\t}, nil\n}\n"
  },
  {
    "path": "raw/handler.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage raw\n\nimport (\n\t\"golang.org/x/net/context\"\n\n\t\"github.com/uber/tchannel-go\"\n)\n\n// Handler is the interface for a raw handler.\ntype Handler interface {\n\t// Handle is called on incoming calls, and contains all the arguments.\n\t// If an error is returned, it will set ApplicationError Arg3 will be the error string.\n\tHandle(ctx context.Context, args *Args) (*Res, error)\n\tOnError(ctx context.Context, err error)\n}\n\n// Args parses the arguments from an incoming call req.\ntype Args struct {\n\tCaller string\n\tFormat tchannel.Format\n\tMethod string\n\tArg2   []byte\n\tArg3   []byte\n}\n\n// Res represents the response to an incoming call req.\ntype Res struct {\n\tSystemErr error\n\t// IsErr is used to set an application error on the underlying call res.\n\tIsErr bool\n\tArg2  []byte\n\tArg3  []byte\n}\n\n// ReadArgs reads the *Args from the given call.\nfunc ReadArgs(call *tchannel.InboundCall) (*Args, error) {\n\tvar args Args\n\targs.Caller = call.CallerName()\n\targs.Format = call.Format()\n\targs.Method = string(call.Method())\n\tif err := tchannel.NewArgReader(call.Arg2Reader()).Read(&args.Arg2); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := tchannel.NewArgReader(call.Arg3Reader()).Read(&args.Arg3); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &args, nil\n}\n\n// WriteResponse writes the given Res to the InboundCallResponse.\nfunc WriteResponse(response *tchannel.InboundCallResponse, resp *Res) error {\n\tif resp.SystemErr != nil {\n\t\treturn response.SendSystemError(resp.SystemErr)\n\t}\n\tif resp.IsErr {\n\t\tif err := response.SetApplicationError(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := tchannel.NewArgWriter(response.Arg2Writer()).Write(resp.Arg2); err != nil {\n\t\treturn err\n\t}\n\treturn tchannel.NewArgWriter(response.Arg3Writer()).Write(resp.Arg3)\n}\n\n// Wrap wraps a Handler as a tchannel.Handler that can be passed to tchannel.Register.\nfunc Wrap(handler Handler) tchannel.Handler {\n\treturn tchannel.HandlerFunc(func(ctx context.Context, call *tchannel.InboundCall) {\n\t\targs, err := ReadArgs(call)\n\t\tif err != nil {\n\t\t\thandler.OnError(ctx, err)\n\t\t\treturn\n\t\t}\n\n\t\tresp, err := handler.Handle(ctx, args)\n\t\tresponse := call.Response()\n\t\tif err != nil {\n\t\t\tresp = &Res{\n\t\t\t\tSystemErr: err,\n\t\t\t}\n\t\t}\n\t\tif err := WriteResponse(response, resp); err != nil {\n\t\t\thandler.OnError(ctx, err)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "relay/relay.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package relay contains relaying interfaces for external use.\n//\n// These interfaces are currently unstable, and aren't covered by the API\n// backwards-compatibility guarantee.\npackage relay\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/thrift/arg2\"\n)\n\n// KeyVal is a key, val pair in arg2\ntype KeyVal struct {\n\tKey []byte\n\tVal []byte\n}\n\n// CallFrame is an interface that abstracts access to the call req frame.\ntype CallFrame interface {\n\t// TTL is the TTL of the underlying frame\n\tTTL() time.Duration\n\t// Caller is the name of the originating service.\n\tCaller() []byte\n\t// Service is the name of the destination service.\n\tService() []byte\n\t// Method is the name of the method being called.\n\tMethod() []byte\n\t// RoutingDelegate is the name of the routing delegate, if any.\n\tRoutingDelegate() []byte\n\t// RoutingKey may refer to an alternate traffic group instead of the\n\t// traffic group identified by the service name.\n\tRoutingKey() []byte\n\t// Arg2StartOffset returns the offset from start of payload to the\n\t// beginning of Arg2 in bytes.\n\tArg2StartOffset() int\n\t// Arg2EndOffset returns the offset from start of payload to the end of\n\t// Arg2 in bytes, and hasMore to indicate if there are more frames and\n\t// Arg3 has not started (i.e. Arg2 is fragmented).\n\tArg2EndOffset() (_ int, hasMore bool)\n\t// Arg2Iterator returns the iterator for reading Arg2 key value pair\n\t// of TChannel-Thrift Arg Scheme. If no iterator is available, return\n\t// io.EOF.\n\tArg2Iterator() (arg2.KeyValIterator, error)\n\t// Arg2Append appends a key/val pair to arg2\n\tArg2Append(key, val []byte)\n}\n\n// RespFrame is an interface that abstracts access to the CallRes frame\ntype RespFrame interface {\n\t// OK indicates whether the call was successful\n\tOK() bool\n\n\t// ArgScheme returns the scheme of the arg\n\tArgScheme() []byte\n\n\t// Arg2IsFragmented indicates whether arg2 runs over the first frame\n\tArg2IsFragmented() bool\n\n\t// Arg2 returns the raw arg2 payload\n\tArg2() []byte\n}\n\n// Conn contains information about the underlying connection.\ntype Conn struct {\n\t// RemoteAddr is the remote address of the underlying TCP connection.\n\tRemoteAddr string\n\n\t// RemoteProcessName is the process name sent in the TChannel handshake.\n\tRemoteProcessName string\n\n\t// IsOutbound returns whether this connection is an outbound connection\n\t// initiated via the relay.\n\tIsOutbound bool\n\n\t// Context contains connection-specific context which can be accessed via\n\t// RelayHost.Start()\n\tContext context.Context\n}\n\n// RateLimitDropError is the error that should be returned from\n// RelayHosts.Get if the request should be dropped silently.\n// This is bit of a hack, because rate limiting of this nature isn't part of\n// the actual TChannel protocol.\n// The relayer will record that it has dropped the packet, but *won't* notify\n// the client.\ntype RateLimitDropError struct{}\n\nfunc (e RateLimitDropError) Error() string {\n\treturn \"frame dropped silently due to rate limiting\"\n}\n"
  },
  {
    "path": "relay/relaytest/func_host.go",
    "content": "package relaytest\n\nimport (\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/relay\"\n)\n\n// Ensure that the hostFunc implements tchannel.RelayHost and hostFuncPeer implements\n// tchannel.RelayCall\nvar _ tchannel.RelayHost = (*hostFunc)(nil)\nvar _ tchannel.RelayCall = (*hostFuncPeer)(nil)\n\ntype hostFunc struct {\n\tch    *tchannel.Channel\n\tstats *MockStats\n\tfn    func(relay.CallFrame, *relay.Conn) (string, error)\n}\n\ntype hostFuncPeer struct {\n\t*MockCallStats\n\n\tpeer      *tchannel.Peer\n\trespFrame relay.RespFrame\n}\n\n// HostFunc wraps a given function to implement tchannel.RelayHost.\nfunc HostFunc(fn func(relay.CallFrame, *relay.Conn) (string, error)) tchannel.RelayHost {\n\treturn &hostFunc{fn: fn}\n}\n\nfunc (hf *hostFunc) SetChannel(ch *tchannel.Channel) {\n\thf.ch = ch\n\thf.stats = NewMockStats()\n}\n\nfunc (hf *hostFunc) Start(cf relay.CallFrame, conn *relay.Conn) (tchannel.RelayCall, error) {\n\tvar peer *tchannel.Peer\n\n\tpeerHP, err := hf.fn(cf, conn)\n\tif peerHP != \"\" {\n\t\tpeer = hf.ch.GetSubChannel(string(cf.Service())).Peers().GetOrAdd(peerHP)\n\t}\n\n\t// We still track stats if we failed to get a peer, so return the peer.\n\treturn &hostFuncPeer{MockCallStats: hf.stats.Begin(cf), peer: peer}, err\n}\n\nfunc (hf *hostFunc) Stats() *MockStats {\n\treturn hf.stats\n}\n\nfunc (p *hostFuncPeer) Destination() (*tchannel.Peer, bool) {\n\treturn p.peer, p.peer != nil\n}\n\nfunc (p *hostFuncPeer) CallResponse(frame relay.RespFrame) {\n\tp.respFrame = frame\n}\n"
  },
  {
    "path": "relay/relaytest/mock_stats.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage relaytest\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/uber/tchannel-go/relay\"\n)\n\n// MockCallStats is a testing spy for the CallStats interface.\ntype MockCallStats struct {\n\t// Store ints and slices instead of bools and strings so that we can assert\n\t// the actual sequence of calls (in case we expect to call both Succeeded\n\t// and Failed). The real implementation will have the first writer win.\n\tsucceeded  int\n\tfailedMsgs []string\n\tended      int\n\tsent       int\n\treceived   int\n\twg         *sync.WaitGroup\n}\n\n// Succeeded marks the RPC as succeeded.\nfunc (m *MockCallStats) Succeeded() {\n\tm.succeeded++\n}\n\n// Failed marks the RPC as failed for the provided reason.\nfunc (m *MockCallStats) Failed(reason string) {\n\tm.failedMsgs = append(m.failedMsgs, reason)\n}\n\n// SentBytes tracks the sent bytes.\nfunc (m *MockCallStats) SentBytes(size uint16) {\n\tm.sent += int(size)\n}\n\n// ReceivedBytes tracks the received bytes.\nfunc (m *MockCallStats) ReceivedBytes(size uint16) {\n\tm.received += int(size)\n}\n\n// End halts timer and metric collection for the RPC.\nfunc (m *MockCallStats) End() {\n\tm.ended++\n\tm.wg.Done()\n}\n\n// FluentMockCallStats wraps the MockCallStats in a fluent API that's convenient for tests.\ntype FluentMockCallStats struct {\n\t*MockCallStats\n}\n\n// Succeeded marks the RPC as succeeded.\nfunc (f *FluentMockCallStats) Succeeded() *FluentMockCallStats {\n\tf.MockCallStats.Succeeded()\n\treturn f\n}\n\n// Failed marks the RPC as failed.\nfunc (f *FluentMockCallStats) Failed(reason string) *FluentMockCallStats {\n\tf.MockCallStats.Failed(reason)\n\treturn f\n}\n\n// MockStats is a testing spy for the Stats interface.\ntype MockStats struct {\n\tmu    sync.Mutex\n\twg    sync.WaitGroup\n\tstats map[string][]*MockCallStats\n}\n\n// NewMockStats constructs a MockStats.\nfunc NewMockStats() *MockStats {\n\treturn &MockStats{\n\t\tstats: make(map[string][]*MockCallStats),\n\t}\n}\n\n// Begin starts collecting metrics for an RPC.\nfunc (m *MockStats) Begin(f relay.CallFrame) *MockCallStats {\n\treturn m.Add(string(f.Caller()), string(f.Service()), string(f.Method())).MockCallStats\n}\n\n// Add explicitly adds a new call along an edge of the call graph.\nfunc (m *MockStats) Add(caller, callee, procedure string) *FluentMockCallStats {\n\tm.wg.Add(1)\n\tcs := &MockCallStats{wg: &m.wg}\n\tkey := m.tripleToKey(caller, callee, procedure)\n\tm.mu.Lock()\n\tm.stats[key] = append(m.stats[key], cs)\n\tm.mu.Unlock()\n\treturn &FluentMockCallStats{cs}\n}\n\n// AssertEqual asserts that two MockStats describe the same call graph.\nfunc (m *MockStats) AssertEqual(t testing.TB, expected *MockStats) {\n\tm.WaitForEnd()\n\n\tm.mu.Lock()\n\tdefer m.mu.Unlock()\n\n\texpected.mu.Lock()\n\tdefer expected.mu.Unlock()\n\n\tif assert.Equal(t, getEdges(expected.stats), getEdges(m.stats), \"Found calls along unexpected edges.\") {\n\t\tfor edge := range expected.stats {\n\t\t\tm.assertEdgeEqual(t, expected, edge)\n\t\t}\n\t}\n}\n\n// WaitForEnd waits for all calls to End.\nfunc (m *MockStats) WaitForEnd() {\n\tm.wg.Wait()\n}\n\nfunc (m *MockStats) assertEdgeEqual(t testing.TB, expected *MockStats, edge string) {\n\texpectedCalls := expected.stats[edge]\n\tactualCalls := m.stats[edge]\n\tif assert.Equal(t, len(expectedCalls), len(actualCalls), \"Unexpected number of calls along %s edge.\", edge) {\n\t\tfor i := range expectedCalls {\n\t\t\tm.assertCallEqual(t, expectedCalls[i], actualCalls[i])\n\t\t}\n\t}\n}\n\nfunc (m *MockStats) assertCallEqual(t testing.TB, expected *MockCallStats, actual *MockCallStats) {\n\t// Revisit these assertions if we ever need to assert zero or many calls to\n\t// End.\n\trequire.Equal(t, 1, expected.ended, \"Expected call must assert exactly one call to End.\")\n\trequire.False(\n\t\tt,\n\t\texpected.succeeded <= 0 && len(expected.failedMsgs) == 0,\n\t\t\"Expectation must indicate whether RPC should succeed or fail.\",\n\t)\n\n\tfailed := !assert.Equal(t, expected.succeeded, actual.succeeded, \"Unexpected number of successes.\")\n\tfailed = !assert.Equal(t, expected.failedMsgs, actual.failedMsgs, \"Unexpected reasons for RPC failure.\") || failed\n\tfailed = !assert.Equal(t, expected.ended, actual.ended, \"Unexpected number of calls to End.\") || failed\n\n\tif failed {\n\t\t// The default testify output is often insufficient.\n\t\tt.Logf(\"\\nExpected relayed stats were:\\n\\t%+v\\nActual relayed stats were:\\n\\t%+v\\n\", expected, actual)\n\t}\n}\n\nfunc (m *MockStats) tripleToKey(caller, callee, procedure string) string {\n\treturn fmt.Sprintf(\"%s->%s::%s\", caller, callee, procedure)\n}\n\nfunc getEdges(m map[string][]*MockCallStats) []string {\n\tedges := make([]string, 0, len(m))\n\tfor k := range m {\n\t\tedges = append(edges, k)\n\t}\n\tsort.Strings(edges)\n\treturn edges\n}\n\n// Map returns all stats as a map of key to int.\n// It waits for any ongoing calls to end first to avoid races.\nfunc (m *MockStats) Map() map[string]int {\n\tm.WaitForEnd()\n\n\tm.mu.Lock()\n\tdefer m.mu.Unlock()\n\n\tstats := make(map[string]int)\n\tfor k, calls := range m.stats {\n\t\tfor _, call := range calls {\n\t\t\tname := k\n\n\t\t\tstats[name+\".calls\"]++\n\t\t\tif call.ended > 0 {\n\t\t\t\tstats[name+\".ended\"]++\n\t\t\t}\n\t\t\tif call.succeeded > 0 {\n\t\t\t\tstats[name+\".succeeded\"]++\n\t\t\t}\n\t\t\tif len(call.failedMsgs) > 0 {\n\t\t\t\tfailureName := name + \".failed-\" + strings.Join(call.failedMsgs, \",\")\n\t\t\t\tstats[failureName]++\n\t\t\t}\n\t\t\tstats[name+\".sent-bytes\"] = call.sent\n\t\t\tstats[name+\".received-bytes\"] = call.received\n\t\t}\n\t}\n\treturn stats\n}\n"
  },
  {
    "path": "relay/relaytest/stub_host.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage relaytest\n\nimport (\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/relay\"\n)\n\n// Ensure that the StubRelayHost implements tchannel.RelayHost and stubCall implements\n// tchannel.RelayCall\nvar _ tchannel.RelayHost = (*StubRelayHost)(nil)\nvar _ tchannel.RelayCall = (*stubCall)(nil)\n\n// StubRelayHost is a stub RelayHost for tests that backs peer selection to an\n// underlying channel using isolated subchannels and the default peer selection.\ntype StubRelayHost struct {\n\tch          *tchannel.Channel\n\tstats       *MockStats\n\tframeFn     func(relay.CallFrame, *relay.Conn)\n\trespFrameFn func(relay.RespFrame)\n}\n\ntype stubCall struct {\n\t*MockCallStats\n\n\tpeer        *tchannel.Peer\n\trespFrameFn func(relay.RespFrame)\n}\n\n// NewStubRelayHost creates a new stub RelayHost for tests.\nfunc NewStubRelayHost() *StubRelayHost {\n\treturn &StubRelayHost{\n\t\tstats:       NewMockStats(),\n\t\trespFrameFn: func(_ relay.RespFrame) {},\n\t}\n}\n\n// SetFrameFn sets a function to run on every frame.\nfunc (rh *StubRelayHost) SetFrameFn(f func(relay.CallFrame, *relay.Conn)) {\n\trh.frameFn = f\n}\n\n// SetRespFrameFn sets a function to run on every frame.\nfunc (rh *StubRelayHost) SetRespFrameFn(f func(relay.RespFrame)) {\n\trh.respFrameFn = f\n}\n\n// SetChannel is called by the channel after creation so we can\n// get a reference to the channels' peers.\nfunc (rh *StubRelayHost) SetChannel(ch *tchannel.Channel) {\n\trh.ch = ch\n}\n\n// Start starts a new RelayCall for the given call on a specific connection.\nfunc (rh *StubRelayHost) Start(cf relay.CallFrame, conn *relay.Conn) (tchannel.RelayCall, error) {\n\tif rh.frameFn != nil {\n\t\trh.frameFn(cf, conn)\n\t}\n\n\t// Get a peer from the subchannel.\n\tpeer, err := rh.ch.GetSubChannel(string(cf.Service())).Peers().Get(nil)\n\treturn &stubCall{\n\t\tMockCallStats: rh.stats.Begin(cf),\n\t\tpeer:          peer,\n\t\trespFrameFn:   rh.respFrameFn,\n\t}, err\n}\n\n// Add adds a service instance with the specified host:port.\nfunc (rh *StubRelayHost) Add(service, hostPort string) {\n\trh.ch.GetSubChannel(service, tchannel.Isolated).Peers().GetOrAdd(hostPort)\n}\n\n// Stats returns the *MockStats tracked for this channel.\nfunc (rh *StubRelayHost) Stats() *MockStats {\n\treturn rh.stats\n}\n\n// Destination returns the selected peer for this call.\nfunc (c *stubCall) Destination() (*tchannel.Peer, bool) {\n\treturn c.peer, c.peer != nil\n}\n\nfunc (c *stubCall) CallResponse(frame relay.RespFrame) {\n\tc.respFrameFn(frame)\n}\n"
  },
  {
    "path": "relay.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/relay\"\n\t\"github.com/uber/tchannel-go/typed\"\n\t\"go.uber.org/atomic\"\n)\n\nconst (\n\t// _defaultRelayMaxTombs is the default maximum number of tombs we'll accumulate\n\t// in a single relayItems.\n\t_defaultRelayMaxTombs = 3e4\n\t// _relayTombTTL is the length of time we'll keep a tomb before GC'ing it.\n\t_relayTombTTL = 3 * time.Second\n\t// _defaultRelayMaxTimeout is the default max TTL for relayed calls.\n\t_defaultRelayMaxTimeout = 2 * time.Minute\n)\n\n// Error strings.\nconst (\n\t_relayErrorNotFound       = \"relay-not-found\"\n\t_relayErrorDestConnSlow   = \"relay-dest-conn-slow\"\n\t_relayErrorSourceConnSlow = \"relay-source-conn-slow\"\n\t_relayArg2ModifyFailed    = \"relay-arg2-modify-failed\"\n\n\t// _relayNoRelease indicates that the relayed frame should not be released immediately, since\n\t// relayed frames normally end up in a send queue where it is released afterward. However in some\n\t// cases, such as frames that are fragmented due to being mutated, we need to release the original\n\t// frame as it won't be relayed.\n\t_relayNoRelease     = false\n\t_relayShouldRelease = true\n)\n\n// TODO: Replace errFrameNotSent with more specific errors from Receive.\nvar (\n\terrRelayMethodFragmented    = NewSystemError(ErrCodeBadRequest, \"relay handler cannot receive fragmented calls\")\n\terrFrameNotSent             = NewSystemError(ErrCodeNetwork, \"frame was not sent to remote side\")\n\terrBadRelayHost             = NewSystemError(ErrCodeDeclined, \"bad relay host implementation\")\n\terrUnknownID                = errors.New(\"non-callReq for inactive ID\")\n\terrNoNHInArg2               = errors.New(\"no nh in arg2\")\n\terrFragmentedArg2WithAppend = errors.New(\"fragmented arg2 not supported for appends\")\n\terrArg2ThriftOnly           = errors.New(\"cannot inspect or modify arg2 for non-Thrift calls\")\n)\n\ntype relayItem struct {\n\tremapID         uint32\n\ttomb            bool\n\tisOriginator    bool\n\tcall            RelayCall\n\tdestination     *Relayer\n\tspan            Span\n\ttimeout         *relayTimer\n\tmutatedChecksum Checksum\n}\n\ntype relayItems struct {\n\tsync.RWMutex\n\n\tlogger   Logger\n\ttimeouts *relayTimerPool\n\tmaxTombs uint64\n\ttombs    uint64\n\titems    map[uint32]relayItem\n}\n\ntype frameReceiver interface {\n\tReceive(f *Frame, fType frameType) (sent bool, failureReason string)\n}\n\nfunc newRelayItems(logger Logger, maxTombs uint64) *relayItems {\n\tif maxTombs == 0 {\n\t\tmaxTombs = _defaultRelayMaxTombs\n\t}\n\treturn &relayItems{\n\t\titems:    make(map[uint32]relayItem),\n\t\tlogger:   logger,\n\t\tmaxTombs: maxTombs,\n\t}\n}\n\nfunc (ri *relayItem) reportRelayBytes(fType frameType, frameSize uint16) {\n\tif fType == requestFrame {\n\t\tri.call.SentBytes(frameSize)\n\t} else {\n\t\tri.call.ReceivedBytes(frameSize)\n\t}\n}\n\n// Count returns the number of non-tombstone items in the relay.\nfunc (r *relayItems) Count() int {\n\tr.RLock()\n\tn := len(r.items) - int(r.tombs)\n\tr.RUnlock()\n\treturn n\n}\n\n// Get checks for a relay item by ID, and will stop the timeout with the\n// read lock held (to avoid a race between timeout stop and deletion).\n// It returns whether a timeout was stopped, and if the item was found.\nfunc (r *relayItems) Get(id uint32, stopTimeout bool) (_ relayItem, stopped bool, found bool) {\n\tr.RLock()\n\tdefer r.RUnlock()\n\n\titem, ok := r.items[id]\n\tif !ok {\n\t\treturn relayItem{}, false /* stopped */, false /* found */\n\t}\n\n\tif !stopTimeout {\n\t\treturn item, false /* stopped */, true /* found */\n\t}\n\n\treturn item, item.timeout.Stop(), true /* found */\n}\n\n// Add adds a relay item.\nfunc (r *relayItems) Add(id uint32, item relayItem) {\n\tr.Lock()\n\tr.items[id] = item\n\tr.Unlock()\n}\n\n// Delete removes a relayItem completely (without leaving a tombstone). It\n// returns the deleted item, along with a bool indicating whether we completed a\n// relayed call.\nfunc (r *relayItems) Delete(id uint32) (relayItem, bool) {\n\tr.Lock()\n\titem, ok := r.items[id]\n\tif !ok {\n\t\tr.Unlock()\n\t\tr.logger.WithFields(LogField{\"id\", id}).Warn(\"Attempted to delete non-existent relay item.\")\n\t\treturn item, false\n\t}\n\tdelete(r.items, id)\n\tif item.tomb {\n\t\tr.tombs--\n\t}\n\tr.Unlock()\n\n\titem.timeout.Release()\n\treturn item, !item.tomb\n}\n\n// Entomb sets the tomb bit on a relayItem and schedules a garbage collection. It\n// returns the entombed item, along with a bool indicating whether we completed\n// a relayed call.\nfunc (r *relayItems) Entomb(id uint32, deleteAfter time.Duration) (relayItem, bool) {\n\tr.Lock()\n\tif r.tombs > r.maxTombs {\n\t\tr.Unlock()\n\t\tr.logger.WithFields(LogField{\"id\", id}).Warn(\"Too many tombstones, deleting relay item immediately.\")\n\t\treturn r.Delete(id)\n\t}\n\titem, ok := r.items[id]\n\tif !ok {\n\t\tr.Unlock()\n\t\tr.logger.WithFields(LogField{\"id\", id}).Warn(\"Can't find relay item to entomb.\")\n\t\treturn item, false\n\t}\n\tif item.tomb {\n\t\tr.Unlock()\n\t\tr.logger.WithFields(LogField{\"id\", id}).Warn(\"Re-entombing a tombstone.\")\n\t\treturn item, false\n\t}\n\tr.tombs++\n\titem.tomb = true\n\tr.items[id] = item\n\tr.Unlock()\n\n\t// TODO: We should be clearing these out in batches, rather than creating\n\t// individual timers for each item.\n\ttime.AfterFunc(deleteAfter, func() { r.Delete(id) })\n\treturn item, true\n}\n\ntype frameType int\n\nconst (\n\trequestFrame  frameType = 0\n\tresponseFrame frameType = 1\n)\n\nvar _ frameReceiver = (*Relayer)(nil)\n\n// A Relayer forwards frames.\ntype Relayer struct {\n\trelayHost      RelayHost\n\tmaxTimeout     time.Duration\n\tmaxConnTimeout time.Duration\n\n\t// localHandlers is the set of service names that are handled by the local\n\t// channel.\n\tlocalHandler map[string]struct{}\n\n\t// outbound is the remapping for requests that originated on this\n\t// connection, and are outbound towards some other connection.\n\t// It stores remappings for all request frames read on this connection.\n\toutbound *relayItems\n\n\t// inbound is the remapping for requests that originated on some other\n\t// connection which was directed to this connection.\n\t// It stores remappings for all response frames read on this connection.\n\tinbound *relayItems\n\n\t// timeouts is the pool of timers used to track call timeouts.\n\t// It allows timer re-use, while allowing timers to be created and started separately.\n\ttimeouts *relayTimerPool\n\n\tpeers     *RootPeerList\n\tconn      *Connection\n\trelayConn *relay.Conn\n\tlogger    Logger\n\tpending   atomic.Uint32\n}\n\n// NewRelayer constructs a Relayer.\nfunc NewRelayer(ch *Channel, conn *Connection) *Relayer {\n\tr := &Relayer{\n\t\trelayHost:      ch.RelayHost(),\n\t\tmaxTimeout:     ch.relayMaxTimeout,\n\t\tmaxConnTimeout: ch.relayMaxConnTimeout,\n\t\tlocalHandler:   ch.relayLocal,\n\t\toutbound:       newRelayItems(conn.log.WithFields(LogField{\"relayItems\", \"outbound\"}), ch.relayMaxTombs),\n\t\tinbound:        newRelayItems(conn.log.WithFields(LogField{\"relayItems\", \"inbound\"}), ch.relayMaxTombs),\n\t\tpeers:          ch.RootPeers(),\n\t\tconn:           conn,\n\t\trelayConn: &relay.Conn{\n\t\t\tRemoteAddr:        conn.conn.RemoteAddr().String(),\n\t\t\tRemoteProcessName: conn.RemotePeerInfo().ProcessName,\n\t\t\tIsOutbound:        conn.connDirection == outbound,\n\t\t\tContext:           conn.baseContext,\n\t\t},\n\t\tlogger: conn.log,\n\t}\n\tr.timeouts = newRelayTimerPool(r.timeoutRelayItem, ch.relayTimerVerify)\n\treturn r\n}\n\n// Relay is called for each frame that is read on the connection.\nfunc (r *Relayer) Relay(f *Frame) (shouldRelease bool, _ error) {\n\tif f.messageType() != messageTypeCallReq {\n\t\tshouldRelease, err := r.handleNonCallReq(f)\n\t\tif err == errUnknownID {\n\t\t\t// This ID may be owned by an outgoing call, so check the outbound\n\t\t\t// message exchange, and if it succeeds, then the frame has been\n\t\t\t// handled successfully.\n\t\t\tif err := r.conn.outbound.forwardPeerFrame(f); err == nil {\n\t\t\t\treturn _relayNoRelease, nil\n\t\t\t}\n\t\t}\n\t\treturn shouldRelease, err\n\t}\n\n\tcr, err := newLazyCallReq(f)\n\tif err != nil {\n\t\treturn _relayNoRelease, err\n\t}\n\n\treturn r.handleCallReq(cr)\n}\n\n// Receive receives frames intended for this connection.\n// It returns whether the frame was sent and a reason for failure if it failed.\nfunc (r *Relayer) Receive(f *Frame, fType frameType) (sent bool, failureReason string) {\n\tid := f.Header.ID\n\n\t// If we receive a response frame, we expect to find that ID in our outbound.\n\t// If we receive a request frame, we expect to find that ID in our inbound.\n\titems := r.receiverItems(fType)\n\tfinished := finishesCall(f)\n\n\t// Stop the timeout if the call if finished.\n\titem, stopped, ok := items.Get(id, finished /* stopTimeout */)\n\tif !ok {\n\t\tr.logger.WithFields(\n\t\t\tLogField{\"id\", id},\n\t\t).Warn(\"Received a frame without a RelayItem.\")\n\t\treturn false, _relayErrorNotFound\n\t}\n\tif item.tomb || (finished && !stopped) {\n\t\t// Item has previously timed out, or is in the process of timing out.\n\t\t// TODO: metrics for late-arriving frames.\n\t\treturn true, \"\"\n\t}\n\n\t// call res frames don't include the OK bit, so we can't wait until the last\n\t// frame of a relayed RPC to determine if the call succeeded.\n\tif fType == responseFrame || f.messageType() == messageTypeCancel {\n\t\t// If we've gotten a response frame, we're the originating relayer and\n\t\t// should handle stats.\n\t\tif succeeded, failMsg := determinesCallSuccess(f); succeeded {\n\t\t\titem.call.Succeeded()\n\t\t} else if len(failMsg) > 0 {\n\t\t\titem.call.Failed(failMsg)\n\t\t}\n\t}\n\tselect {\n\tcase r.conn.sendCh <- f:\n\tdefault:\n\t\t// Buffer is full, so drop this frame and cancel the call.\n\n\t\t// Since this is typically due to the send buffer being full, get send buffer\n\t\t// usage + limit and add that to the log.\n\t\tsendBuf, sendBufLimit, sendBufErr := r.conn.sendBufSize()\n\t\tnow := r.conn.timeNow().UnixNano()\n\t\tlogFields := []LogField{\n\t\t\t{\"id\", id},\n\t\t\t{\"destConnSendBufferCurrent\", sendBuf},\n\t\t\t{\"destConnSendBufferLimit\", sendBufLimit},\n\t\t\t{\"sendChQueued\", len(r.conn.sendCh)},\n\t\t\t{\"sendChCapacity\", cap(r.conn.sendCh)},\n\t\t\t{\"lastActivityRead\", r.conn.lastActivityRead.Load()},\n\t\t\t{\"lastActivityWrite\", r.conn.lastActivityRead.Load()},\n\t\t\t{\"sinceLastActivityRead\", time.Duration(now - r.conn.lastActivityRead.Load()).String()},\n\t\t\t{\"sinceLastActivityWrite\", time.Duration(now - r.conn.lastActivityWrite.Load()).String()},\n\t\t}\n\t\tif sendBufErr != nil {\n\t\t\tlogFields = append(logFields, LogField{\"destConnSendBufferError\", sendBufErr.Error()})\n\t\t}\n\n\t\tr.logger.WithFields(logFields...).Warn(\"Dropping call due to slow connection.\")\n\n\t\titems := r.receiverItems(fType)\n\n\t\terr := _relayErrorDestConnSlow\n\t\t// If we're dealing with a response frame, then the client is slow.\n\t\tif fType == responseFrame {\n\t\t\terr = _relayErrorSourceConnSlow\n\t\t}\n\n\t\tr.failRelayItem(items, id, err, errFrameNotSent)\n\t\treturn false, err\n\t}\n\n\tif finished {\n\t\tr.finishRelayItem(items, id)\n\t}\n\n\treturn true, \"\"\n}\n\nfunc (r *Relayer) canHandleNewCall() (bool, connectionState) {\n\tvar (\n\t\tcanHandle bool\n\t\tcurState  connectionState\n\t)\n\n\tr.conn.withStateRLock(func() error {\n\t\tcurState = r.conn.state\n\t\tcanHandle = curState == connectionActive\n\t\tif canHandle {\n\t\t\tr.pending.Inc()\n\t\t}\n\t\treturn nil\n\t})\n\treturn canHandle, curState\n}\n\nfunc (r *Relayer) getDestination(f *lazyCallReq, call RelayCall) (*Connection, bool, error) {\n\tif _, _, ok := r.outbound.Get(f.Header.ID, false /* stopTimeout */); ok {\n\t\tr.logger.WithFields(\n\t\t\tLogField{\"id\", f.Header.ID},\n\t\t\tLogField{\"source\", string(f.Caller())},\n\t\t\tLogField{\"dest\", string(f.Service())},\n\t\t\tLogField{\"method\", string(f.Method())},\n\t\t).Warn(\"Received duplicate callReq.\")\n\t\tcall.Failed(ErrCodeProtocol.relayMetricsKey())\n\t\t// TODO: this is a protocol error, kill the connection.\n\t\treturn nil, false, errors.New(\"callReq with already active ID\")\n\t}\n\n\t// Get the destination\n\tpeer, ok := call.Destination()\n\tif !ok {\n\t\tcall.Failed(\"relay-bad-relay-host\")\n\t\tr.conn.SendSystemError(f.Header.ID, f.Span(), errBadRelayHost)\n\t\treturn nil, false, errBadRelayHost\n\t}\n\n\tremoteConn, err := peer.getConnectionRelay(f.TTL(), r.maxConnTimeout)\n\tif err != nil {\n\t\tr.logger.WithFields(\n\t\t\tErrField(err),\n\t\t\tLogField{\"source\", string(f.Caller())},\n\t\t\tLogField{\"dest\", string(f.Service())},\n\t\t\tLogField{\"method\", string(f.Method())},\n\t\t\tLogField{\"selectedPeer\", peer},\n\t\t).Warn(\"Failed to connect to relay host.\")\n\t\tcall.Failed(\"relay-connection-failed\")\n\t\tr.conn.SendSystemError(f.Header.ID, f.Span(), NewWrappedSystemError(ErrCodeNetwork, err))\n\t\treturn nil, false, nil\n\t}\n\n\treturn remoteConn, true, nil\n}\n\nfunc (r *Relayer) handleCallReq(f *lazyCallReq) (shouldRelease bool, _ error) {\n\tif handled := r.handleLocalCallReq(f); handled {\n\t\treturn _relayNoRelease, nil\n\t}\n\n\tcall, err := r.relayHost.Start(f, r.relayConn)\n\tif err != nil {\n\t\t// If we have a RateLimitDropError we record the statistic, but\n\t\t// we *don't* send an error frame back to the client.\n\t\tif _, silentlyDrop := err.(relay.RateLimitDropError); silentlyDrop {\n\t\t\tif call != nil {\n\t\t\t\tcall.Failed(\"relay-dropped\")\n\t\t\t\tcall.End()\n\t\t\t}\n\t\t\treturn _relayShouldRelease, nil\n\t\t}\n\t\tif _, ok := err.(SystemError); !ok {\n\t\t\terr = NewSystemError(ErrCodeDeclined, err.Error())\n\t\t}\n\t\tif call != nil {\n\t\t\tcall.Failed(GetSystemErrorCode(err).relayMetricsKey())\n\t\t\tcall.End()\n\t\t}\n\t\tr.conn.SendSystemError(f.Header.ID, f.Span(), err)\n\n\t\t// If the RelayHost returns a protocol error, close the connection.\n\t\tif GetSystemErrorCode(err) == ErrCodeProtocol {\n\t\t\treturn _relayShouldRelease, r.conn.close(LogField{\"reason\", \"RelayHost returned protocol error\"})\n\t\t}\n\t\treturn _relayShouldRelease, nil\n\t}\n\n\t// Check that the current connection is in a valid state to handle a new call.\n\tif canHandle, state := r.canHandleNewCall(); !canHandle {\n\t\tcall.Failed(\"relay-client-conn-inactive\")\n\t\tcall.End()\n\t\terr := errConnNotActive{\"incoming\", state}\n\t\tr.conn.SendSystemError(f.Header.ID, f.Span(), NewWrappedSystemError(ErrCodeDeclined, err))\n\t\treturn _relayShouldRelease, err\n\t}\n\n\t// Get a remote connection and check whether it can handle this call.\n\tremoteConn, ok, err := r.getDestination(f, call)\n\tif err == nil && ok {\n\t\tif canHandle, state := remoteConn.relay.canHandleNewCall(); !canHandle {\n\t\t\terr = NewWrappedSystemError(ErrCodeNetwork, errConnNotActive{\"selected remote\", state})\n\t\t\tcall.Failed(\"relay-remote-inactive\")\n\t\t\tr.conn.SendSystemError(f.Header.ID, f.Span(), NewWrappedSystemError(ErrCodeDeclined, err))\n\t\t}\n\t}\n\tif err != nil || !ok {\n\t\t// Failed to get a remote connection, or the connection is not in the right\n\t\t// state to handle this call. Since we already incremented pending on\n\t\t// the current relay, we need to decrement it.\n\t\tr.decrementPending()\n\t\tcall.End()\n\t\treturn _relayShouldRelease, err\n\t}\n\n\torigID := f.Header.ID\n\tdestinationID := remoteConn.NextMessageID()\n\tttl := f.TTL()\n\tif ttl > r.maxTimeout {\n\t\tttl = r.maxTimeout\n\t\tf.SetTTL(r.maxTimeout)\n\t}\n\tspan := f.Span()\n\n\tvar mutatedChecksum Checksum\n\tif len(f.arg2Appends) > 0 {\n\t\tmutatedChecksum = f.checksumType.New()\n\t}\n\n\t// The remote side of the relay doesn't need to track stats or call state.\n\tremoteConn.relay.addRelayItem(false /* isOriginator */, destinationID, f.Header.ID, r, ttl, span, call, nil /* mutatedChecksum */)\n\trelayToDest := r.addRelayItem(true /* isOriginator */, f.Header.ID, destinationID, remoteConn.relay, ttl, span, call, mutatedChecksum)\n\n\tf.Header.ID = destinationID\n\n\t// If we have appends, the size of the frame to be relayed will change, potentially going\n\t// over the max frame size. Do a fragmenting send which is slightly more expensive but\n\t// will handle fragmenting if it is needed.\n\tif len(f.arg2Appends) > 0 {\n\t\tif err := r.fragmentingSend(call, f, relayToDest, origID); err != nil {\n\t\t\tr.failRelayItem(r.outbound, origID, _relayArg2ModifyFailed, err)\n\t\t\tr.logger.WithFields(\n\t\t\t\tLogField{\"id\", origID},\n\t\t\t\tLogField{\"err\", err.Error()},\n\t\t\t\tLogField{\"caller\", string(f.Caller())},\n\t\t\t\tLogField{\"dest\", string(f.Service())},\n\t\t\t\tLogField{\"method\", string(f.Method())},\n\t\t\t).Warn(\"Failed to send call with modified arg2.\")\n\t\t}\n\n\t\t// fragmentingSend always sends new frames in place of the old frame so we must\n\t\t// release it separately\n\t\treturn _relayShouldRelease, nil\n\t}\n\n\tcall.SentBytes(f.Frame.Header.FrameSize())\n\tsent, failure := relayToDest.destination.Receive(f.Frame, requestFrame)\n\tif !sent {\n\t\tr.failRelayItem(r.outbound, origID, failure, errFrameNotSent)\n\t\treturn _relayShouldRelease, nil\n\t}\n\treturn _relayNoRelease, nil\n}\n\n// Handle all frames except messageTypeCallReq.\nfunc (r *Relayer) handleNonCallReq(f *Frame) (shouldRelease bool, _ error) {\n\tframeType := frameTypeFor(f)\n\tfinished := finishesCall(f)\n\n\t// If we read a request frame, we need to use the outbound map to decide\n\t// the destination. Otherwise, we use the inbound map.\n\titems := r.outbound\n\tif frameType == responseFrame {\n\t\titems = r.inbound\n\t}\n\n\t// Stop the timeout if the call if finished.\n\titem, stopped, ok := items.Get(f.Header.ID, finished /* stopTimeout */)\n\tif !ok {\n\t\treturn _relayShouldRelease, errUnknownID\n\t}\n\tif item.tomb || (finished && !stopped) {\n\t\t// Item has previously timed out, or is in the process of timing out.\n\t\t// TODO: metrics for late-arriving frames.\n\t\treturn _relayShouldRelease, nil\n\t}\n\n\tswitch f.messageType() {\n\tcase messageTypeCallRes:\n\t\t// Invoke call.CallResponse() if we get a valid call response frame.\n\t\tcr, err := newLazyCallRes(f)\n\t\tif err == nil {\n\t\t\titem.call.CallResponse(cr)\n\t\t} else {\n\t\t\tr.logger.WithFields(\n\t\t\t\tErrField(err),\n\t\t\t\tLogField{\"id\", f.Header.ID},\n\t\t\t).Error(\"Malformed callRes frame.\")\n\t\t}\n\tcase messageTypeCallReqContinue:\n\t\t// Recalculate and update the checksum for this frame if it has non-nil item.mutatedChecksum\n\t\t// (meaning the call was mutated) and it is a callReqContinue frame.\n\t\tif item.mutatedChecksum != nil {\n\t\t\tr.updateMutatedCallReqContinueChecksum(f, item.mutatedChecksum)\n\t\t}\n\t}\n\n\t// Track sent/received bytes. We don't do this before we check\n\t// for timeouts, since this should only be called before call.End().\n\titem.reportRelayBytes(frameType, f.Header.FrameSize())\n\n\toriginalID := f.Header.ID\n\tf.Header.ID = item.remapID\n\n\tsent, failure := item.destination.Receive(f, frameType)\n\tif !sent {\n\t\tr.failRelayItem(items, originalID, failure, errFrameNotSent)\n\t\treturn _relayShouldRelease, nil\n\t}\n\n\tif finished {\n\t\tr.finishRelayItem(items, originalID)\n\t}\n\treturn _relayNoRelease, nil\n}\n\n// addRelayItem adds a relay item to either outbound or inbound.\nfunc (r *Relayer) addRelayItem(isOriginator bool, id, remapID uint32, destination *Relayer, ttl time.Duration, span Span, call RelayCall, mutatedChecksum Checksum) relayItem {\n\titem := relayItem{\n\t\tisOriginator:    isOriginator,\n\t\tcall:            call,\n\t\tremapID:         remapID,\n\t\tdestination:     destination,\n\t\tspan:            span,\n\t\tmutatedChecksum: mutatedChecksum,\n\t}\n\n\titems := r.inbound\n\tif isOriginator {\n\t\titems = r.outbound\n\t}\n\titem.timeout = r.timeouts.Get()\n\titems.Add(id, item)\n\titem.timeout.Start(ttl, items, id, isOriginator)\n\treturn item\n}\n\nfunc (r *Relayer) timeoutRelayItem(items *relayItems, id uint32, isOriginator bool) {\n\titem, ok := items.Entomb(id, _relayTombTTL)\n\tif !ok {\n\t\treturn\n\t}\n\tif isOriginator {\n\t\tr.conn.SendSystemError(id, item.span, ErrTimeout)\n\t\titem.call.Failed(\"timeout\")\n\t\titem.call.End()\n\t}\n\n\tr.decrementPending()\n}\n\n// failRelayItem tombs the relay item so that future frames for this call are not\n// forwarded. We keep the relay item tombed, rather than delete it to ensure that\n// future frames do not cause error logs.\nfunc (r *Relayer) failRelayItem(items *relayItems, id uint32, reason string, err error) {\n\t// Stop the timeout, so we either fail it here, or in the timeout goroutine but not both.\n\titem, stopped, found := items.Get(id, true /* stopTimeout */)\n\tif !found {\n\t\titems.logger.WithFields(LogField{\"id\", id}).Warn(\"Attempted to fail non-existent relay item.\")\n\t\treturn\n\t}\n\tif !stopped {\n\t\treturn\n\t}\n\n\t// Entomb it so that we don't get unknown exchange errors on further frames\n\t// for this call.\n\titem, ok := items.Entomb(id, _relayTombTTL)\n\tif !ok {\n\t\treturn\n\t}\n\tif item.isOriginator {\n\t\t// If the client is too slow, then there's no point sending an error frame.\n\t\tif reason != _relayErrorSourceConnSlow {\n\t\t\tr.conn.SendSystemError(id, item.span, fmt.Errorf(\"%v: %v\", reason, err))\n\t\t}\n\t\titem.call.Failed(reason)\n\t\titem.call.End()\n\t}\n\n\tr.decrementPending()\n}\n\nfunc (r *Relayer) finishRelayItem(items *relayItems, id uint32) {\n\titem, ok := items.Delete(id)\n\tif !ok {\n\t\treturn\n\t}\n\tif item.isOriginator {\n\t\titem.call.End()\n\t\tif item.mutatedChecksum != nil {\n\t\t\titem.mutatedChecksum.Release()\n\t\t}\n\t}\n\tr.decrementPending()\n}\n\nfunc (r *Relayer) decrementPending() {\n\tr.pending.Dec()\n\tr.conn.checkExchanges()\n}\n\nfunc (r *Relayer) canClose() bool {\n\tif r == nil {\n\t\treturn true\n\t}\n\treturn r.countPending() == 0\n}\n\nfunc (r *Relayer) countPending() uint32 {\n\treturn r.pending.Load()\n}\n\nfunc (r *Relayer) receiverItems(fType frameType) *relayItems {\n\tif fType == requestFrame {\n\t\treturn r.inbound\n\t}\n\treturn r.outbound\n}\n\nfunc (r *Relayer) handleLocalCallReq(cr *lazyCallReq) (shouldRelease bool) {\n\t// Check whether this is a service we want to handle locally.\n\tif _, ok := r.localHandler[string(cr.Service())]; !ok {\n\t\treturn _relayNoRelease\n\t}\n\n\tf := cr.Frame\n\n\t// We can only handle non-fragmented calls in the relay channel.\n\t// This is a simplification to avoid back references from a mex to a\n\t// relayItem so that the relayItem is cleared when the call completes.\n\tif cr.HasMoreFragments() {\n\t\tr.logger.WithFields(\n\t\t\tLogField{\"id\", cr.Header.ID},\n\t\t\tLogField{\"source\", string(cr.Caller())},\n\t\t\tLogField{\"dest\", string(cr.Service())},\n\t\t\tLogField{\"method\", string(cr.Method())},\n\t\t).Error(\"Received fragmented callReq intended for local relay channel, can only handle unfragmented calls.\")\n\t\tr.conn.SendSystemError(f.Header.ID, cr.Span(), errRelayMethodFragmented)\n\t\treturn _relayShouldRelease\n\t}\n\n\tif release := r.conn.handleFrameNoRelay(f); release {\n\t\tr.conn.opts.FramePool.Release(f)\n\t}\n\treturn _relayShouldRelease\n}\n\nfunc (r *Relayer) fragmentingSend(call RelayCall, f *lazyCallReq, relayToDest relayItem, origID uint32) error {\n\tif f.isArg2Fragmented {\n\t\treturn errFragmentedArg2WithAppend\n\t}\n\tif !bytes.Equal(f.as, _tchanThriftValueBytes) {\n\t\treturn fmt.Errorf(\"%v: got %s\", errArg2ThriftOnly, f.as)\n\t}\n\n\tcs := relayToDest.mutatedChecksum\n\n\t// TODO(echung): should we pool the writers?\n\tfragWriter := newFragmentingWriter(\n\t\tr.logger, r.newFragmentSender(relayToDest.destination, f, origID, call),\n\t\tcs,\n\t)\n\n\targ2Writer, err := fragWriter.ArgWriter(false /* last */)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get arg2 writer: %v\", err)\n\t}\n\n\tif err := writeArg2WithAppends(arg2Writer, f.arg2(), f.arg2Appends); err != nil {\n\t\treturn fmt.Errorf(\"write arg2: %v\", err)\n\t}\n\tif err := arg2Writer.Close(); err != nil {\n\t\treturn fmt.Errorf(\"close arg2 writer: %v\", err)\n\t}\n\n\tif err := NewArgWriter(fragWriter.ArgWriter(true /* last */)).Write(f.arg3()); err != nil {\n\t\treturn errors.New(\"arg3 write failed\")\n\t}\n\n\treturn nil\n}\n\nfunc (r *Relayer) updateMutatedCallReqContinueChecksum(f *Frame, cs Checksum) {\n\trbuf := typed.NewReadBuffer(f.SizedPayload())\n\trbuf.SkipBytes(1) // flags\n\trbuf.SkipBytes(1) // checksum type: this should match the checksum type of the callReq frame\n\n\tchecksumRef := typed.BytesRef(rbuf.ReadBytes(cs.Size()))\n\n\t// We only support non-fragmented arg2 for mutated calls, so by the time we hit callReqContinue both\n\t// arg1 and arg2 must already have been read. As the call would be finished when we've read all of\n\t// arg3, it isn't necessary to separately track its completion.\n\t//\n\t// In theory we could have a frame with 0-length arg3, which can happen if a manual flush occurred\n\t// after writing 0 bytes for arg3. This is handled correctly by\n\t// 1) reading n=0 (nArg3)\n\t// 2) reading 0 bytes from the rbuf\n\t// 3) updating the checksum with the current running checksum\n\t//\n\t// Additionally, if the checksum type results in a 0-length checksum, the .Update() would\n\t// become a copy between empty slices, which correctly becomes a noop.\n\t// TODO(cinchurge): include a test for len(arg3)==0 in the unit tests\n\tn := rbuf.ReadUint16()\n\tcs.Add(rbuf.ReadBytes(int(n)))\n\tchecksumRef.Update(cs.Sum())\n}\n\nfunc writeArg2WithAppends(w io.WriteCloser, arg2 []byte, appends []relay.KeyVal) (err error) {\n\tif len(arg2) < 2 {\n\t\treturn errNoNHInArg2\n\t}\n\n\twriter := typed.NewWriter(w)\n\n\t// nh:2 is the first two bytes of arg2, which should always be present\n\tnh := binary.BigEndian.Uint16(arg2[:2]) + uint16(len(appends))\n\twriter.WriteUint16(nh)\n\n\t// arg2[2:] is the existing sequence of key/val pairs, which we can just copy\n\t// over verbatim\n\tif len(arg2) > 2 {\n\t\twriter.WriteBytes(arg2[2:])\n\t}\n\n\t// append new key/val pairs to end of arg2\n\tfor _, kv := range appends {\n\t\twriter.WriteLen16Bytes(kv.Key)\n\t\twriter.WriteLen16Bytes(kv.Val)\n\t}\n\n\treturn writer.Err()\n}\n\nfunc frameTypeFor(f *Frame) frameType {\n\tswitch t := f.Header.messageType; t {\n\tcase messageTypeCallRes, messageTypeCallResContinue, messageTypeError, messageTypePingRes:\n\t\treturn responseFrame\n\tcase messageTypeCallReq, messageTypeCallReqContinue, messageTypePingReq, messageTypeCancel:\n\t\treturn requestFrame\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unsupported frame type: %v\", t))\n\t}\n}\n\nfunc determinesCallSuccess(f *Frame) (succeeded bool, failMsg string) {\n\tswitch f.messageType() {\n\tcase messageTypeError:\n\t\tmsg := newLazyError(f).Code().MetricsKey()\n\t\treturn false, msg\n\tcase messageTypeCancel:\n\t\treturn false, \"canceled\"\n\tcase messageTypeCallRes:\n\t\tif isCallResOK(f) {\n\t\t\treturn true, \"\"\n\t\t}\n\t\treturn false, \"application-error\"\n\tdefault:\n\t\treturn false, \"\"\n\t}\n}\n\nfunc validateRelayMaxTimeout(d time.Duration, logger Logger) time.Duration {\n\tmaxMillis := d / time.Millisecond\n\tif maxMillis > 0 && maxMillis <= math.MaxUint32 {\n\t\treturn d\n\t}\n\tif d == 0 {\n\t\treturn _defaultRelayMaxTimeout\n\t}\n\tlogger.WithFields(\n\t\tLogField{\"configuredMaxTimeout\", d},\n\t\tLogField{\"defaultMaxTimeout\", _defaultRelayMaxTimeout},\n\t).Warn(\"Configured RelayMaxTimeout is invalid, using default instead.\")\n\treturn _defaultRelayMaxTimeout\n}\n\ntype sentBytesReporter interface {\n\tSentBytes(size uint16)\n}\n\ntype relayFragmentSender struct {\n\tcallReq            *lazyCallReq\n\tframePool          FramePool\n\tframeReceiver      frameReceiver\n\tfailRelayItemFunc  func(items *relayItems, id uint32, failure string, err error)\n\toutboundRelayItems *relayItems\n\torigID             uint32\n\tsentReporter       sentBytesReporter\n}\n\nfunc (r *Relayer) newFragmentSender(dstRelay frameReceiver, cr *lazyCallReq, origID uint32, sentReporter sentBytesReporter) *relayFragmentSender {\n\t// TODO(cinchurge): pool fragment senders\n\treturn &relayFragmentSender{\n\t\tcallReq:            cr,\n\t\tframePool:          r.conn.opts.FramePool,\n\t\tframeReceiver:      dstRelay,\n\t\tfailRelayItemFunc:  r.failRelayItem,\n\t\toutboundRelayItems: r.outbound,\n\t\torigID:             origID,\n\t\tsentReporter:       sentReporter,\n\t}\n}\n\nfunc (rfs *relayFragmentSender) newFragment(initial bool, checksum Checksum) (*writableFragment, error) {\n\tframe := rfs.framePool.Get()\n\tframe.Header.ID = rfs.callReq.Header.ID\n\tif initial {\n\t\tframe.Header.messageType = messageTypeCallReq\n\t} else {\n\t\tframe.Header.messageType = messageTypeCallReqContinue\n\t}\n\n\tcontents := typed.NewWriteBuffer(frame.Payload[:])\n\n\t// flags:1\n\t// Flags MUST be copied over from the callReq frame to all new fragments since if there are more\n\t// fragments to follow the callReq, the destination needs to know about this or those frames will\n\t// be dropped from the call\n\tflagsRef := contents.DeferByte()\n\tflagsRef.Update(rfs.callReq.Payload[_flagsIndex])\n\n\tif initial {\n\t\t// Copy all data before the checksum for the initial frame\n\t\tcontents.WriteBytes(rfs.callReq.Payload[_flagsIndex+1 : rfs.callReq.checksumTypeOffset])\n\t}\n\n\t// checksumType:1\n\tcontents.WriteSingleByte(byte(checksum.TypeCode()))\n\n\t// checksum: checksum.Size()\n\tchecksumRef := contents.DeferBytes(checksum.Size())\n\n\tif initial {\n\t\t// arg1~1: write arg1 to the initial frame\n\t\tcontents.WriteUint16(uint16(len(rfs.callReq.method)))\n\t\tcontents.WriteBytes(rfs.callReq.method)\n\t\tchecksum.Add(rfs.callReq.method)\n\t}\n\n\t// TODO(cinchurge): pool writableFragment\n\treturn &writableFragment{\n\t\tflagsRef:    flagsRef,\n\t\tchecksumRef: checksumRef,\n\t\t// checksum will be released by the relayer when the call is finished\n\t\tchecksum: &noReleaseChecksum{Checksum: checksum},\n\t\tcontents: contents,\n\t\tframe:    frame,\n\t}, contents.Err()\n}\n\nfunc (rfs *relayFragmentSender) flushFragment(wf *writableFragment) error {\n\twf.frame.Header.SetPayloadSize(uint16(wf.contents.BytesWritten()))\n\trfs.sentReporter.SentBytes(wf.frame.Header.FrameSize())\n\n\tsent, failure := rfs.frameReceiver.Receive(wf.frame, requestFrame)\n\tif !sent {\n\t\trfs.failRelayItemFunc(rfs.outboundRelayItems, rfs.origID, failure, errFrameNotSent)\n\t\trfs.framePool.Release(wf.frame)\n\t\treturn nil\n\t}\n\treturn nil\n}\n\nfunc (rfs *relayFragmentSender) doneSending() {}\n"
  },
  {
    "path": "relay_api.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport \"github.com/uber/tchannel-go/relay\"\n\n// RelayHost is the interface used to create RelayCalls when the relay\n// receives an incoming call.\ntype RelayHost interface {\n\t// SetChannels is called on creation of the channel. It's used to set a\n\t// channel reference which can be used to get references to *Peer.\n\tSetChannel(ch *Channel)\n\n\t// Start starts a new RelayCall given the call frame and connection.\n\t// It may return a call and an error, in which case the caller will\n\t// call Failed/End on the RelayCall.\n\tStart(relay.CallFrame, *relay.Conn) (RelayCall, error)\n}\n\n// RelayCall abstracts away peer selection, stats, and any other business\n// logic from the underlying relay implementation. A RelayCall may not\n// have a destination if there was an error during peer selection\n// (which should be returned from start).\ntype RelayCall interface {\n\t// Destination returns the selected peer (if there was no error from Start).\n\tDestination() (peer *Peer, ok bool)\n\n\t// SentBytes is called when a frame is sent to the destination peer.\n\tSentBytes(uint16)\n\n\t// ReceivedBytes is called when a frame is received from the destination peer.\n\tReceivedBytes(uint16)\n\n\t// CallResponse is called when a call response frame is received from the destination peer\n\tCallResponse(relay.RespFrame)\n\n\t// The call succeeded (possibly after retrying).\n\tSucceeded()\n\n\t// The call failed.\n\tFailed(reason string)\n\n\t// End stats collection for this RPC. Will be called exactly once.\n\tEnd()\n}\n"
  },
  {
    "path": "relay_benchmark_test.go",
    "content": "package tchannel_test\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/bmizerany/perks/quantile\"\n\t\"github.com/stretchr/testify/require\"\n\t. \"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/benchmark\"\n\t\"github.com/uber/tchannel-go/relay\"\n\t\"github.com/uber/tchannel-go/testutils\"\n)\n\ntype benchmarkParams struct {\n\tservers, clients int\n\trequestSize      int\n\tappends          []relay.KeyVal\n}\n\ntype workerControl struct {\n\tstart        sync.WaitGroup\n\tunblockStart chan struct{}\n\tdone         sync.WaitGroup\n}\n\nfunc init() {\n\tbenchmark.BenchmarkDir = \"./benchmark/\"\n}\n\nfunc newWorkerControl(numWorkers int) *workerControl {\n\twc := &workerControl{\n\t\tunblockStart: make(chan struct{}),\n\t}\n\twc.start.Add(numWorkers)\n\twc.done.Add(numWorkers)\n\treturn wc\n}\n\nfunc (c *workerControl) WaitForStart(f func()) {\n\tc.start.Wait()\n\tf()\n\tclose(c.unblockStart)\n}\n\nfunc (c *workerControl) WaitForEnd() {\n\tc.done.Wait()\n}\n\nfunc (c *workerControl) WorkerStart() {\n\tc.start.Done()\n\t<-c.unblockStart\n}\n\nfunc (c *workerControl) WorkerDone() {\n\tc.done.Done()\n}\n\nfunc defaultParams() benchmarkParams {\n\treturn benchmarkParams{\n\t\tservers:     2,\n\t\tclients:     2,\n\t\trequestSize: 1024,\n\t}\n}\n\nfunc closeAndVerify(b *testing.B, ch *Channel) {\n\tch.Close()\n\tisChanClosed := func() bool {\n\t\treturn ch.State() == ChannelClosed\n\t}\n\tif !testutils.WaitFor(time.Second, isChanClosed) {\n\t\tb.Errorf(\"Timed out waiting for channel to close, state: %v\", ch.State())\n\t}\n}\n\nfunc benchmarkRelay(b *testing.B, p benchmarkParams) {\n\tb.SetBytes(int64(p.requestSize))\n\tb.ReportAllocs()\n\n\tservices := make(map[string][]string)\n\n\tservers := make([]benchmark.Server, p.servers)\n\tfor i := range servers {\n\t\tservers[i] = benchmark.NewServer(\n\t\t\tbenchmark.WithServiceName(\"svc\"),\n\t\t\tbenchmark.WithRequestSize(p.requestSize),\n\t\t\tbenchmark.WithExternalProcess(),\n\t\t)\n\t\tdefer servers[i].Close()\n\t\tservices[\"svc\"] = append(services[\"svc]\"], servers[i].HostPort())\n\t}\n\n\trelay, err := benchmark.NewRealRelay(services, p.appends)\n\trequire.NoError(b, err, \"Failed to create relay\")\n\tdefer relay.Close()\n\n\tclients := make([]benchmark.Client, p.clients)\n\tfor i := range clients {\n\t\tclients[i] = benchmark.NewClient([]string{relay.HostPort()},\n\t\t\tbenchmark.WithServiceName(\"svc\"),\n\t\t\tbenchmark.WithRequestSize(p.requestSize),\n\t\t\tbenchmark.WithExternalProcess(),\n\t\t\tbenchmark.WithTimeout(10*time.Second),\n\t\t)\n\t\tdefer clients[i].Close()\n\t\trequire.NoError(b, clients[i].Warmup(), \"Warmup failed\")\n\t}\n\n\tquantileVals := []float64{0.50, 0.95, 0.99, 1.0}\n\tquantiles := make([]*quantile.Stream, p.clients)\n\tfor i := range quantiles {\n\t\tquantiles[i] = quantile.NewTargeted(quantileVals...)\n\t}\n\n\twc := newWorkerControl(p.clients)\n\tdec := testutils.Decrementor(b.N)\n\n\tvar wg sync.WaitGroup\n\terrC := make(chan error, 1)\n\tdefer close(errC)\n\n\tfor i, c := range clients {\n\t\twg.Add(1)\n\t\tgo func(i int, c benchmark.Client) {\n\t\t\tdefer wg.Done()\n\n\t\t\t// Do a warm up call.\n\t\t\tc.RawCall(1)\n\n\t\t\twc.WorkerStart()\n\t\t\tdefer wc.WorkerDone()\n\n\t\t\tfor {\n\t\t\t\ttokens := dec.Multiple(200)\n\t\t\t\tif tokens == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tdurations, err := c.RawCall(tokens)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrC <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tfor _, d := range durations {\n\t\t\t\t\tquantiles[i].Insert(float64(d))\n\t\t\t\t}\n\t\t\t}\n\t\t}(i, c)\n\t}\n\n\twg.Wait()\n\tif err := <-errC; err != nil {\n\t\tb.Fatalf(\"Call failed: %v\", err)\n\t}\n\n\tvar started time.Time\n\twc.WaitForStart(func() {\n\t\tb.ResetTimer()\n\t\tstarted = time.Now()\n\t})\n\twc.WaitForEnd()\n\tduration := time.Since(started)\n\n\tfmt.Printf(\"\\nb.N: %v Duration: %v RPS = %0.0f\\n\", b.N, duration, float64(b.N)/duration.Seconds())\n\n\t// Merge all the quantiles into 1\n\tfor _, q := range quantiles[1:] {\n\t\tquantiles[0].Merge(q.Samples())\n\t}\n\n\tfor _, q := range quantileVals {\n\t\tfmt.Printf(\"  %0.4f = %v\\n\", q, time.Duration(quantiles[0].Query(q)))\n\t}\n\tfmt.Println()\n}\n\nfunc BenchmarkRelayNoLatencies(b *testing.B) {\n\tserver := benchmark.NewServer(\n\t\tbenchmark.WithServiceName(\"svc\"),\n\t\tbenchmark.WithExternalProcess(),\n\t\tbenchmark.WithNoLibrary(),\n\t)\n\tdefer server.Close()\n\n\thostMapping := map[string][]string{\"svc\": {server.HostPort()}}\n\trelay, err := benchmark.NewRealRelay(hostMapping, nil)\n\trequire.NoError(b, err, \"NewRealRelay failed\")\n\tdefer relay.Close()\n\n\tclient := benchmark.NewClient([]string{relay.HostPort()},\n\t\tbenchmark.WithServiceName(\"svc\"),\n\t\tbenchmark.WithExternalProcess(),\n\t\tbenchmark.WithNoLibrary(),\n\t\tbenchmark.WithNumClients(10),\n\t\tbenchmark.WithNoChecking(),\n\t\tbenchmark.WithNoDurations(),\n\t\tbenchmark.WithTimeout(10*time.Second),\n\t)\n\tdefer client.Close()\n\trequire.NoError(b, client.Warmup(), \"client.Warmup failed\")\n\n\tb.ResetTimer()\n\tstarted := time.Now()\n\tfor _, calls := range testutils.Batch(b.N, 10000) {\n\t\tif _, err := client.RawCall(calls); err != nil {\n\t\t\tb.Fatalf(\"Calls failed: %v\", err)\n\t\t}\n\t}\n\n\tduration := time.Since(started)\n\tfmt.Printf(\"\\nb.N: %v Duration: %v RPS = %0.0f\\n\", b.N, duration, float64(b.N)/duration.Seconds())\n}\n\nfunc BenchmarkRelay2Servers5Clients1k(b *testing.B) {\n\tp := defaultParams()\n\tp.clients = 5\n\tp.servers = 2\n\tbenchmarkRelay(b, p)\n}\n\nfunc BenchmarkRelay4Servers20Clients1k(b *testing.B) {\n\tp := defaultParams()\n\tp.clients = 20\n\tp.servers = 4\n\tbenchmarkRelay(b, p)\n}\n\nfunc BenchmarkRelay2Servers5Clients4k(b *testing.B) {\n\tp := defaultParams()\n\tp.requestSize = 4 * 1024\n\tp.clients = 5\n\tp.servers = 2\n\tbenchmarkRelay(b, p)\n}\n\nfunc BenchmarkRelayAppends(b *testing.B) {\n\tfor _, n := range []int{0, 1, 2, 5, 10} {\n\t\tb.Run(fmt.Sprintf(\"%v appends\", n), func(b *testing.B) {\n\t\t\tp := defaultParams()\n\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\tp.appends = append(p.appends, relay.KeyVal{Key: []byte(\"foo\"), Val: []byte(\"bar\")})\n\t\t\t}\n\t\t\tb.ResetTimer()\n\t\t\tbenchmarkRelay(b, p)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "relay_fragment_sender_test.go",
    "content": "package tchannel\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/uber/tchannel-go/relay\"\n\t\"github.com/uber/tchannel-go/testutils/thriftarg2test\"\n)\n\nvar _ frameReceiver = (*dummyFrameReceiver)(nil)\n\ntype dummyFrameReceiver struct {\n\tretSent          bool\n\tretFailureReason string\n\tpool             FramePool\n\n\t// mutable\n\tgotPayload []byte\n}\n\nfunc newDummyFrameReceiver(retSent bool, retFailureReason string, pool FramePool) *dummyFrameReceiver {\n\treturn &dummyFrameReceiver{\n\t\tretSent:          retSent,\n\t\tretFailureReason: retFailureReason,\n\t\tpool:             pool,\n\t}\n}\n\nfunc (d *dummyFrameReceiver) Receive(f *Frame, fType frameType) (sent bool, failureReason string) {\n\t// Keep a record of the received payload for verification\n\td.gotPayload = make([]byte, len(f.SizedPayload()))\n\tcopy(d.gotPayload, f.SizedPayload())\n\n\t// Frames should be released after transmission\n\tif d.retSent {\n\t\td.pool.Release(f)\n\t}\n\n\treturn d.retSent, d.retFailureReason\n}\n\ntype noopSentReporter struct{}\n\nfunc (r *noopSentReporter) SentBytes(_ uint16) {}\n\nfunc TestRelayFragmentSender(t *testing.T) {\n\ttests := []struct {\n\t\tmsg                            string\n\t\tsent                           bool\n\t\tfailure                        string\n\t\twantFailureRelayItemFuncCalled bool\n\t\twantPayload                    []byte\n\t}{\n\t\t{\n\t\t\tmsg:         \"successful send\",\n\t\t\tsent:        true,\n\t\t\twantPayload: []byte(\"hello, world\"),\n\t\t},\n\t\t{\n\t\t\tmsg:                            \"send failure\",\n\t\t\tsent:                           false,\n\t\t\tfailure:                        \"something bad happened\",\n\t\t\twantFailureRelayItemFuncCalled: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\tvar failRelayItemFuncCalled bool\n\n\t\t\tpool := NewCheckedFramePoolForTest()\n\t\t\tdefer func() {\n\t\t\t\tCheckFramePoolIsEmpty(t, pool)\n\t\t\t}()\n\n\t\t\tcr := reqHasAll.req(t)\n\t\t\treceiver := newDummyFrameReceiver(tt.sent, tt.failure, pool)\n\t\t\trfs := relayFragmentSender{\n\t\t\t\tcallReq:       &cr,\n\t\t\t\tframePool:     pool,\n\t\t\t\tframeReceiver: receiver,\n\t\t\t\tfailRelayItemFunc: func(items *relayItems, id uint32, failure string, err error) {\n\t\t\t\t\tfailRelayItemFuncCalled = true\n\t\t\t\t\tassert.Equal(t, uint32(123), id, \"got unexpected id\")\n\t\t\t\t\tassert.Equal(t, tt.failure, failure, \"got unexpected failure string\")\n\t\t\t\t\tassert.Error(t, err, \"missing err\")\n\t\t\t\t},\n\t\t\t\torigID:       123,\n\t\t\t\tsentReporter: &noopSentReporter{},\n\t\t\t}\n\n\t\t\twf, err := rfs.newFragment(true, nullChecksum{})\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// Get the payload expected by receive before the fragment is released\n\t\t\twantPayload := make([]byte, wf.contents.BytesWritten())\n\t\t\tcopy(wantPayload, wf.frame.Payload[:wf.contents.BytesWritten()])\n\n\t\t\terr = rfs.flushFragment(wf)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, wantPayload, receiver.gotPayload)\n\t\t\tassert.Equal(t, tt.wantFailureRelayItemFuncCalled, failRelayItemFuncCalled, \"unexpected failRelayItemFunc called state\")\n\t\t})\n\t}\n}\n\ntype dummyArgWriter struct {\n\tnumCall      int\n\twriteError   []string\n\tcloseError   string\n\tbytesWritten []byte\n}\n\nfunc (w *dummyArgWriter) Write(b []byte) (int, error) {\n\tretErr := w.writeError[w.numCall]\n\tw.bytesWritten = append(w.bytesWritten, b...)\n\tw.numCall++\n\tif retErr != \"\" {\n\t\treturn 0, errors.New(retErr)\n\t}\n\treturn len(b), nil\n}\n\nfunc (w *dummyArgWriter) Close() error {\n\tif w.closeError != \"\" {\n\t\treturn errors.New(w.closeError)\n\t}\n\treturn nil\n}\n\nfunc TestWriteArg2WithAppends(t *testing.T) {\n\ttests := []struct {\n\t\tmsg             string\n\t\twriter          *dummyArgWriter\n\t\targ2Map         map[string]string\n\t\toverrideArg2Buf []byte\n\t\tappends         []relay.KeyVal\n\t\twantError       string\n\t}{\n\t\t{\n\t\t\tmsg: \"write success without appends\",\n\t\t\twriter: &dummyArgWriter{\n\t\t\t\twriteError: []string{\n\t\t\t\t\t\"\", // nh\n\t\t\t\t\t\"\", // arg2\n\t\t\t\t},\n\t\t\t},\n\t\t\targ2Map: exampleArg2Map,\n\t\t},\n\t\t{\n\t\t\tmsg: \"write success with appends\",\n\t\t\twriter: &dummyArgWriter{\n\t\t\t\twriteError: []string{\n\t\t\t\t\t\"\", // nh\n\t\t\t\t\t\"\", // arg2\n\t\t\t\t\t\"\", // key length\n\t\t\t\t\t\"\", // key\n\t\t\t\t\t\"\", // val length\n\t\t\t\t\t\"\", // val\n\t\t\t\t},\n\t\t\t},\n\t\t\targ2Map: exampleArg2Map,\n\t\t\tappends: []relay.KeyVal{\n\t\t\t\t{Key: []byte(\"foo\"), Val: []byte(\"bar\")},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"no nh in data\",\n\t\t\twriter: &dummyArgWriter{\n\t\t\t\twriteError: []string{\n\t\t\t\t\tassert.AnError.Error(), // nh\n\t\t\t\t},\n\t\t\t},\n\t\t\toverrideArg2Buf: []byte{0},\n\t\t\twantError:       \"no nh in arg2\",\n\t\t},\n\t\t{\n\t\t\tmsg: \"write nh fails\",\n\t\t\twriter: &dummyArgWriter{\n\t\t\t\twriteError: []string{\n\t\t\t\t\tassert.AnError.Error(), // nh\n\t\t\t\t},\n\t\t\t},\n\t\t\targ2Map:   exampleArg2Map,\n\t\t\twantError: assert.AnError.Error(),\n\t\t},\n\t\t{\n\t\t\tmsg: \"write arg2 fails\",\n\t\t\twriter: &dummyArgWriter{\n\t\t\t\twriteError: []string{\n\t\t\t\t\t\"\",                     // write nh\n\t\t\t\t\tassert.AnError.Error(), // write arg2\n\t\t\t\t},\n\t\t\t},\n\t\t\targ2Map:   exampleArg2Map,\n\t\t\twantError: assert.AnError.Error(),\n\t\t},\n\t\t{\n\t\t\tmsg: \"write append key length fails\",\n\t\t\twriter: &dummyArgWriter{\n\t\t\t\twriteError: []string{\n\t\t\t\t\t\"\",                     // write nh\n\t\t\t\t\t\"\",                     // write arg2\n\t\t\t\t\tassert.AnError.Error(), // write key length\n\t\t\t\t},\n\t\t\t},\n\t\t\targ2Map: exampleArg2Map,\n\t\t\tappends: []relay.KeyVal{\n\t\t\t\t{Key: []byte(\"foo\"), Val: []byte(\"bar\")},\n\t\t\t},\n\t\t\twantError: assert.AnError.Error(),\n\t\t},\n\t\t{\n\t\t\tmsg: \"write append key fails\",\n\t\t\twriter: &dummyArgWriter{\n\t\t\t\twriteError: []string{\n\t\t\t\t\t\"\",                     // write nh\n\t\t\t\t\t\"\",                     // write arg2\n\t\t\t\t\t\"\",                     // write key length\n\t\t\t\t\tassert.AnError.Error(), // write key\n\t\t\t\t},\n\t\t\t},\n\t\t\targ2Map: exampleArg2Map,\n\t\t\tappends: []relay.KeyVal{\n\t\t\t\t{Key: []byte(\"foo\"), Val: []byte(\"bar\")},\n\t\t\t},\n\t\t\twantError: assert.AnError.Error(),\n\t\t},\n\t\t{\n\t\t\tmsg: \"write append val length fails\",\n\t\t\twriter: &dummyArgWriter{\n\t\t\t\twriteError: []string{\n\t\t\t\t\t\"\",                     // write nh\n\t\t\t\t\t\"\",                     // write arg2\n\t\t\t\t\t\"\",                     // write key length\n\t\t\t\t\t\"\",                     // write key\n\t\t\t\t\tassert.AnError.Error(), // write val length\n\t\t\t\t},\n\t\t\t},\n\t\t\targ2Map: exampleArg2Map,\n\t\t\tappends: []relay.KeyVal{\n\t\t\t\t{Key: []byte(\"foo\"), Val: []byte(\"bar\")},\n\t\t\t},\n\t\t\twantError: assert.AnError.Error(),\n\t\t},\n\t\t{\n\t\t\tmsg: \"write append val fails\",\n\t\t\twriter: &dummyArgWriter{\n\t\t\t\twriteError: []string{\n\t\t\t\t\t\"\",                     // write nh\n\t\t\t\t\t\"\",                     // write arg2\n\t\t\t\t\t\"\",                     // write key length\n\t\t\t\t\t\"\",                     // write key\n\t\t\t\t\t\"\",                     // write val length\n\t\t\t\t\tassert.AnError.Error(), // write val\n\t\t\t\t},\n\t\t\t},\n\t\t\targ2Map: exampleArg2Map,\n\t\t\tappends: []relay.KeyVal{\n\t\t\t\t{Key: []byte(\"foo\"), Val: []byte(\"bar\")},\n\t\t\t},\n\t\t\twantError: assert.AnError.Error(),\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\tvar arg2buf []byte\n\t\t\tif tt.overrideArg2Buf != nil {\n\t\t\t\targ2buf = tt.overrideArg2Buf\n\t\t\t} else if len(tt.arg2Map) > 0 {\n\t\t\t\targ2buf = thriftarg2test.BuildKVBuffer(tt.arg2Map)\n\t\t\t}\n\t\t\terr := writeArg2WithAppends(tt.writer, arg2buf, tt.appends)\n\t\t\tif tt.wantError != \"\" {\n\t\t\t\trequire.EqualError(t, err, tt.wantError)\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, tt.writer.Close())\n\n\t\t\tfinalMap := make(map[string]string)\n\t\t\tfor k, v := range tt.arg2Map {\n\t\t\t\tfinalMap[k] = v\n\t\t\t}\n\t\t\tfor _, kv := range tt.appends {\n\t\t\t\tfinalMap[string(kv.Key)] = string(kv.Val)\n\t\t\t}\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, finalMap, thriftarg2test.MustReadKVBuffer(t, tt.writer.bytesWritten))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "relay_internal_test.go",
    "content": "package tchannel\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestFinishesCallResponses(t *testing.T) {\n\ttests := []struct {\n\t\tmsgType      messageType\n\t\tflags        byte\n\t\tfinishesCall bool\n\t}{\n\t\t{messageTypeCallRes, 0x00, true},\n\t\t{messageTypeCallRes, 0x01, false},\n\t\t{messageTypeCallRes, 0x02, true},\n\t\t{messageTypeCallRes, 0x03, false},\n\t\t{messageTypeCallRes, 0x04, true},\n\t\t{messageTypeCallResContinue, 0x00, true},\n\t\t{messageTypeCallResContinue, 0x01, false},\n\t\t{messageTypeCallResContinue, 0x02, true},\n\t\t{messageTypeCallResContinue, 0x03, false},\n\t\t{messageTypeCallResContinue, 0x04, true},\n\t\t// By definition, callreq should never terminate an RPC.\n\t\t{messageTypeCallReq, 0x00, false},\n\t\t{messageTypeCallReq, 0x01, false},\n\t\t{messageTypeCallReq, 0x02, false},\n\t\t{messageTypeCallReq, 0x03, false},\n\t\t{messageTypeCallReq, 0x04, false},\n\t}\n\tfor _, tt := range tests {\n\t\tf := NewFrame(100)\n\t\tfh := FrameHeader{\n\t\t\tsize:        uint16(0xFF34),\n\t\t\tmessageType: tt.msgType,\n\t\t\tID:          0xDEADBEEF,\n\t\t}\n\t\tf.Header = fh\n\t\tfh.write(typed.NewWriteBuffer(f.headerBuffer))\n\n\t\tpayload := typed.NewWriteBuffer(f.Payload)\n\t\tpayload.WriteSingleByte(tt.flags)\n\t\tassert.Equal(t, tt.finishesCall, finishesCall(f), \"Wrong isLast for flags %v and message type %v\", tt.flags, tt.msgType)\n\t}\n}\n\nfunc TestRelayTimerPoolMisuse(t *testing.T) {\n\ttests := []struct {\n\t\tmsg string\n\t\tf   func(*relayTimer)\n\t}{\n\t\t{\n\t\t\tmsg: \"release without stop\",\n\t\t\tf: func(rt *relayTimer) {\n\t\t\t\trt.Start(time.Hour, &relayItems{}, 0, false /* isOriginator */)\n\t\t\t\trt.Release()\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"start twice\",\n\t\t\tf: func(rt *relayTimer) {\n\t\t\t\trt.Start(time.Hour, &relayItems{}, 0, false /* isOriginator */)\n\t\t\t\trt.Start(time.Hour, &relayItems{}, 0, false /* isOriginator */)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"underlying timer is already active\",\n\t\t\tf: func(rt *relayTimer) {\n\t\t\t\trt.timer.Reset(time.Hour)\n\t\t\t\trt.Start(time.Hour, &relayItems{}, 0, false /* isOriginator */)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"use timer after releasing it\",\n\t\t\tf: func(rt *relayTimer) {\n\t\t\t\trt.Release()\n\t\t\t\trt.Stop()\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttrigger := func(*relayItems, uint32, bool) {}\n\t\trtp := newRelayTimerPool(trigger, true /* verify */)\n\n\t\trt := rtp.Get()\n\t\tassert.Panics(t, func() {\n\t\t\ttt.f(rt)\n\t\t}, tt.msg)\n\t}\n}\n"
  },
  {
    "path": "relay_messages.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/relay\"\n\t\"github.com/uber/tchannel-go/thrift/arg2\"\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\nvar _ relay.RespFrame = (*lazyCallRes)(nil)\n\nvar (\n\t_callerNameKeyBytes      = []byte(CallerName)\n\t_routingDelegateKeyBytes = []byte(RoutingDelegate)\n\t_routingKeyKeyBytes      = []byte(RoutingKey)\n\t_argSchemeKeyBytes       = []byte(ArgScheme)\n\t_tchanThriftValueBytes   = []byte(Thrift)\n)\n\nconst (\n\t// Common to many frame types.\n\t_flagsIndex = 0\n\n\t// For call req, indexes into the frame.\n\t// Use int for indexes to avoid overflow caused by accidental byte arithmentic.\n\t_ttlIndex         int = 1\n\t_ttlLen           int = 4\n\t_spanIndex        int = _ttlIndex + _ttlLen\n\t_spanLength       int = 25\n\t_serviceLenIndex  int = _spanIndex + _spanLength\n\t_serviceNameIndex int = _serviceLenIndex + 1\n\n\t// For call res and call res continue.\n\t_resCodeOK        = 0x00\n\t_resCodeIndex int = 1\n\n\t// For error.\n\t_errCodeIndex int = 0\n)\n\ntype lazyError struct {\n\t*Frame\n}\n\nfunc newLazyError(f *Frame) lazyError {\n\tif msgType := f.Header.messageType; msgType != messageTypeError {\n\t\tpanic(fmt.Errorf(\"newLazyError called for wrong messageType: %v\", msgType))\n\t}\n\treturn lazyError{f}\n}\n\nfunc (e lazyError) Code() SystemErrCode {\n\treturn SystemErrCode(e.Payload[_errCodeIndex])\n}\n\ntype lazyCallRes struct {\n\t*Frame\n\n\tas               []byte\n\targ2IsFragmented bool\n\targ2Payload      []byte\n}\n\nfunc newLazyCallRes(f *Frame) (lazyCallRes, error) {\n\tif msgType := f.Header.messageType; msgType != messageTypeCallRes {\n\t\tpanic(fmt.Errorf(\"newLazyCallRes called for wrong messageType: %v\", msgType))\n\t}\n\n\trbuf := typed.NewReadBuffer(f.SizedPayload())\n\trbuf.SkipBytes(1)           // flags\n\trbuf.SkipBytes(1)           // code\n\trbuf.SkipBytes(_spanLength) // tracing\n\n\tvar as []byte\n\tnh := int(rbuf.ReadSingleByte())\n\tfor i := 0; i < nh; i++ {\n\t\tkeyLen := int(rbuf.ReadSingleByte())\n\t\tkey := rbuf.ReadBytes(keyLen)\n\t\tvalLen := int(rbuf.ReadSingleByte())\n\t\tval := rbuf.ReadBytes(valLen)\n\n\t\tif bytes.Equal(key, _argSchemeKeyBytes) {\n\t\t\tas = val\n\t\t\tcontinue\n\t\t}\n\t}\n\n\tcsumtype := ChecksumType(rbuf.ReadSingleByte()) // csumtype\n\trbuf.SkipBytes(csumtype.ChecksumSize())         // csum\n\n\t// arg1: ignored\n\tnarg1 := int(rbuf.ReadUint16())\n\trbuf.SkipBytes(narg1)\n\n\t// arg2: keep track of payload\n\tnarg2 := int(rbuf.ReadUint16())\n\targ2Payload := rbuf.ReadBytes(narg2)\n\targ2IsFragmented := rbuf.BytesRemaining() == 0 && hasMoreFragments(f)\n\n\t// arg3: ignored\n\n\t// Make sure we didn't hit any issues reading the buffer\n\tif err := rbuf.Err(); err != nil {\n\t\treturn lazyCallRes{}, fmt.Errorf(\"read response frame: %v\", err)\n\t}\n\n\treturn lazyCallRes{\n\t\tFrame:            f,\n\t\tas:               as,\n\t\targ2IsFragmented: arg2IsFragmented,\n\t\targ2Payload:      arg2Payload,\n\t}, nil\n}\n\n// OK implements relay.RespFrame\nfunc (cr lazyCallRes) OK() bool {\n\treturn isCallResOK(cr.Frame)\n}\n\n// ArgScheme implements relay.RespFrame\nfunc (cr lazyCallRes) ArgScheme() []byte {\n\treturn cr.as\n}\n\n// Arg2IsFragmented implements relay.RespFrame\nfunc (cr lazyCallRes) Arg2IsFragmented() bool {\n\treturn cr.arg2IsFragmented\n}\n\n// Arg2 implements relay.RespFrame\nfunc (cr lazyCallRes) Arg2() []byte {\n\treturn cr.arg2Payload\n}\n\ntype lazyCallReq struct {\n\t*Frame\n\n\tchecksumTypeOffset             uint16\n\targ2StartOffset, arg2EndOffset uint16\n\targ3StartOffset                uint16\n\n\tcaller, method, delegate, key, as []byte\n\targ2Appends                       []relay.KeyVal\n\tchecksumType                      ChecksumType\n\tisArg2Fragmented                  bool\n\n\t// Intentionally an array to combine allocations with that of lazyCallReq\n\targ2InitialBuf [1]relay.KeyVal\n}\n\n// TODO: Consider pooling lazyCallReq and using pointers to the struct.\n\nfunc newLazyCallReq(f *Frame) (*lazyCallReq, error) {\n\tif msgType := f.Header.messageType; msgType != messageTypeCallReq {\n\t\tpanic(fmt.Errorf(\"newLazyCallReq called for wrong messageType: %v\", msgType))\n\t}\n\n\tcr := &lazyCallReq{Frame: f}\n\tcr.arg2Appends = cr.arg2InitialBuf[:0]\n\n\trbuf := typed.NewReadBuffer(f.SizedPayload())\n\trbuf.SkipBytes(_serviceLenIndex)\n\n\t// service~1\n\tserviceLen := rbuf.ReadSingleByte()\n\trbuf.SkipBytes(int(serviceLen))\n\n\t// nh:1 (hk~1 hv~1){nh}\n\tnumHeaders := int(rbuf.ReadSingleByte())\n\tfor i := 0; i < numHeaders; i++ {\n\t\tkeyLen := int(rbuf.ReadSingleByte())\n\t\tkey := rbuf.ReadBytes(keyLen)\n\n\t\tvalLen := int(rbuf.ReadSingleByte())\n\t\tval := rbuf.ReadBytes(valLen)\n\n\t\tif bytes.Equal(key, _argSchemeKeyBytes) {\n\t\t\tcr.as = val\n\t\t} else if bytes.Equal(key, _callerNameKeyBytes) {\n\t\t\tcr.caller = val\n\t\t} else if bytes.Equal(key, _routingDelegateKeyBytes) {\n\t\t\tcr.delegate = val\n\t\t} else if bytes.Equal(key, _routingKeyKeyBytes) {\n\t\t\tcr.key = val\n\t\t}\n\t}\n\n\t// csumtype:1 (csum:4){0,1} arg1~2 arg2~2 arg3~2\n\tcr.checksumTypeOffset = uint16(rbuf.BytesRead())\n\tcr.checksumType = ChecksumType(rbuf.ReadSingleByte())\n\trbuf.SkipBytes(cr.checksumType.ChecksumSize())\n\n\t// arg1~2\n\targ1Len := int(rbuf.ReadUint16())\n\tcr.method = rbuf.ReadBytes(arg1Len)\n\n\t// arg2~2\n\targ2Len := rbuf.ReadUint16()\n\tcr.arg2StartOffset = uint16(rbuf.BytesRead())\n\tcr.arg2EndOffset = cr.arg2StartOffset + arg2Len\n\n\t// arg2 is fragmented if we don't see arg3 in this frame.\n\trbuf.SkipBytes(int(arg2Len))\n\tcr.isArg2Fragmented = rbuf.BytesRemaining() == 0 && cr.HasMoreFragments()\n\n\tif !cr.isArg2Fragmented {\n\t\t// arg3~2\n\t\trbuf.SkipBytes(2)\n\t\tcr.arg3StartOffset = uint16(rbuf.BytesRead())\n\t}\n\n\tif rbuf.Err() != nil {\n\t\treturn nil, rbuf.Err()\n\t}\n\n\treturn cr, nil\n}\n\n// Caller returns the name of the originator of this callReq.\nfunc (f *lazyCallReq) Caller() []byte {\n\treturn f.caller\n}\n\n// Service returns the name of the destination service for this callReq.\nfunc (f *lazyCallReq) Service() []byte {\n\tl := f.Payload[_serviceLenIndex]\n\treturn f.Payload[_serviceNameIndex : _serviceNameIndex+int(l)]\n}\n\n// Method returns the name of the method being called.\nfunc (f *lazyCallReq) Method() []byte {\n\treturn f.method\n}\n\n// RoutingDelegate returns the routing delegate for this call req, if any.\nfunc (f *lazyCallReq) RoutingDelegate() []byte {\n\treturn f.delegate\n}\n\n// RoutingKey returns the routing delegate for this call req, if any.\nfunc (f *lazyCallReq) RoutingKey() []byte {\n\treturn f.key\n}\n\n// TTL returns the time to live for this callReq.\nfunc (f *lazyCallReq) TTL() time.Duration {\n\tttl := binary.BigEndian.Uint32(f.Payload[_ttlIndex : _ttlIndex+_ttlLen])\n\treturn time.Duration(ttl) * time.Millisecond\n}\n\n// SetTTL overwrites the frame's TTL.\nfunc (f *lazyCallReq) SetTTL(d time.Duration) {\n\tttl := uint32(d / time.Millisecond)\n\tbinary.BigEndian.PutUint32(f.Payload[_ttlIndex:_ttlIndex+_ttlLen], ttl)\n}\n\n// Span returns the Span\nfunc (f *lazyCallReq) Span() Span {\n\treturn callReqSpan(f.Frame)\n}\n\n// HasMoreFragments returns whether the callReq has more fragments.\nfunc (f *lazyCallReq) HasMoreFragments() bool {\n\treturn f.Payload[_flagsIndex]&hasMoreFragmentsFlag != 0\n}\n\n// Arg2EndOffset returns the offset from start of payload to the end of Arg2\n// in bytes, and hasMore to be true if there are more frames and arg3 has\n// not started.\nfunc (f *lazyCallReq) Arg2EndOffset() (_ int, hasMore bool) {\n\treturn int(f.arg2EndOffset), f.isArg2Fragmented\n}\n\n// Arg2StartOffset returns the offset from start of payload to the beginning\n// of Arg2 in bytes.\nfunc (f *lazyCallReq) Arg2StartOffset() int {\n\treturn int(f.arg2StartOffset)\n}\n\nfunc (f *lazyCallReq) arg2() []byte {\n\treturn f.Payload[f.arg2StartOffset:f.arg2EndOffset]\n}\n\nfunc (f *lazyCallReq) arg3() []byte {\n\treturn f.SizedPayload()[f.arg3StartOffset:]\n}\n\n// Arg2Iterator returns the iterator for reading Arg2 key value pair\n// of TChannel-Thrift Arg Scheme.\nfunc (f *lazyCallReq) Arg2Iterator() (arg2.KeyValIterator, error) {\n\tif !bytes.Equal(f.as, _tchanThriftValueBytes) {\n\t\treturn arg2.KeyValIterator{}, fmt.Errorf(\"%v: got %s\", errArg2ThriftOnly, f.as)\n\t}\n\treturn arg2.NewKeyValIterator(f.Payload[f.arg2StartOffset:f.arg2EndOffset])\n}\n\nfunc (f *lazyCallReq) Arg2Append(key, val []byte) {\n\tf.arg2Appends = append(f.arg2Appends, relay.KeyVal{Key: key, Val: val})\n}\n\n// finishesCall checks whether this frame is the last one we should expect for\n// this RPC req-res.\nfunc finishesCall(f *Frame) bool {\n\tswitch f.messageType() {\n\tcase messageTypeError, messageTypeCancel:\n\t\treturn true\n\tcase messageTypeCallRes, messageTypeCallResContinue:\n\t\tflags := f.Payload[_flagsIndex]\n\t\treturn flags&hasMoreFragmentsFlag == 0\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// isCallResOK indicates whether the call was successful\nfunc isCallResOK(f *Frame) bool {\n\treturn f.Payload[_resCodeIndex] == _resCodeOK\n}\n\n// hasMoreFragments indicates whether there are more fragments following this frame\nfunc hasMoreFragments(f *Frame) bool {\n\treturn f.Payload[_flagsIndex]&hasMoreFragmentsFlag != 0\n}\n"
  },
  {
    "path": "relay_messages_benchmark_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"testing\"\n)\n\nfunc BenchmarkCallReqFrame(b *testing.B) {\n\tcr := reqHasAll.req(b)\n\tf := cr.Frame\n\n\tvar service, caller, method []byte\n\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tcr, err := newLazyCallReq(f)\n\t\tif err != nil {\n\t\t\tb.Fatal(\"Unexpected error\")\n\t\t}\n\n\t\t// Multiple calls due to peer selection, stats, etc.\n\t\tfor i := 0; i < 3; i++ {\n\t\t\tservice = cr.Service()\n\t\t\tcaller = cr.Caller()\n\t\t\tmethod = cr.Method()\n\t\t}\n\t}\n\tb.StopTimer()\n\n\tfmt.Fprint(ioutil.Discard, service, caller, method)\n}\n"
  },
  {
    "path": "relay_messages_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/thrift/arg2\"\n\n\t\"github.com/uber/tchannel-go/testutils/thriftarg2test\"\n\t\"github.com/uber/tchannel-go/typed\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype testCallReq int\n\nconst (\n\treqHasHeaders testCallReq = (1 << iota)\n\treqHasCaller\n\treqHasDelegate\n\treqHasRoutingKey\n\treqHasChecksum\n\treqTotalCombinations\n\treqHasAll testCallReq = reqTotalCombinations - 1\n)\n\nvar (\n\texampleArg2Map = map[string]string{\n\t\t\"foo\": \"bar\",\n\t\t\"baz\": \"qux\",\n\t}\n)\n\nconst (\n\texampleService  = \"fooservice\"\n\texampleArg3Data = \"some arg3 data\"\n)\n\ntype testCallReqParams struct {\n\tflags           byte\n\thasTChanThrift  bool\n\targScheme       Format\n\targ2Buf         []byte\n\toverrideArg2Len int\n\tskipArg3        bool\n\targ3Buf         []byte\n\n\tserviceOverride string\n}\n\nfunc (cr testCallReq) req(tb testing.TB) lazyCallReq {\n\treturn cr.reqWithParams(tb, testCallReqParams{})\n}\n\nfunc (cr testCallReq) reqWithParams(tb testing.TB, p testCallReqParams) lazyCallReq {\n\tlcr, err := newLazyCallReq(cr.frameWithParams(tb, p))\n\trequire.NoError(tb, err)\n\treturn *lcr\n}\n\nfunc (cr testCallReq) frameWithParams(t testing.TB, p testCallReqParams) *Frame {\n\t// TODO: Constructing a frame is ugly because the initial flags byte is\n\t// written in reqResWriter instead of callReq. We should instead handle that\n\t// in callReq, which will allow our tests to be sane.\n\tf := NewFrame(MaxFramePayloadSize)\n\tfh := fakeHeader(messageTypeCallReq)\n\n\t// Set the size in the header and write out the header after we know the payload contents.\n\tdefer func() {\n\t\tfh.size = FrameHeaderSize + uint16(len(f.Payload))\n\t\tf.Header = fh\n\t\trequire.NoError(t, fh.write(typed.NewWriteBuffer(f.headerBuffer)), \"failed to write header\")\n\t}()\n\n\tpayload := typed.NewWriteBuffer(f.Payload)\n\tpayload.WriteSingleByte(p.flags)     // flags\n\tpayload.WriteUint32(42)              // TTL\n\tpayload.WriteBytes(make([]byte, 25)) // tracing\n\n\tsvc := p.serviceOverride\n\tif svc == \"\" {\n\t\tsvc = exampleService\n\t}\n\tpayload.WriteLen8String(svc) // service\n\n\theaders := make(map[string]string)\n\tswitch p.argScheme {\n\tcase HTTP, JSON, Raw, Thrift:\n\t\theaders[\"as\"] = p.argScheme.String()\n\t}\n\tif cr&reqHasHeaders != 0 {\n\t\taddRandomHeaders(headers)\n\t}\n\tif cr&reqHasCaller != 0 {\n\t\theaders[\"cn\"] = \"fake-caller\"\n\t}\n\tif cr&reqHasDelegate != 0 {\n\t\theaders[\"rd\"] = \"fake-delegate\"\n\t}\n\tif cr&reqHasRoutingKey != 0 {\n\t\theaders[\"rk\"] = \"fake-routingkey\"\n\t}\n\twriteHeaders(payload, headers)\n\n\tif cr&reqHasChecksum == 0 {\n\t\tpayload.WriteSingleByte(byte(ChecksumTypeNone)) // checksum type\n\t\t// no checksum contents for None\n\t} else {\n\t\tpayload.WriteSingleByte(byte(ChecksumTypeCrc32C)) // checksum type\n\t\tpayload.WriteUint32(0)                            // checksum contents\n\t}\n\tpayload.WriteLen16String(\"moneys\") // method\n\n\targ2Len := len(p.arg2Buf)\n\tif p.overrideArg2Len > 0 {\n\t\targ2Len = p.overrideArg2Len\n\t}\n\tpayload.WriteUint16(uint16(arg2Len))\n\tpayload.WriteBytes(p.arg2Buf)\n\n\tif !p.skipArg3 {\n\t\targ3Len := len(p.arg3Buf)\n\t\tpayload.WriteUint16(uint16(arg3Len))\n\t\tpayload.WriteBytes(p.arg3Buf)\n\t}\n\tf.Payload = f.Payload[:payload.BytesWritten()]\n\n\trequire.NoError(t, payload.Err(), \"failed to write payload\")\n\treturn f\n}\n\nfunc withLazyCallReqCombinations(f func(cr testCallReq)) {\n\tfor cr := testCallReq(0); cr < reqTotalCombinations; cr++ {\n\t\tf(cr)\n\t}\n}\n\ntype testCallRes int\n\ntype testCallResParams struct {\n\thasFragmentedArg2 bool\n\n\tflags       byte\n\tcode        byte\n\tspan        [25]byte\n\tisThrift    bool\n\theaders     map[string]string\n\tcsumType    byte\n\targ1        []byte\n\targ2Prefix  []byte // used for corrupting arg2\n\targ2KeyVals map[string]string\n\targ3        []byte\n}\n\nconst (\n\tresIsContinued testCallRes = (1 << iota)\n\tresIsOK\n\tresHasHeaders\n\tresHasChecksum\n\tresIsThrift\n\tresHasArg2\n\tresHasFragmentedArg2\n\tresTotalCombinations\n)\n\nfunc (cr testCallRes) res(tb testing.TB) lazyCallRes {\n\tvar params testCallResParams\n\n\tif cr&resHasFragmentedArg2 != 0 {\n\t\tparams.hasFragmentedArg2 = true\n\t}\n\tif cr&(resIsContinued|resHasFragmentedArg2) != 0 {\n\t\tparams.flags |= hasMoreFragmentsFlag\n\t}\n\tif cr&resIsOK == 0 {\n\t\tparams.code = 1\n\t}\n\tparams.headers = map[string]string{}\n\tif cr&resHasHeaders != 0 {\n\t\tparams.headers[\"k1\"] = \"v1\"\n\t\tparams.headers[\"k222222\"] = \"\"\n\t\tparams.headers[\"k3\"] = \"thisisalonglongkey\"\n\t}\n\tif cr&(resIsThrift) != 0 {\n\t\tparams.isThrift = true\n\t\tparams.headers[string(_argSchemeKeyBytes)] = string(_tchanThriftValueBytes)\n\t}\n\tif cr&resHasChecksum != 0 {\n\t\tparams.csumType = byte(ChecksumTypeCrc32C)\n\t}\n\tif cr&resHasArg2 != 0 {\n\t\tparams.arg2KeyVals = exampleArg2Map\n\t}\n\n\tlcr, err := newLazyCallRes(newCallResFrame(tb, params))\n\trequire.NoError(tb, err, \"Unexpected error creating lazyCallRes\")\n\n\treturn lcr\n}\n\nfunc withLazyCallResCombinations(t *testing.T, f func(t *testing.T, cr testCallRes)) {\n\tfor cr := testCallRes(0); cr < resTotalCombinations; cr++ {\n\t\tt.Run(fmt.Sprintf(\"cr=%v\", strconv.FormatInt(int64(cr), 2)), func(t *testing.T) {\n\t\t\tf(t, cr)\n\t\t})\n\t}\n}\n\nfunc newCallResFrame(tb testing.TB, p testCallResParams) *Frame {\n\tf := NewFrame(MaxFramePayloadSize)\n\tfh := fakeHeader(messageTypeCallRes)\n\tpayload := typed.NewWriteBuffer(f.Payload)\n\n\tdefer func() {\n\t\tfh.SetPayloadSize(uint16(payload.BytesWritten()))\n\t\tf.Header = fh\n\t\trequire.NoError(tb, fh.write(typed.NewWriteBuffer(f.headerBuffer)), \"Failed to write header\")\n\t}()\n\n\tpayload.WriteSingleByte(p.flags)              // flags\n\tpayload.WriteSingleByte(p.code)               // code\n\tpayload.WriteBytes(p.span[:])                 // span\n\tpayload.WriteSingleByte(byte(len(p.headers))) // headers\n\tfor k, v := range p.headers {\n\t\tpayload.WriteSingleByte(byte(len(k)))\n\t\tpayload.WriteBytes([]byte(k))\n\t\tpayload.WriteSingleByte(byte(len(v)))\n\t\tpayload.WriteBytes([]byte(v))\n\t}\n\tpayload.WriteSingleByte(p.csumType)                                       // checksum type\n\tpayload.WriteBytes(make([]byte, ChecksumType(p.csumType).ChecksumSize())) // dummy checksum (not used in tests)\n\n\t// arg1\n\tpayload.WriteUint16(uint16(len(p.arg1)))\n\tpayload.WriteBytes(p.arg1)\n\trequire.NoError(tb, payload.Err(), \"Got unexpected error constructing callRes frame\")\n\n\t// arg2\n\tpayload.WriteBytes(p.arg2Prefix) // prefix is used only for corrupting arg2\n\targ2SizeRef := payload.DeferUint16()\n\targ2StartBytes := payload.BytesWritten()\n\tif p.isThrift {\n\t\targ2NHRef := payload.DeferUint16()\n\t\tvar arg2NH uint16\n\t\tfor k, v := range p.arg2KeyVals {\n\t\t\targ2NH++\n\t\t\tpayload.WriteLen16String(k)\n\t\t\tpayload.WriteLen16String(v)\n\t\t}\n\t\tif p.hasFragmentedArg2 {\n\t\t\t// fill remainder of frame with the next key/val\n\t\t\targ2NH++\n\t\t\tpayload.WriteLen16String(\"ube\")\n\t\t\tpayload.WriteLen16String(strings.Repeat(\"r\", payload.BytesRemaining()-2))\n\t\t}\n\t\targ2NHRef.Update(arg2NH)\n\t} else {\n\t\tfor k, v := range p.arg2KeyVals {\n\t\t\tpayload.WriteString(k + v)\n\t\t}\n\t\tif p.hasFragmentedArg2 {\n\t\t\tpayload.WriteString(\"ube\" + strings.Repeat(\"r\", payload.BytesRemaining()-3))\n\t\t}\n\t}\n\trequire.NoError(tb, payload.Err(), \"Got unexpected error constructing callRes frame\")\n\targ2SizeRef.Update(uint16(payload.BytesWritten() - arg2StartBytes))\n\n\tif !p.hasFragmentedArg2 {\n\t\t// arg3\n\t\tpayload.WriteUint16(uint16(len(p.arg3)))\n\t\tpayload.WriteBytes(p.arg3)\n\t}\n\n\trequire.NoError(tb, payload.Err(), \"Got unexpected error constructing callRes frame\")\n\n\treturn f\n}\n\nfunc (ec SystemErrCode) fakeErrFrame() lazyError {\n\tf := NewFrame(100)\n\tfh := FrameHeader{\n\t\tsize:        uint16(0xFF34),\n\t\tmessageType: messageTypeError,\n\t\tID:          invalidMessageID,\n\t}\n\tf.Header = fh\n\tfh.write(typed.NewWriteBuffer(f.headerBuffer))\n\n\tpayload := typed.NewWriteBuffer(f.Payload)\n\tpayload.WriteSingleByte(byte(ec))\n\tpayload.WriteBytes(make([]byte, 25)) // tracing\n\n\tmsg := ec.String()\n\tpayload.WriteUint16(uint16(len(msg)))\n\tpayload.WriteBytes([]byte(msg))\n\treturn newLazyError(f)\n}\n\nfunc withLazyErrorCombinations(f func(ec SystemErrCode)) {\n\tcodes := []SystemErrCode{\n\t\tErrCodeInvalid,\n\t\tErrCodeTimeout,\n\t\tErrCodeCancelled,\n\t\tErrCodeBusy,\n\t\tErrCodeDeclined,\n\t\tErrCodeUnexpected,\n\t\tErrCodeBadRequest,\n\t\tErrCodeNetwork,\n\t\tErrCodeProtocol,\n\t}\n\tfor _, ec := range codes {\n\t\tf(ec)\n\t}\n}\n\nfunc addRandomHeaders(headers map[string]string) {\n\theaders[\"k1\"] = \"v1\"\n\theaders[\"k222222\"] = \"\"\n\theaders[\"k3\"] = \"thisisalonglongkey\"\n}\n\nfunc writeHeaders(w *typed.WriteBuffer, headers map[string]string) {\n\tw.WriteSingleByte(byte(len(headers))) // number of headers\n\tfor k, v := range headers {\n\t\tw.WriteLen8String(k)\n\t\tw.WriteLen8String(v)\n\t}\n}\n\nfunc assertWrappingPanics(t testing.TB, f *Frame, wrap func(f *Frame)) {\n\tassert.Panics(t, func() {\n\t\twrap(f)\n\t}, \"Should panic when wrapping an unexpected frame type.\")\n}\n\nfunc TestLazyCallReqRejectsOtherFrames(t *testing.T) {\n\tassertWrappingPanics(\n\t\tt,\n\t\tresIsContinued.res(t).Frame,\n\t\tfunc(f *Frame) { newLazyCallReq(f) },\n\t)\n}\n\nfunc TestLazyCallReqService(t *testing.T) {\n\twithLazyCallReqCombinations(func(crt testCallReq) {\n\t\tcr := crt.req(t)\n\t\tassert.Equal(t, exampleService, string(cr.Service()), \"Service name mismatch\")\n\t})\n}\n\nfunc TestLazyCallReqCaller(t *testing.T) {\n\twithLazyCallReqCombinations(func(crt testCallReq) {\n\t\tcr := crt.req(t)\n\t\tif crt&reqHasCaller == 0 {\n\t\t\tassert.Equal(t, []byte(nil), cr.Caller(), \"Unexpected caller name.\")\n\t\t} else {\n\t\t\tassert.Equal(t, \"fake-caller\", string(cr.Caller()), \"Caller name mismatch\")\n\t\t}\n\t})\n}\n\nfunc TestLazyCallReqRoutingDelegate(t *testing.T) {\n\twithLazyCallReqCombinations(func(crt testCallReq) {\n\t\tcr := crt.req(t)\n\t\tif crt&reqHasDelegate == 0 {\n\t\t\tassert.Equal(t, []byte(nil), cr.RoutingDelegate(), \"Unexpected routing delegate.\")\n\t\t} else {\n\t\t\tassert.Equal(t, \"fake-delegate\", string(cr.RoutingDelegate()), \"Routing delegate mismatch.\")\n\t\t}\n\t})\n}\n\nfunc TestLazyCallReqRoutingKey(t *testing.T) {\n\twithLazyCallReqCombinations(func(crt testCallReq) {\n\t\tcr := crt.req(t)\n\t\tif crt&reqHasRoutingKey == 0 {\n\t\t\tassert.Equal(t, []byte(nil), cr.RoutingKey(), \"Unexpected routing key.\")\n\t\t} else {\n\t\t\tassert.Equal(t, \"fake-routingkey\", string(cr.RoutingKey()), \"Routing key mismatch.\")\n\t\t}\n\t})\n}\n\nfunc TestLazyCallReqMethod(t *testing.T) {\n\twithLazyCallReqCombinations(func(crt testCallReq) {\n\t\tcr := crt.req(t)\n\t\tassert.Equal(t, \"moneys\", string(cr.Method()), \"Method name mismatch\")\n\t})\n}\n\nfunc TestLazyCallReqTTL(t *testing.T) {\n\twithLazyCallReqCombinations(func(crt testCallReq) {\n\t\tcr := crt.req(t)\n\t\tassert.Equal(t, 42*time.Millisecond, cr.TTL(), \"Failed to parse TTL from frame.\")\n\t})\n}\n\nfunc TestLazyCallReqSetTTL(t *testing.T) {\n\twithLazyCallReqCombinations(func(crt testCallReq) {\n\t\tcr := crt.req(t)\n\t\tcr.SetTTL(time.Second)\n\t\tassert.Equal(t, time.Second, cr.TTL(), \"Failed to write TTL to frame.\")\n\t})\n}\n\nfunc TestLazyCallArg2Offset(t *testing.T) {\n\twantArg2Buf := []byte(\"test arg2 buf\")\n\ttests := []struct {\n\t\tmsg     string\n\t\tflags   byte\n\t\targ2Buf []byte\n\t}{\n\t\t{\n\t\t\tmsg:     \"arg2 is fully contained in frame\",\n\t\t\targ2Buf: wantArg2Buf,\n\t\t},\n\t\t{\n\t\t\tmsg: \"has no arg2\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"frame fragmented but arg2 is fully contained\",\n\t\t\tflags:   hasMoreFragmentsFlag,\n\t\t\targ2Buf: wantArg2Buf,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\twithLazyCallReqCombinations(func(crt testCallReq) {\n\t\t\t\tcr := crt.reqWithParams(t, testCallReqParams{\n\t\t\t\t\tflags:   tt.flags,\n\t\t\t\t\targ2Buf: tt.arg2Buf,\n\t\t\t\t})\n\t\t\t\targ2EndOffset, hasMore := cr.Arg2EndOffset()\n\t\t\t\tassert.False(t, hasMore)\n\t\t\t\tif len(tt.arg2Buf) == 0 {\n\t\t\t\t\tassert.Zero(t, arg2EndOffset-cr.Arg2StartOffset())\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\targ2Payload := cr.Payload[cr.Arg2StartOffset():arg2EndOffset]\n\t\t\t\tassert.Equal(t, tt.arg2Buf, arg2Payload)\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"no arg3 set\", func(t *testing.T) {\n\t\ttests := []struct {\n\t\t\tmsg       string\n\t\t\thasMore   bool\n\t\t\twantError string\n\t\t}{\n\t\t\t{\n\t\t\t\tmsg:     \"hasMore flag=true\",\n\t\t\t\thasMore: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:       \"hasMore flag=false\",\n\t\t\t\twantError: \"buffer is too small\",\n\t\t\t},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(fmt.Sprintf(tt.msg), func(t *testing.T) {\n\t\t\t\twithLazyCallReqCombinations(func(crt testCallReq) {\n\t\t\t\t\t// For each CallReq, we first get the remaining space left, and\n\t\t\t\t\t// fill up the remaining space with arg2.\n\t\t\t\t\tcrNoArg2 := crt.req(t)\n\t\t\t\t\targ2Size := MaxFramePayloadSize - crNoArg2.Arg2StartOffset()\n\t\t\t\t\tvar flags byte\n\t\t\t\t\tif tt.hasMore {\n\t\t\t\t\t\tflags |= hasMoreFragmentsFlag\n\t\t\t\t\t}\n\t\t\t\t\tf := crt.frameWithParams(t, testCallReqParams{\n\t\t\t\t\t\tflags:    flags,\n\t\t\t\t\t\targ2Buf:  make([]byte, arg2Size),\n\t\t\t\t\t\tskipArg3: true,\n\t\t\t\t\t})\n\t\t\t\t\tcr, err := newLazyCallReq(f)\n\t\t\t\t\tif tt.wantError != \"\" {\n\t\t\t\t\t\trequire.EqualError(t, err, tt.wantError)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tendOffset, hasMore := cr.Arg2EndOffset()\n\t\t\t\t\tassert.Equal(t, hasMore, tt.hasMore)\n\t\t\t\t\tassert.EqualValues(t, MaxFramePayloadSize, endOffset)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestLazyCallReqSetTChanThriftArg2(t *testing.T) {\n\ttests := []struct {\n\t\tmsg            string\n\t\tbufKV          map[string]string\n\t\twantKV         map[string]string // if not set, use bufKV\n\t\targScheme      Format\n\t\toverrideBufLen int\n\t\twantBadErr     string\n\t}{\n\t\t{\n\t\t\tmsg:       \"two key value pairs\",\n\t\t\targScheme: Thrift,\n\t\t\tbufKV: map[string]string{\n\t\t\t\t\"key\":  \"val\",\n\t\t\t\t\"key2\": \"val2\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg:       \"length not enough to cover key len\",\n\t\t\targScheme: Thrift,\n\t\t\tbufKV: map[string]string{\n\t\t\t\t\"key\": \"val\",\n\t\t\t},\n\t\t\toverrideBufLen: 3, // 2 (nh) + 2 - 1\n\t\t\twantBadErr:     \"buffer is too small\",\n\t\t},\n\t\t{\n\t\t\tmsg:       \"length not enough to cover key\",\n\t\t\targScheme: Thrift,\n\t\t\tbufKV: map[string]string{\n\t\t\t\t\"key\": \"val\",\n\t\t\t},\n\t\t\toverrideBufLen: 6, // 2 (nh) + 2 + len(key) - 1\n\t\t\twantBadErr:     \"buffer is too small\",\n\t\t},\n\t\t{\n\t\t\tmsg:       \"length not enough to cover value len\",\n\t\t\targScheme: Thrift,\n\t\t\tbufKV: map[string]string{\n\t\t\t\t\"key\": \"val\",\n\t\t\t},\n\t\t\toverrideBufLen: 8, // 2 (nh) + 2 + len(key) + 2 - 1\n\t\t\twantBadErr:     \"buffer is too small\",\n\t\t},\n\t\t{\n\t\t\tmsg:       \"length not enough to cover value\",\n\t\t\targScheme: Thrift,\n\t\t\tbufKV: map[string]string{\n\t\t\t\t\"key\": \"val\",\n\t\t\t},\n\t\t\toverrideBufLen: 10, // 2 (nh) + 2 + len(key) + 2 + len(val) - 2\n\t\t\twantBadErr:     \"buffer is too small\",\n\t\t},\n\t\t{\n\t\t\tmsg:       \"no key value pairs\",\n\t\t\targScheme: Thrift,\n\t\t\tbufKV:     map[string]string{},\n\t\t},\n\t\t{\n\t\t\tmsg:        \"not tchannel thrift\",\n\t\t\targScheme:  HTTP,\n\t\t\tbufKV:      map[string]string{\"key\": \"val\"},\n\t\t\twantBadErr: \"non-Thrift\",\n\t\t},\n\t\t{\n\t\t\tmsg:        \"not arg scheme\",\n\t\t\tbufKV:      map[string]string{\"key\": \"val\"},\n\t\t\twantBadErr: \"non-Thrift\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\twithLazyCallReqCombinations(func(crt testCallReq) {\n\t\t\t\targ2Buf := thriftarg2test.BuildKVBuffer(tt.bufKV)\n\t\t\t\tif tt.overrideBufLen > 0 {\n\t\t\t\t\targ2Buf = arg2Buf[:tt.overrideBufLen]\n\t\t\t\t}\n\t\t\t\tcr := crt.reqWithParams(t, testCallReqParams{\n\t\t\t\t\targ2Buf:   arg2Buf,\n\t\t\t\t\targScheme: tt.argScheme,\n\t\t\t\t})\n\t\t\t\tgotIter := make(map[string]string)\n\t\t\t\titer, err := cr.Arg2Iterator()\n\t\t\t\tfor err == nil {\n\t\t\t\t\tgotIter[string(iter.Key())] = string(iter.Value())\n\t\t\t\t\titer, err = iter.Next()\n\t\t\t\t}\n\t\t\t\tif tt.wantBadErr != \"\" {\n\t\t\t\t\trequire.NotEqual(t, io.EOF, err, \"should not return EOF for iterator exit\")\n\t\t\t\t\tassert.Contains(t, err.Error(), tt.wantBadErr)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Equal(t, io.EOF, err, \"should return EOF for iterator exit\")\n\t\t\t\t\twantKV := tt.wantKV\n\t\t\t\t\tif wantKV == nil {\n\t\t\t\t\t\twantKV = tt.bufKV\n\t\t\t\t\t}\n\t\t\t\t\tassert.Equal(t, wantKV, gotIter, \"unexpected arg2 keys, call req %+v\", crt)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"bad Arg2 length\", func(t *testing.T) {\n\t\twithLazyCallReqCombinations(func(crt testCallReq) {\n\t\t\tcrNoArg2 := crt.req(t)\n\t\t\tleftSpace := int(crNoArg2.Header.PayloadSize()) - crNoArg2.Arg2StartOffset()\n\t\t\tfrm := crt.frameWithParams(t, testCallReqParams{\n\t\t\t\targ2Buf:         make([]byte, leftSpace),\n\t\t\t\targScheme:       Thrift,\n\t\t\t\toverrideArg2Len: leftSpace + 5, // Arg2 length extends beyond payload\n\t\t\t})\n\t\t\t_, err := newLazyCallReq(frm)\n\t\t\tassert.EqualError(t, err, \"buffer is too small\")\n\t\t})\n\t})\n}\n\nfunc TestLazyCallResRejectsOtherFrames(t *testing.T) {\n\tassertWrappingPanics(\n\t\tt,\n\t\treqHasHeaders.req(t).Frame,\n\t\tfunc(f *Frame) { newLazyCallRes(f) },\n\t)\n}\n\nfunc TestLazyCallRes(t *testing.T) {\n\twithLazyCallResCombinations(t, func(t *testing.T, crt testCallRes) {\n\t\tcr := crt.res(t)\n\n\t\t// isOK\n\t\tif crt&resIsOK == 0 {\n\t\t\tassert.False(t, cr.OK(), \"Expected call res to have a non-ok code.\")\n\t\t} else {\n\t\t\tassert.True(t, cr.OK(), \"Expected call res to have code ok.\")\n\t\t}\n\n\t\t// isThrift\n\t\tif crt&resIsThrift != 0 {\n\t\t\tassert.Equal(t, Thrift.String(), string(cr.ArgScheme()), \"Expected call res to have isThrift=true\")\n\t\t\tassert.Equal(t, cr.as, _tchanThriftValueBytes, \"Expected arg scheme to be thrift\")\n\t\t} else {\n\t\t\tassert.NotEqual(t, Thrift.String(), string(cr.ArgScheme()), \"Expected call res to have isThrift=false\")\n\t\t\tassert.NotEqual(t, cr.as, _tchanThriftValueBytes, \"Expected arg scheme to not be thrift\")\n\t\t}\n\n\t\t// arg2IsFragmented\n\t\tif crt&resHasFragmentedArg2 != 0 {\n\t\t\tassert.True(t, cr.Arg2IsFragmented(), \"Expected arg2 to be fragmented\")\n\t\t}\n\n\t\tif crt&resIsThrift != 0 {\n\t\t\titer, err := arg2.NewKeyValIterator(cr.Arg2())\n\t\t\tif crt&resHasArg2 != 0 || crt&resHasFragmentedArg2 != 0 {\n\t\t\t\trequire.NoError(t, err, \"Got unexpected error for .Arg2()\")\n\t\t\t\tkvMap := make(map[string]string)\n\t\t\t\tfor ; err == nil; iter, err = iter.Next() {\n\t\t\t\t\tkvMap[string(iter.Key())] = string(iter.Value())\n\t\t\t\t}\n\t\t\t\tif crt&resHasArg2 != 0 {\n\t\t\t\t\tfor k, v := range exampleArg2Map {\n\t\t\t\t\t\tassert.Equal(t, kvMap[k], v)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trequire.Error(t, err, io.EOF, \"Got unexpected error for .Arg2()\")\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestNewLazyCallResCorruptedFrame(t *testing.T) {\n\t_, err := newLazyCallRes(newCallResFrame(t, testCallResParams{\n\t\targ2Prefix:  []byte{0, 100},\n\t\targ2KeyVals: exampleArg2Map,\n\t}))\n\n\trequire.EqualError(t, err, \"read response frame: buffer is too small\", \"Got unexpected error for corrupted frame\")\n}\n\nfunc TestLazyErrorRejectsOtherFrames(t *testing.T) {\n\tassertWrappingPanics(\n\t\tt,\n\t\treqHasHeaders.req(t).Frame,\n\t\tfunc(f *Frame) { newLazyError(f) },\n\t)\n}\n\nfunc TestLazyErrorCodes(t *testing.T) {\n\twithLazyErrorCombinations(func(ec SystemErrCode) {\n\t\tf := ec.fakeErrFrame()\n\t\tassert.Equal(t, ec, f.Code(), \"Mismatch between error code and lazy frame's Code() method.\")\n\t})\n}\n\n// TODO(cinchurge): replace with e.g. decodeThriftHeader once we've resolved the import cycle\nfunc uint16KeyValToMap(tb testing.TB, buffer []byte) map[string]string {\n\trbuf := typed.NewReadBuffer(buffer)\n\tnh := int(rbuf.ReadUint16())\n\tretMap := make(map[string]string, nh)\n\tfor i := 0; i < nh; i++ {\n\t\tkeyLen := int(rbuf.ReadUint16())\n\t\tkey := rbuf.ReadBytes(keyLen)\n\t\tvalLen := int(rbuf.ReadUint16())\n\t\tval := rbuf.ReadBytes(valLen)\n\t\tretMap[string(key)] = string(val)\n\t}\n\trequire.NoError(tb, rbuf.Err())\n\treturn retMap\n}\n\nfunc TestLazyCallReqContents(t *testing.T) {\n\tcr := reqHasAll.reqWithParams(t, testCallReqParams{\n\t\targ2Buf: thriftarg2test.BuildKVBuffer(exampleArg2Map),\n\t\targ3Buf: []byte(exampleArg3Data),\n\t})\n\n\tt.Run(\"checksum\", func(t *testing.T) {\n\t\tassert.Equal(t, ChecksumTypeCrc32C, cr.checksumType, \"Got unexpected checksum type\")\n\t\tassert.Equal(t, byte(ChecksumTypeCrc32C), cr.Frame.Payload[cr.checksumTypeOffset], \"Unexpected value read from checksum offset\")\n\t})\n\n\tt.Run(\".arg2()\", func(t *testing.T) {\n\t\tassert.Equal(t, exampleArg2Map, uint16KeyValToMap(t, cr.arg2()), \"Got unexpected headers\")\n\t})\n\n\tt.Run(\".arg3()\", func(t *testing.T) {\n\t\t// TODO(echung): switch to assert.Equal once we have more robust test frame generation\n\t\tassert.Contains(t, string(cr.arg3()), exampleArg3Data, \"Got unexpected headers\")\n\t})\n}\n\nfunc TestLazyCallReqLargeService(t *testing.T) {\n\tfor _, svcSize := range []int{10, 100, 200, 240, math.MaxInt8} {\n\t\tt.Run(fmt.Sprintf(\"size=%v\", svcSize), func(t *testing.T) {\n\t\t\tlargeService := strings.Repeat(\"a\", svcSize)\n\t\t\twithLazyCallReqCombinations(func(cr testCallReq) {\n\t\t\t\tf := cr.frameWithParams(t, testCallReqParams{\n\t\t\t\t\tserviceOverride: largeService,\n\t\t\t\t})\n\n\t\t\t\tcallReq, err := newLazyCallReq(f)\n\t\t\t\trequire.NoError(t, err, \"newLazyCallReq failed\")\n\n\t\t\t\tassert.Equal(t, largeService, string(callReq.Service()), \"service name mismatch\")\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "relay_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/benchmark\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/relay\"\n\t\"github.com/uber/tchannel-go/relay/relaytest\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\t\"github.com/uber/tchannel-go/testutils/testreader\"\n\t\"github.com/uber/tchannel-go/testutils/thriftarg2test\"\n\t\"github.com/uber/tchannel-go/thrift\"\n\t\"github.com/uber/tchannel-go/thrift/arg2\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\ntype relayTest struct {\n\ttestutils.TestServer\n}\n\nfunc serviceNameOpts(s string) *testutils.ChannelOpts {\n\treturn testutils.NewOpts().SetServiceName(s)\n}\n\nfunc withRelayedEcho(t testing.TB, f func(relay, server, client *Channel, ts *testutils.TestServer)) {\n\topts := serviceNameOpts(\"test\").\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\t\tclient := ts.NewClient(serviceNameOpts(\"client\"))\n\t\tclient.Peers().Add(ts.HostPort())\n\t\tf(ts.Relay(), ts.Server(), client, ts)\n\t})\n}\n\nfunc TestRelay(t *testing.T) {\n\twithRelayedEcho(t, func(_, _, client *Channel, ts *testutils.TestServer) {\n\t\ttests := []struct {\n\t\t\theader string\n\t\t\tbody   string\n\t\t}{\n\t\t\t{\"fake-header\", \"fake-body\"},                        // fits in one frame\n\t\t\t{\"fake-header\", strings.Repeat(\"fake-body\", 10000)}, // requires continuation\n\t\t}\n\t\tsc := client.GetSubChannel(\"test\")\n\t\tfor _, tt := range tests {\n\t\t\tctx, cancel := NewContext(time.Second)\n\t\t\tdefer cancel()\n\n\t\t\targ2, arg3, _, err := raw.CallSC(ctx, sc, \"echo\", []byte(tt.header), []byte(tt.body))\n\t\t\trequire.NoError(t, err, \"Relayed call failed.\")\n\t\t\tassert.Equal(t, tt.header, string(arg2), \"Header was mangled during relay.\")\n\t\t\tassert.Equal(t, tt.body, string(arg3), \"Body was mangled during relay.\")\n\t\t}\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tfor range tests {\n\t\t\tcalls.Add(\"client\", \"test\", \"echo\").Succeeded().End()\n\t\t}\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestRelaySetHost(t *testing.T) {\n\trh := relaytest.NewStubRelayHost()\n\topts := serviceNameOpts(\"test\").\n\t\tSetRelayHost(rh).\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\n\t\tclient := ts.NewClient(serviceNameOpts(\"client\"))\n\t\tclient.Peers().Add(ts.HostPort())\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.Server().ServiceName())\n\t})\n}\n\nfunc TestRelayHandlesClosedPeers(t *testing.T) {\n\topts := serviceNameOpts(\"test\").\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\t// Disable logs as we are closing connections that can error in a lot of places.\n\t\tDisableLogVerification()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tctx, cancel := NewContext(300 * time.Millisecond)\n\t\tdefer cancel()\n\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\t\tclient := ts.NewClient(serviceNameOpts(\"client\"))\n\t\tclient.Peers().Add(ts.HostPort())\n\n\t\tsc := client.GetSubChannel(\"test\")\n\t\t_, _, _, err := raw.CallSC(ctx, sc, \"echo\", []byte(\"fake-header\"), []byte(\"fake-body\"))\n\t\trequire.NoError(t, err, \"Relayed call failed.\")\n\n\t\tts.Server().Close()\n\t\trequire.NotPanics(t, func() {\n\t\t\traw.CallSC(ctx, sc, \"echo\", []byte(\"fake-header\"), []byte(\"fake-body\"))\n\t\t})\n\t})\n}\n\nfunc TestRelayConnectionCloseDrainsRelayItems(t *testing.T) {\n\topts := serviceNameOpts(\"s1\").\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\ts1 := ts.Server()\n\t\ts2 := ts.NewServer(serviceNameOpts(\"s2\"))\n\n\t\ts2HP := s2.PeerInfo().HostPort\n\t\ttestutils.RegisterEcho(s1, func() {\n\t\t\t// When s1 gets called, it calls Close on the connection from the relay to s2.\n\t\t\tconn, err := ts.Relay().Peers().GetOrAdd(s2HP).GetConnection(ctx)\n\t\t\trequire.NoError(t, err, \"Unexpected failure getting connection between s1 and relay\")\n\t\t\tconn.Close()\n\t\t})\n\n\t\ttestutils.AssertEcho(t, s2, ts.HostPort(), \"s1\")\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(\"s2\", \"s1\", \"echo\").Succeeded().End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestRelayIDClash(t *testing.T) {\n\t// TODO: enable framepool checks\n\topts := serviceNameOpts(\"s1\").\n\t\tSetRelayOnly()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ts1 := ts.Server()\n\t\ts2 := ts.NewServer(serviceNameOpts(\"s2\"))\n\n\t\tunblock := make(chan struct{})\n\t\ttestutils.RegisterEcho(s1, func() {\n\t\t\t<-unblock\n\t\t})\n\t\ttestutils.RegisterEcho(s2, nil)\n\n\t\tvar wg sync.WaitGroup\n\t\tfor i := 0; i < 10; i++ {\n\t\t\twg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\t\t\t\ttestutils.AssertEcho(t, s2, ts.HostPort(), s1.ServiceName())\n\t\t\t}()\n\t\t}\n\n\t\tfor i := 0; i < 5; i++ {\n\t\t\ttestutils.AssertEcho(t, s1, ts.HostPort(), s2.ServiceName())\n\t\t}\n\n\t\tclose(unblock)\n\t\twg.Wait()\n\t})\n}\n\nfunc TestRelayErrorsOnGetPeer(t *testing.T) {\n\tbusyErr := NewSystemError(ErrCodeBusy, \"busy\")\n\ttests := []struct {\n\t\tdesc       string\n\t\treturnPeer string\n\t\treturnErr  error\n\t\tstatsKey   string\n\t\twantErr    error\n\t}{\n\t\t{\n\t\t\tdesc:       \"No peer and no error\",\n\t\t\treturnPeer: \"\",\n\t\t\treturnErr:  nil,\n\t\t\tstatsKey:   \"relay-bad-relay-host\",\n\t\t\twantErr:    NewSystemError(ErrCodeDeclined, `bad relay host implementation`),\n\t\t},\n\t\t{\n\t\t\tdesc:      \"System error getting peer\",\n\t\t\treturnErr: busyErr,\n\t\t\tstatsKey:  \"relay-busy\",\n\t\t\twantErr:   busyErr,\n\t\t},\n\t\t{\n\t\t\tdesc:      \"Unknown error getting peer\",\n\t\t\treturnErr: errors.New(\"unknown\"),\n\t\t\tstatsKey:  \"relay-declined\",\n\t\t\twantErr:   NewSystemError(ErrCodeDeclined, \"unknown\"),\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tf := func(relay.CallFrame, *relay.Conn) (string, error) {\n\t\t\t\treturn tt.returnPeer, tt.returnErr\n\t\t\t}\n\n\t\t\topts := testutils.NewOpts().\n\t\t\t\tSetRelayHost(relaytest.HostFunc(f)).\n\t\t\t\tSetRelayOnly().\n\t\t\t\tSetCheckFramePooling().\n\t\t\t\tDisableLogVerification() // some of the test cases cause warnings.\n\t\t\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\t\tclient := ts.NewClient(nil)\n\t\t\t\terr := testutils.CallEcho(client, ts.HostPort(), \"svc\", nil)\n\t\t\t\trequire.Error(t, err, \"Call to unknown service should fail\")\n\t\t\t\tassert.Equal(t, tt.wantErr, err, \"unexpected error\")\n\n\t\t\t\tcalls := relaytest.NewMockStats()\n\t\t\t\tcalls.Add(client.PeerInfo().ServiceName, \"svc\", \"echo\").\n\t\t\t\t\tFailed(tt.statsKey).End()\n\t\t\t\tts.AssertRelayStats(calls)\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestErrorFrameEndsRelay(t *testing.T) {\n\t// TestServer validates that there are no relay items left after the given func.\n\topts := serviceNameOpts(\"svc\").\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tDisableLogVerification()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tclient := ts.NewClient(nil)\n\n\t\terr := testutils.CallEcho(client, ts.HostPort(), \"svc\", nil)\n\t\tif !assert.Error(t, err, \"Expected error due to unknown method\") {\n\t\t\treturn\n\t\t}\n\n\t\tse, ok := err.(SystemError)\n\t\tif !assert.True(t, ok, \"err should be a SystemError, got %T\", err) {\n\t\t\treturn\n\t\t}\n\n\t\tassert.Equal(t, ErrCodeBadRequest, se.Code(), \"Expected BadRequest error\")\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(client.PeerInfo().ServiceName, \"svc\", \"echo\").Failed(\"bad-request\").End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\n// Trigger a race between receiving a new call and a connection closing\n// by closing the relay while a lot of background calls are being made.\nfunc TestRaceCloseWithNewCall(t *testing.T) {\n\topts := serviceNameOpts(\"s1\").\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tDisableLogVerification()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ts1 := ts.Server()\n\t\ts2 := ts.NewServer(serviceNameOpts(\"s2\").DisableLogVerification())\n\t\ttestutils.RegisterEcho(s1, nil)\n\n\t\t// signal to start closing the relay.\n\t\tvar (\n\t\t\tcloseRelay  sync.WaitGroup\n\t\t\tstopCalling atomic.Int32\n\t\t\tcallers     sync.WaitGroup\n\t\t)\n\n\t\tfor i := 0; i < 5; i++ {\n\t\t\tcallers.Add(1)\n\t\t\tcloseRelay.Add(1)\n\n\t\t\tgo func() {\n\t\t\t\tdefer callers.Done()\n\n\t\t\t\tcalls := 0\n\t\t\t\tfor stopCalling.Load() == 0 {\n\t\t\t\t\ttestutils.CallEcho(s2, ts.HostPort(), \"s1\", nil)\n\t\t\t\t\tcalls++\n\t\t\t\t\tif calls == 5 {\n\t\t\t\t\t\tcloseRelay.Done()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\tcloseRelay.Wait()\n\n\t\t// Close the relay, wait for it to close.\n\t\tts.Relay().Close()\n\t\tclosed := testutils.WaitFor(time.Second, func() bool {\n\t\t\treturn ts.Relay().State() == ChannelClosed\n\t\t})\n\t\tassert.True(t, closed, \"Relay did not close within timeout\")\n\n\t\t// Now stop all calls, and wait for the calling goroutine to end.\n\t\tstopCalling.Inc()\n\t\tcallers.Wait()\n\t})\n}\n\nfunc TestTimeoutCallsThenClose(t *testing.T) {\n\t// TODO: enable framepool checks\n\t// Test needs at least 2 CPUs to trigger race conditions.\n\tdefer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))\n\n\topts := serviceNameOpts(\"s1\").\n\t\tSetRelayOnly().\n\t\tDisableLogVerification()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ts1 := ts.Server()\n\t\ts2 := ts.NewServer(serviceNameOpts(\"s2\").DisableLogVerification())\n\n\t\tunblockEcho := make(chan struct{})\n\t\ttestutils.RegisterEcho(s1, func() {\n\t\t\t<-unblockEcho\n\t\t})\n\n\t\tctx, cancel := NewContext(testutils.Timeout(100 * time.Millisecond))\n\t\tdefer cancel()\n\n\t\tvar callers sync.WaitGroup\n\t\tfor i := 0; i < 100; i++ {\n\t\t\tcallers.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer callers.Done()\n\t\t\t\traw.Call(ctx, s2, ts.HostPort(), \"s1\", \"echo\", nil, nil)\n\t\t\t}()\n\t\t}\n\n\t\tclose(unblockEcho)\n\n\t\t// Wait for all the callers to end\n\t\tcallers.Wait()\n\t})\n}\n\nfunc TestLargeTimeoutsAreClamped(t *testing.T) {\n\tconst (\n\t\tclampTTL = time.Millisecond\n\t\tlongTTL  = time.Minute\n\t)\n\n\topts := serviceNameOpts(\"echo-service\").\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tSetRelayMaxTimeout(clampTTL).\n\t\tDisableLogVerification() // handler returns after deadline\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tsrv := ts.Server()\n\t\tclient := ts.NewClient(nil)\n\n\t\tunblock := make(chan struct{})\n\t\tdefer close(unblock) // let server shut down cleanly\n\t\ttestutils.RegisterFunc(srv, \"echo\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\tnow := time.Now()\n\t\t\tdeadline, ok := ctx.Deadline()\n\t\t\tassert.True(t, ok, \"Expected deadline to be set in handler.\")\n\t\t\tassert.True(t, deadline.Sub(now) <= clampTTL, \"Expected relay to clamp TTL sent to backend.\")\n\t\t\t<-unblock\n\t\t\treturn &raw.Res{Arg2: args.Arg2, Arg3: args.Arg3}, nil\n\t\t})\n\n\t\tdone := make(chan struct{})\n\t\tgo func() {\n\t\t\tctx, cancel := NewContext(longTTL)\n\t\t\tdefer cancel()\n\t\t\t_, _, _, err := raw.Call(ctx, client, ts.HostPort(), \"echo-service\", \"echo\", nil, nil)\n\t\t\trequire.Error(t, err)\n\t\t\tcode := GetSystemErrorCode(err)\n\t\t\tassert.Equal(t, ErrCodeTimeout, code)\n\t\t\tclose(done)\n\t\t}()\n\n\t\t// This test is very sensitive to system noise, where a spike of latency in the relay (e.g. caused by load)\n\t\t// is able to cause the client call to timeout, making this test prone to false positives. As such, we\n\t\t// can't time out too close to clampTTL, but instead check that we don't time out after longTTL/2. This might\n\t\t// be a bit generous, but should be sufficient for our purposes here.\n\t\tselect {\n\t\tcase <-time.After(testutils.Timeout(longTTL / 2)):\n\t\t\tt.Fatal(\"Failed to clamp timeout.\")\n\t\tcase <-done:\n\t\t}\n\t})\n}\n\n// TestRelayConcurrentCalls makes many concurrent calls and ensures that\n// we don't try to reuse any frames once they've been released.\nfunc TestRelayConcurrentCalls(t *testing.T) {\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tserver := benchmark.NewServer(\n\t\t\tbenchmark.WithNoLibrary(),\n\t\t\tbenchmark.WithServiceName(\"s1\"),\n\t\t)\n\t\tdefer server.Close()\n\t\tts.RelayHost().Add(\"s1\", server.HostPort())\n\n\t\tclient := benchmark.NewClient([]string{ts.HostPort()},\n\t\t\tbenchmark.WithNoDurations(),\n\t\t\t// TODO(prashant): Enable once we have control over concurrency with NoLibrary.\n\t\t\t// benchmark.WithNoLibrary(),\n\t\t\tbenchmark.WithNumClients(20),\n\t\t\tbenchmark.WithServiceName(\"s1\"),\n\t\t\tbenchmark.WithTimeout(time.Minute),\n\t\t)\n\t\tdefer client.Close()\n\t\trequire.NoError(t, client.Warmup(), \"Client warmup failed\")\n\n\t\t_, err := client.RawCall(1000)\n\t\tassert.NoError(t, err, \"RawCalls failed\")\n\t})\n}\n\n// Ensure that any connections created in the relay path send the ephemeral\n// host:port.\nfunc TestRelayOutgoingConnectionsEphemeral(t *testing.T) {\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ts2 := ts.NewServer(serviceNameOpts(\"s2\"))\n\t\ttestutils.RegisterFunc(s2, \"echo\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\tassert.True(t, CurrentCall(ctx).RemotePeer().IsEphemeral,\n\t\t\t\t\"Connections created for the relay should send ephemeral host:port header\")\n\n\t\t\treturn &raw.Res{\n\t\t\t\tArg2: args.Arg2,\n\t\t\t\tArg3: args.Arg3,\n\t\t\t}, nil\n\t\t})\n\n\t\trequire.NoError(t, testutils.CallEcho(ts.Server(), ts.HostPort(), \"s2\", nil), \"CallEcho failed\")\n\t})\n}\n\nfunc TestRelayHandleLocalCall(t *testing.T) {\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tSetRelayLocal(\"relay\", \"tchannel\", \"test\").\n\t\t// We make a call to \"test\" for an unknown method.\n\t\tAddLogFilter(\"Couldn't find handler.\", 1)\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ts2 := ts.NewServer(serviceNameOpts(\"s2\"))\n\t\ttestutils.RegisterEcho(s2, nil)\n\n\t\tclient := ts.NewClient(nil)\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), \"s2\")\n\n\t\ttestutils.RegisterEcho(ts.Relay(), nil)\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), \"relay\")\n\n\t\t// Sould get a bad request for \"test\" since the channel does not handle it.\n\t\terr := testutils.CallEcho(client, ts.HostPort(), \"test\", nil)\n\t\tassert.Equal(t, ErrCodeBadRequest, GetSystemErrorCode(err), \"Expected BadRequest for test\")\n\n\t\t// But an unknown service causes declined\n\t\terr = testutils.CallEcho(client, ts.HostPort(), \"unknown\", nil)\n\t\tassert.Equal(t, ErrCodeDeclined, GetSystemErrorCode(err), \"Expected Declined for unknown\")\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(client.ServiceName(), \"s2\", \"echo\").Succeeded().End()\n\t\tcalls.Add(client.ServiceName(), \"unknown\", \"echo\").Failed(\"relay-declined\").End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestRelayHandleLargeLocalCall(t *testing.T) {\n\t// TODO: enablle framepool checks\n\topts := testutils.NewOpts().SetRelayOnly().\n\t\tSetRelayLocal(\"relay\").\n\t\tAddLogFilter(\"Received fragmented callReq\", 1).\n\t\t// Expect 4 callReqContinues for 256 kb payload that we cannot relay.\n\t\tAddLogFilter(\"Failed to relay frame.\", 4)\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tclient := ts.NewClient(nil)\n\t\ttestutils.RegisterEcho(ts.Relay(), nil)\n\n\t\t// This large call should fail with a bad request.\n\t\terr := testutils.CallEcho(client, ts.HostPort(), \"relay\", &raw.Args{\n\t\t\tArg2: testutils.RandBytes(128 * 1024),\n\t\t\tArg3: testutils.RandBytes(128 * 1024),\n\t\t})\n\t\tif assert.Equal(t, ErrCodeBadRequest, GetSystemErrorCode(err), \"Expected BadRequest for large call to relay\") {\n\t\t\tassert.Contains(t, err.Error(), \"cannot receive fragmented calls\")\n\t\t}\n\n\t\t// We may get an error before the call is finished flushing.\n\t\t// Do a ping to ensure everything has been flushed.\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\t\trequire.NoError(t, client.Ping(ctx, ts.HostPort()), \"Ping failed\")\n\t})\n}\n\nfunc TestRelayMakeOutgoingCall(t *testing.T) {\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tsvr1 := ts.Relay()\n\t\tsvr2 := ts.NewServer(testutils.NewOpts().SetServiceName(\"svc2\"))\n\t\ttestutils.RegisterEcho(svr2, nil)\n\n\t\tsizes := []int{128, 1024, 128 * 1024}\n\t\tfor _, size := range sizes {\n\t\t\tt.(*testing.T).Run(fmt.Sprintf(\"size=%d\", size), func(t *testing.T) {\n\t\t\t\terr := testutils.CallEcho(svr1, ts.HostPort(), \"svc2\", &raw.Args{\n\t\t\t\t\tArg2: testutils.RandBytes(size),\n\t\t\t\t\tArg3: testutils.RandBytes(size),\n\t\t\t\t})\n\t\t\t\tassert.NoError(t, err, \"Echo with size %v failed\", size)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestRelayInboundConnContext(t *testing.T) {\n\trh := relaytest.NewStubRelayHost()\n\trh.SetFrameFn(func(f relay.CallFrame, conn *relay.Conn) {\n\t\t// Verify that the relay gets the base context set in the server's ConnContext\n\t\tassert.Equal(t, \"bar\", conn.Context.Value(\"foo\"), \"Unexpected value set in base context\")\n\t})\n\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tSetRelayHost(rh).\n\t\tSetConnContext(func(ctx context.Context, conn net.Conn) context.Context {\n\t\t\treturn context.WithValue(ctx, \"foo\", \"bar\")\n\t\t})\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\trly := ts.Relay()\n\t\tsvr := ts.Server()\n\t\ttestutils.RegisterEcho(svr, nil)\n\n\t\tclient := testutils.NewClient(t, nil)\n\t\ttestutils.AssertEcho(t, client, rly.PeerInfo().HostPort, ts.ServiceName())\n\t})\n}\n\nfunc TestRelayContextInheritsFromOutboundConnection(t *testing.T) {\n\trh := relaytest.NewStubRelayHost()\n\trh.SetFrameFn(func(f relay.CallFrame, conn *relay.Conn) {\n\t\t// Verify that the relay gets the base context set by the outbound connection to the caller\n\t\tassert.Equal(t, \"bar\", conn.Context.Value(\"foo\"), \"Unexpected value set in base context\")\n\t})\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tSetRelayHost(rh)\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\trly := ts.Relay()\n\t\tcallee := ts.Server()\n\t\ttestutils.RegisterEcho(callee, nil)\n\n\t\tcaller := ts.NewServer(testutils.NewOpts())\n\t\ttestutils.RegisterEcho(caller, nil)\n\n\t\tbaseCtx := context.WithValue(context.Background(), \"foo\", \"bar\")\n\t\tctx, cancel := NewContextBuilder(time.Second).SetConnectBaseContext(baseCtx).Build()\n\t\tdefer cancel()\n\n\t\trequire.NoError(t, rly.Ping(ctx, caller.PeerInfo().HostPort))\n\t\ttestutils.AssertEcho(t, caller, ts.HostPort(), ts.ServiceName())\n\t})\n}\n\nfunc TestRelayConnection(t *testing.T) {\n\tvar errTest = errors.New(\"test\")\n\tvar gotConn *relay.Conn\n\n\tgetHost := func(_ relay.CallFrame, conn *relay.Conn) (string, error) {\n\t\tgotConn = conn\n\t\treturn \"\", errTest\n\t}\n\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tSetRelayHost(relaytest.HostFunc(getHost))\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tgetConn := func(ch *Channel, outbound bool) ConnectionRuntimeState {\n\t\t\tstate := ch.IntrospectState(nil)\n\t\t\tpeer, ok := state.RootPeers[ts.HostPort()]\n\t\t\trequire.True(t, ok, \"Failed to find peer for relay\")\n\n\t\t\tconns := peer.InboundConnections\n\t\t\tif outbound {\n\t\t\t\tconns = peer.OutboundConnections\n\t\t\t}\n\n\t\t\trequire.Len(t, conns, 1, \"Expect single connection from client to relay\")\n\t\t\treturn conns[0]\n\t\t}\n\n\t\t// Create a client that is listening so we can set the expected host:port.\n\t\tclient := ts.NewClient(nil)\n\n\t\terr := testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil)\n\t\trequire.Error(t, err, \"Expected CallEcho to fail\")\n\t\tassert.Contains(t, err.Error(), errTest.Error(), \"Unexpected error\")\n\n\t\twantConn := &relay.Conn{\n\t\t\tRemoteAddr:        getConn(client, true /* outbound */).LocalHostPort,\n\t\t\tRemoteProcessName: client.PeerInfo().ProcessName,\n\t\t\tIsOutbound:        false,\n\t\t\tContext:           context.Background(),\n\t\t}\n\t\tassert.Equal(t, wantConn, gotConn, \"Unexpected remote addr\")\n\n\t\t// Verify something similar with a listening channel, ensuring that\n\t\t// we're not using the host:port of the listening server, but the\n\t\t// host:port of the outbound TCP connection.\n\t\tlisteningC := ts.NewServer(nil)\n\n\t\terr = testutils.CallEcho(listeningC, ts.HostPort(), ts.ServiceName(), nil)\n\t\trequire.Error(t, err, \"Expected CallEcho to fail\")\n\t\tassert.Contains(t, err.Error(), errTest.Error(), \"Unexpected error\")\n\n\t\tconnHostPort := getConn(listeningC, true /* outbound */).LocalHostPort\n\t\tassert.NotEqual(t, connHostPort, listeningC.PeerInfo().HostPort, \"Ensure connection host:port is not listening host:port\")\n\t\twantConn = &relay.Conn{\n\t\t\tRemoteAddr:        connHostPort,\n\t\t\tRemoteProcessName: listeningC.PeerInfo().ProcessName,\n\t\t\tContext:           context.Background(),\n\t\t}\n\t\tassert.Equal(t, wantConn, gotConn, \"Unexpected remote addr\")\n\n\t\t// Connections created when relaying hide the relay host:port to ensure\n\t\t// services don't send calls back over that same connection. However,\n\t\t// this is what happens in the hyperbahn emulation case, so create\n\t\t// an explicit connection to a new listening channel.\n\t\tlisteningHBSvc := ts.NewServer(nil)\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), time.Second)\n\t\tdefer cancel()\n\n\t\t// Ping to ensure the connection has been added to peers on both sides.\n\t\terr = ts.Relay().Ping(ctx, listeningHBSvc.PeerInfo().HostPort)\n\t\trequire.NoError(t, err, \"Failed to connect from relay to listening host:port\")\n\n\t\t// Now when listeningHBSvc makes a call, it should use the above connection.\n\t\terr = testutils.CallEcho(listeningHBSvc, ts.HostPort(), ts.ServiceName(), nil)\n\t\trequire.Error(t, err, \"Expected CallEcho to fail\")\n\t\tassert.Contains(t, err.Error(), errTest.Error(), \"Unexpected error\")\n\n\t\t// We expect an inbound connection on listeningHBSvc.\n\t\tconnHostPort = getConn(listeningHBSvc, false /* outbound */).LocalHostPort\n\t\twantConn = &relay.Conn{\n\t\t\tRemoteAddr:        connHostPort,\n\t\t\tRemoteProcessName: listeningHBSvc.PeerInfo().ProcessName,\n\t\t\tIsOutbound:        true, // outbound connection according to relay.\n\t\t\tContext:           context.Background(),\n\t\t}\n\t\tassert.Equal(t, wantConn, gotConn, \"Unexpected remote addr\")\n\t})\n}\n\nfunc TestRelayConnectionClosed(t *testing.T) {\n\tprotocolErr := NewSystemError(ErrCodeProtocol, \"invalid service name\")\n\tgetHost := func(relay.CallFrame, *relay.Conn) (string, error) {\n\t\treturn \"\", protocolErr\n\t}\n\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tSetRelayHost(relaytest.HostFunc(getHost))\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t// The client receives a protocol error which causes the following logs.\n\t\topts := testutils.NewOpts().\n\t\t\tAddLogFilter(\"Peer reported protocol error\", 1).\n\t\t\tAddLogFilter(\"Connection error\", 1)\n\t\tclient := ts.NewClient(opts)\n\n\t\terr := testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil)\n\t\tassert.Equal(t, protocolErr, err, \"Unexpected error on call\")\n\n\t\tclosedAll := testutils.WaitFor(time.Second, func() bool {\n\t\t\treturn ts.Relay().IntrospectNumConnections() == 0\n\t\t})\n\t\tassert.True(t, closedAll, \"Relay should close client connection\")\n\t})\n}\n\nfunc TestRelayUsesRootPeers(t *testing.T) {\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\t\tclient := testutils.NewClient(t, nil)\n\t\terr := testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil)\n\t\tassert.NoError(t, err, \"Echo failed\")\n\t\tassert.Len(t, ts.Relay().Peers().Copy(), 0, \"Peers should not be modified by relay\")\n\t})\n}\n\n// Ensure that if the relay recieves a call on a connection that is not active,\n// it declines the call, and increments a relay-client-conn-inactive stat.\nfunc TestRelayRejectsDuringClose(t *testing.T) {\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tAddLogFilter(\"Failed to relay frame.\", 1, \"error\", \"incoming connection is not active: connectionStartClose\")\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tgotCall := make(chan struct{})\n\t\tblock := make(chan struct{})\n\n\t\ttestutils.RegisterEcho(ts.Server(), func() {\n\t\t\tclose(gotCall)\n\t\t\t<-block\n\t\t})\n\n\t\tclient := ts.NewClient(nil)\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\t}()\n\n\t\t<-gotCall\n\t\t// Close the relay so that it stops accepting more calls.\n\t\tts.Relay().Close()\n\t\terr := testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil)\n\t\trequire.Error(t, err, \"Expect call to fail after relay is shutdown\")\n\t\tassert.Contains(t, err.Error(), \"incoming connection is not active\")\n\t\tclose(block)\n\t\twg.Wait()\n\n\t\t// We have a successful call that ran in the goroutine\n\t\t// and a failed call that we just checked the error on.\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(client.PeerInfo().ServiceName, ts.ServiceName(), \"echo\").\n\t\t\tSucceeded().End()\n\t\tcalls.Add(client.PeerInfo().ServiceName, ts.ServiceName(), \"echo\").\n\t\t\t// No peer is set since we rejected the call before selecting one.\n\t\t\tFailed(\"relay-client-conn-inactive\").End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\nfunc TestRelayRateLimitDrop(t *testing.T) {\n\tgetHost := func(relay.CallFrame, *relay.Conn) (string, error) {\n\t\treturn \"\", relay.RateLimitDropError{}\n\t}\n\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tSetRelayHost(relaytest.HostFunc(getHost))\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tvar gotCall bool\n\t\ttestutils.RegisterEcho(ts.Server(), func() {\n\t\t\tgotCall = true\n\t\t})\n\n\t\tclient := ts.NewClient(nil)\n\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\t// We want to use a low timeout here since the test waits for this\n\t\t\t// call to timeout.\n\t\t\tctx, cancel := NewContext(testutils.Timeout(100 * time.Millisecond))\n\t\t\tdefer cancel()\n\t\t\t_, _, _, err := raw.Call(ctx, client, ts.HostPort(), ts.ServiceName(), \"echo\", nil, nil)\n\t\t\trequire.Equal(t, ErrTimeout, err, \"Expected CallEcho to fail\")\n\t\t\tdefer wg.Done()\n\t\t}()\n\n\t\twg.Wait()\n\t\tassert.False(t, gotCall, \"Server should not receive a call\")\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(client.PeerInfo().ServiceName, ts.ServiceName(), \"echo\").\n\t\t\tFailed(\"relay-dropped\").End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\n// Test that a stalled connection to a single server does not block all calls\n// from that server, and we have stats to capture that this is happening.\nfunc TestRelayStalledConnection(t *testing.T) {\n\t// TODO(ablackmon): Debug why this is flaky in github\n\tif os.Getenv(\"GITHUB_WORKFLOW\") != \"\" {\n\t\tt.Skip(\"skipping test flaky in github actions.\")\n\t}\n\n\t// TODO: enable framepool checks\n\topts := testutils.NewOpts().\n\t\tAddLogFilter(\"Dropping call due to slow connection.\", 1, \"sendChCapacity\", \"32\").\n\t\tSetSendBufferSize(32). // We want to hit the buffer size earlier, but also ensure we're only dropping once the sendCh is full.\n\t\tSetServiceName(\"s1\").\n\t\tSetRelayOnly()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ts2 := ts.NewServer(testutils.NewOpts().SetServiceName(\"s2\"))\n\t\ttestutils.RegisterEcho(s2, nil)\n\n\t\tstall := make(chan struct{})\n\t\tstallComplete := make(chan struct{})\n\t\tstallHandler := func(ctx context.Context, call *InboundCall) {\n\t\t\t<-stall\n\t\t\traw.ReadArgs(call)\n\t\t\tclose(stallComplete)\n\t\t}\n\t\tts.Register(HandlerFunc(stallHandler), \"echo\")\n\n\t\tctx, cancel := NewContext(testutils.Timeout(300 * time.Millisecond))\n\t\tdefer cancel()\n\n\t\tclient := ts.NewClient(nil)\n\t\tcall, err := client.BeginCall(ctx, ts.HostPort(), ts.ServiceName(), \"echo\", nil)\n\t\trequire.NoError(t, err, \"BeginCall failed\")\n\t\twriter, err := call.Arg2Writer()\n\t\trequire.NoError(t, err, \"Arg2Writer failed\")\n\t\tgo io.Copy(writer, testreader.Looper([]byte(\"test\")))\n\n\t\t// Try to read the response which might get an error.\n\t\treadDone := make(chan struct{})\n\t\tgo func() {\n\t\t\tdefer close(readDone)\n\n\t\t\t_, err := call.Response().Arg2Reader()\n\t\t\tif assert.Error(t, err, \"Expected error while reading\") {\n\t\t\t\tassert.Contains(t, err.Error(), \"frame was not sent to remote side\")\n\t\t\t}\n\t\t}()\n\n\t\t// Wait for the reader to error out.\n\t\tselect {\n\t\tcase <-time.After(testutils.Timeout(10 * time.Second)):\n\t\t\tt.Fatalf(\"Test timed out waiting for reader to fail\")\n\t\tcase <-readDone:\n\t\t}\n\n\t\t// We should be able to make calls to s2 even if s1 is stalled.\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), \"s2\")\n\n\t\t// Verify the sendCh is full, and the buffers are utilized.\n\t\tstate := ts.Relay().IntrospectState(&IntrospectionOptions{})\n\t\tconnState := state.RootPeers[ts.Server().PeerInfo().HostPort].OutboundConnections[0]\n\t\tassert.Equal(t, 32, connState.SendChCapacity, \"unexpected SendChCapacity\")\n\t\tassert.NotZero(t, connState.SendChQueued, \"unexpected SendChQueued\")\n\t\tassert.NotZero(t, connState.SendBufferUsage, \"unexpected SendBufferUsage\")\n\t\tassert.NotZero(t, connState.SendBufferSize, \"unexpected SendBufferSize\")\n\n\t\t// Cancel the call and unblock the stall handler.\n\t\tcancel()\n\t\tclose(stall)\n\n\t\t// The server channel will not close until the stall handler receives\n\t\t// an error. Since we don't propagate cancels, the handler will keep\n\t\t// trying to read arguments till the timeout.\n\t\tselect {\n\t\tcase <-stallComplete:\n\t\tcase <-time.After(testutils.Timeout(300 * time.Millisecond)):\n\t\t\tt.Fatalf(\"Stall handler did not complete\")\n\t\t}\n\n\t\tcalls := relaytest.NewMockStats()\n\t\tcalls.Add(client.PeerInfo().ServiceName, ts.ServiceName(), \"echo\").\n\t\t\tFailed(\"relay-dest-conn-slow\").End()\n\t\tcalls.Add(client.PeerInfo().ServiceName, \"s2\", \"echo\").\n\t\t\tSucceeded().End()\n\t\tts.AssertRelayStats(calls)\n\t})\n}\n\n// Test that a stalled connection to the client does not cause stuck calls\n// See https://github.com/uber/tchannel-go/issues/700 for more info.\nfunc TestRelayStalledClientConnection(t *testing.T) {\n\t// This needs to be large enough to fill up the client TCP buffer.\n\tconst _calls = 100\n\n\t// TODO: enable framepool checks\n\topts := testutils.NewOpts().\n\t\t// Expect errors from dropped frames.\n\t\tAddLogFilter(\"Dropping call due to slow connection.\", _calls).\n\t\tSetSendBufferSize(10). // We want to hit the buffer size earlier.\n\t\tSetServiceName(\"s1\").\n\t\tSetRelayOnly()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t// Track when the server receives calls\n\t\tgotCall := make(chan struct{}, _calls)\n\t\ttestutils.RegisterEcho(ts.Server(), func() {\n\t\t\tgotCall <- struct{}{}\n\t\t})\n\n\t\t// Create a frame relay that will block all client inbound frames.\n\t\tunblockClientInbound := make(chan struct{})\n\t\tblockerHostPort, relayCancel := testutils.FrameRelay(t, ts.HostPort(), func(outgoing bool, f *Frame) *Frame {\n\t\t\tif !outgoing && f.Header.ID > 1 {\n\t\t\t\t// Block all inbound frames except the initRes\n\t\t\t\t<-unblockClientInbound\n\t\t\t}\n\n\t\t\treturn f\n\t\t})\n\t\tdefer relayCancel()\n\t\tdefer close(unblockClientInbound)\n\n\t\tclient := ts.NewClient(nil)\n\n\t\tctx, cancel := NewContext(testutils.Timeout(time.Second))\n\t\tdefer cancel()\n\n\t\tvar calls []*OutboundCall\n\n\t\t// Data to fit one frame fully, but large enough that a number of these frames will fill\n\t\t// all the buffers and cause the relay to drop the response frame. Buffers are:\n\t\t// 1. Relay's sendCh on the connection to the client (set to 10 frames explicitly)\n\t\t// 2. Relay's TCP send buffer for the connection to the client.\n\t\t// 3. Client's TCP receive buffer on the connection to the relay.\n\t\tdata := bytes.Repeat([]byte(\"test\"), 256*60)\n\t\tfor i := 0; i < _calls; i++ {\n\t\t\tcall, err := client.BeginCall(ctx, blockerHostPort, ts.ServiceName(), \"echo\", nil)\n\t\t\trequire.NoError(t, err, \"BeginCall failed\")\n\n\t\t\trequire.NoError(t, NewArgWriter(call.Arg2Writer()).Write(nil), \"arg2 write failed\")\n\t\t\trequire.NoError(t, NewArgWriter(call.Arg3Writer()).Write(data), \"arg2 write failed\")\n\n\t\t\t// Wait for server to receive the call\n\t\t\t<-gotCall\n\n\t\t\tcalls = append(calls, call)\n\t\t}\n\n\t\t// Wait for all calls to end on the relay, and ensure we got failures from the slow client.\n\t\tstats := ts.RelayHost().Stats()\n\t\tstats.WaitForEnd()\n\t\tassert.Contains(t, stats.Map(), \"testService-client->s1::echo.failed-relay-source-conn-slow\", \"Expect at least 1 failed call due to slow client\")\n\n\t\t// We don't read the responses, as we want the client's TCP buffers to fill up\n\t\t// and the relay to drop calls. However, we should unblock the client reader\n\t\t// to make sure the client channel can close.\n\t\t// Unblock the client so it can close.\n\t\tcancel()\n\t\tfor _, call := range calls {\n\t\t\trequire.Error(t, NewArgReader(call.Response().Arg2Reader()).Read(&data), \"should fail to read response\")\n\t\t}\n\t})\n}\n\n// Test that a corrupted callRes frame results in log emission. We set up the following:\n//\n//\tclient <-> relay <-> man-in-the-middle (MITM) relay <-> server\n//\n// The MITM relay is configured to intercept and corrupt response frames (through truncation)\n// sent back from the server, and forward them back to the relay, where it is checked for errors.\nfunc TestRelayCorruptedCallResFrame(t *testing.T) {\n\t// TODO: Debug why this is flaky in github\n\tif os.Getenv(\"GITHUB_WORKFLOW\") != \"\" {\n\t\tt.Skip(\"skipping test flaky in github actions.\")\n\t}\n\topts := testutils.NewOpts().\n\t\t// Expect errors from corrupted callRes frames.\n\t\tAddLogFilter(\"Malformed callRes frame.\", 1).\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling()\n\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ts1 := testutils.NewServer(t, testutils.NewOpts().SetServiceName(\"s1\"))\n\t\tdefer s1.Close()\n\n\t\t// Track when the server receives the call\n\t\tgotCall := make(chan struct{})\n\t\ttestutils.RegisterFunc(s1, \"echo\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\tgotCall <- struct{}{}\n\t\t\treturn &raw.Res{Arg2: args.Arg2, Arg3: args.Arg3}, nil\n\t\t})\n\n\t\tmitmHostPort, relayCancel := testutils.FrameRelay(t, s1.PeerInfo().HostPort, func(outgoing bool, f *Frame) *Frame {\n\t\t\t// We care only about callRes frames\n\t\t\tif f.Header.MessageType() == 0x04 {\n\t\t\t\t// Corrupt the frame by truncating its payload size to 1 byte\n\t\t\t\tf.Header.SetPayloadSize(1)\n\t\t\t}\n\t\t\treturn f\n\t\t})\n\t\tdefer relayCancel()\n\n\t\t// The relay only forwards requests to the MITM relay\n\t\tts.RelayHost().Add(\"s1\", mitmHostPort)\n\n\t\tclient := ts.NewClient(nil)\n\t\tdefer client.Close()\n\n\t\tctx, cancel := NewContext(testutils.Timeout(time.Second))\n\t\tdefer cancel()\n\n\t\tdata := bytes.Repeat([]byte(\"test\"), 256*60)\n\t\tcall, err := client.BeginCall(ctx, ts.Relay().PeerInfo().HostPort, \"s1\", \"echo\", nil)\n\t\trequire.NoError(t, err, \"BeginCall failed\")\n\n\t\trequire.NoError(t, NewArgWriter(call.Arg2Writer()).Write(nil), \"arg2 write failed\")\n\t\trequire.NoError(t, NewArgWriter(call.Arg3Writer()).Write(data), \"arg2 write failed\")\n\n\t\t// Wait for server to receive the call\n\t\t<-gotCall\n\n\t\t// Unblock the client so it can close.\n\t\tcancel()\n\t\trequire.Error(t, NewArgReader(call.Response().Arg2Reader()).Read(&data), \"should fail to read response\")\n\t})\n}\n\nfunc TestRelayThroughSeparateRelay(t *testing.T) {\n\t// TODO: enable framepool checks\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tserverHP := ts.Server().PeerInfo().HostPort\n\t\tdummyFactory := func(relay.CallFrame, *relay.Conn) (string, error) {\n\t\t\tpanic(\"should not get invoked\")\n\t\t}\n\t\trelay2Opts := testutils.NewOpts().SetRelayHost(relaytest.HostFunc(dummyFactory))\n\t\trelay2 := ts.NewServer(relay2Opts)\n\n\t\t// Override where the peers come from.\n\t\tts.RelayHost().SetChannel(relay2)\n\t\trelay2.GetSubChannel(ts.ServiceName(), Isolated).Peers().Add(serverHP)\n\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\t\tclient := ts.NewClient(nil)\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\n\t\tnumConns := func(p PeerRuntimeState) int {\n\t\t\treturn len(p.InboundConnections) + len(p.OutboundConnections)\n\t\t}\n\n\t\t// Verify that there are no connections from ts.Relay() to the server.\n\t\tintrospected := ts.Relay().IntrospectState(nil)\n\t\tassert.Zero(t, numConns(introspected.RootPeers[serverHP]), \"Expected no connections from relay to server\")\n\n\t\tintrospected = relay2.IntrospectState(nil)\n\t\tassert.Equal(t, 1, numConns(introspected.RootPeers[serverHP]), \"Expected 1 connection from relay2 to server\")\n\t})\n}\n\nfunc TestRelayConcurrentNewConnectionAttempts(t *testing.T) {\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t// Create a server that is slow to accept connections by using\n\t\t// a frame relay to slow down the initial message.\n\t\tslowServer := testutils.NewServer(t, serviceNameOpts(\"slow-server\"))\n\t\tdefer slowServer.Close()\n\t\ttestutils.RegisterEcho(slowServer, nil)\n\n\t\tvar delayed atomic.Bool\n\t\trelayFunc := func(outgoing bool, f *Frame) *Frame {\n\t\t\tif !delayed.Load() {\n\t\t\t\ttime.Sleep(testutils.Timeout(50 * time.Millisecond))\n\t\t\t\tdelayed.Store(true)\n\t\t\t}\n\t\t\treturn f\n\t\t}\n\n\t\tslowHP, close := testutils.FrameRelay(t, slowServer.PeerInfo().HostPort, relayFunc)\n\t\tdefer close()\n\t\tts.RelayHost().Add(\"slow-server\", slowHP)\n\n\t\t// Make concurrent calls to trigger concurrent getConnectionRelay calls.\n\t\tvar wg sync.WaitGroup\n\t\tfor i := 0; i < 5; i++ {\n\t\t\twg.Add(1)\n\t\t\t// Create client and get dest host:port in the main goroutine to avoid races.\n\t\t\tclient := ts.NewClient(nil)\n\t\t\trelayHostPort := ts.HostPort()\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\t\t\t\ttestutils.AssertEcho(t, client, relayHostPort, \"slow-server\")\n\t\t\t}()\n\t\t}\n\t\twg.Wait()\n\n\t\t// Verify that the slow server only received a single connection.\n\t\tinboundConns := 0\n\t\tfor _, state := range slowServer.IntrospectState(nil).RootPeers {\n\t\t\tinboundConns += len(state.InboundConnections)\n\t\t}\n\t\tassert.Equal(t, 1, inboundConns, \"Expected a single inbound connection to the server\")\n\t})\n}\n\nfunc TestRelayRaceTimerCausesStuckConnectionOnClose(t *testing.T) {\n\t// TODO: enable framepool checks\n\t// TODO(ablackmon): Debug why this is flaky in github\n\tif os.Getenv(\"GITHUB_WORKFLOW\") != \"\" {\n\t\tt.Skip(\"skipping test flaky in github actions.\")\n\t}\n\tconst (\n\t\tconcurrentClients = 15\n\t\tcallsPerClient    = 100\n\t)\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetSendBufferSize(concurrentClients * callsPerClient) // Avoid dropped frames causing unexpected logs.\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\t\t// Create clients and ensure we can make a successful request.\n\t\tclients := make([]*Channel, concurrentClients)\n\n\t\tvar callTime time.Duration\n\t\tfor i := range clients {\n\t\t\tclients[i] = ts.NewClient(opts)\n\t\t\tstarted := time.Now()\n\t\t\ttestutils.AssertEcho(t, clients[i], ts.HostPort(), ts.ServiceName())\n\t\t\tcallTime = time.Since(started)\n\t\t}\n\n\t\t// Overwrite the echo method with one that times out for the test.\n\t\tts.Server().Register(HandlerFunc(func(ctx context.Context, call *InboundCall) {\n\t\t\tcall.Response().Blackhole()\n\t\t}), \"echo\")\n\n\t\tvar wg sync.WaitGroup\n\t\tfor i := 0; i < concurrentClients; i++ {\n\t\t\twg.Add(1)\n\t\t\tgo func(client *Channel) {\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\tfor j := 0; j < callsPerClient; j++ {\n\t\t\t\t\t// Make many concurrent calls which, some of which should timeout.\n\t\t\t\t\tctx, cancel := NewContext(callTime)\n\t\t\t\t\traw.Call(ctx, client, ts.HostPort(), ts.ServiceName(), \"echo\", nil, nil)\n\t\t\t\t\tcancel()\n\t\t\t\t}\n\t\t\t}(clients[i])\n\t\t}\n\n\t\twg.Wait()\n\t})\n}\n\nfunc TestRelayRaceCompletionAndTimeout(t *testing.T) {\n\t// TODO: enable framepool checks\n\tconst numCalls = 100\n\n\topts := testutils.NewOpts().\n\t\tAddLogFilter(\"simpleHandler OnError.\", numCalls).\n\t\t// Trigger deletion on timeout, see https://github.com/uber/tchannel-go/issues/808.\n\t\tSetRelayMaxTombs(numCalls/2).\n\t\t// Hitting max tombs will cause the following logs:\n\t\tAddLogFilter(\"Too many tombstones, deleting relay item immediately.\", numCalls).\n\t\tAddLogFilter(\"Received a frame without a RelayItem.\", numCalls).\n\t\tAddLogFilter(\"Attempted to create new mex after mexset shutdown.\", numCalls).\n\t\tSetRelayOnly()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\n\t\tclient := ts.NewClient(nil)\n\t\tstarted := time.Now()\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\tcallTime := time.Since(started)\n\n\t\t// Make many calls with the same timeout, with the goal of\n\t\t// timing out right as we process the response frame.\n\t\tvar wg sync.WaitGroup\n\t\tfor i := 0; i < numCalls; i++ {\n\t\t\twg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\tctx, cancel := NewContext(callTime)\n\t\t\t\traw.Call(ctx, client, ts.HostPort(), ts.ServiceName(), \"echo\", nil, nil)\n\t\t\t\tcancel()\n\t\t\t}()\n\t\t}\n\n\t\t// Some of those calls should triger the race.\n\t\twg.Wait()\n\t})\n}\n\nfunc TestRelayArg2OffsetIntegration(t *testing.T) {\n\tctx, cancel := NewContext(testutils.Timeout(time.Second))\n\tdefer cancel()\n\n\trh := relaytest.NewStubRelayHost()\n\tframeCh := inspectFrames(rh)\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tSetRelayHost(rh)\n\n\ttestutils.WithTestServer(t, opts, func(tb testing.TB, ts *testutils.TestServer) {\n\t\tconst (\n\t\t\ttestMethod = \"echo\"\n\t\t\targ2Data   = \"arg2-is\"\n\t\t\targ3Data   = \"arg3-here\"\n\t\t)\n\n\t\tvar (\n\t\t\twantArg2Start = len(ts.ServiceName()) + len(testMethod) + 70 /*data before arg1*/\n\t\t\tpayloadLeft   = MaxFramePayloadSize - wantArg2Start\n\t\t)\n\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\n\t\tclient := testutils.NewClient(t, nil /*opts*/)\n\t\tdefer client.Close()\n\n\t\ttests := []struct {\n\t\t\tmsg               string\n\t\t\targ2Data          string\n\t\t\targ2Flush         bool\n\t\t\targ2PostFlushData string\n\t\t\tnoArg3            bool\n\t\t\twantEndOffset     int\n\t\t\twantHasMore       bool\n\t\t}{\n\t\t\t{\n\t\t\t\tmsg:           \"all within a frame\",\n\t\t\t\targ2Data:      arg2Data,\n\t\t\t\twantEndOffset: wantArg2Start + len(arg2Data),\n\t\t\t\twantHasMore:   false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:           \"arg2 flushed\",\n\t\t\t\targ2Data:      arg2Data,\n\t\t\t\targ2Flush:     true,\n\t\t\t\twantEndOffset: wantArg2Start + len(arg2Data),\n\t\t\t\twantHasMore:   true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:               \"arg2 flushed called then write again\",\n\t\t\t\targ2Data:          arg2Data,\n\t\t\t\targ2Flush:         true,\n\t\t\t\targ2PostFlushData: \"more data\",\n\t\t\t\twantEndOffset:     wantArg2Start + len(arg2Data),\n\t\t\t\twantHasMore:       true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:           \"no arg2 but flushed\",\n\t\t\t\twantEndOffset: wantArg2Start,\n\t\t\t\twantHasMore:   false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:           \"XL arg2 which is fragmented\",\n\t\t\t\targ2Data:      string(make([]byte, MaxFrameSize+100)),\n\t\t\t\twantEndOffset: wantArg2Start + payloadLeft,\n\t\t\t\twantHasMore:   true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:           \"large arg2 with 3 bytes left for arg3\",\n\t\t\t\targ2Data:      string(make([]byte, payloadLeft-3)),\n\t\t\t\twantEndOffset: wantArg2Start + payloadLeft - 3,\n\t\t\t\twantHasMore:   false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:           \"large arg2, 2 bytes left\",\n\t\t\t\targ2Data:      string(make([]byte, payloadLeft-2)),\n\t\t\t\twantEndOffset: wantArg2Start + payloadLeft - 2,\n\t\t\t\twantHasMore:   true, // no arg3\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:           \"large arg2, 2 bytes left, no arg3\",\n\t\t\t\targ2Data:      string(make([]byte, payloadLeft-2)),\n\t\t\t\twantEndOffset: wantArg2Start + payloadLeft - 2,\n\t\t\t\tnoArg3:        true,\n\t\t\t\twantHasMore:   true, // no arg3 and still got CALL_REQ_CONTINUE\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:           \"large arg2, 1 bytes left\",\n\t\t\t\targ2Data:      string(make([]byte, payloadLeft-1)),\n\t\t\t\twantEndOffset: wantArg2Start + payloadLeft - 1,\n\t\t\t\twantHasMore:   true, // no arg3\n\t\t\t},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\t\tcall, err := client.BeginCall(ctx, ts.HostPort(), ts.ServiceName(), testMethod, nil)\n\t\t\t\trequire.NoError(t, err, \"BeginCall failed\")\n\t\t\t\twriter, err := call.Arg2Writer()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\t_, err = writer.Write([]byte(tt.arg2Data))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tif tt.arg2Flush {\n\t\t\t\t\twriter.Flush()\n\t\t\t\t\t// tries to write after flush\n\t\t\t\t\tif tt.arg2PostFlushData != \"\" {\n\t\t\t\t\t\t_, err := writer.Write([]byte(tt.arg2PostFlushData))\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, writer.Close())\n\n\t\t\t\targ3DataToWrite := arg3Data\n\t\t\t\tif tt.noArg3 {\n\t\t\t\t\targ3DataToWrite = \"\"\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, NewArgWriter(call.Arg3Writer()).Write([]byte(arg3DataToWrite)), \"arg3 write failed\")\n\n\t\t\t\tf := <-frameCh\n\t\t\t\tstart := f.Arg2StartOffset()\n\t\t\t\tend, hasMore := f.Arg2EndOffset()\n\t\t\t\tassert.Equal(t, wantArg2Start, start, \"arg2 start offset does not match expectation\")\n\t\t\t\tassert.Equal(t, tt.wantEndOffset, end, \"arg2 end offset does not match expectation\")\n\t\t\t\tassert.Equal(t, tt.wantHasMore, hasMore, \"arg2 hasMore bit does not match expectation\")\n\n\t\t\t\tgotArg2, gotArg3, err := raw.ReadArgsV2(call.Response())\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tassert.Equal(t, tt.arg2Data+tt.arg2PostFlushData, string(gotArg2), \"arg2 in response does not meet expectation\")\n\t\t\t\tassert.Equal(t, arg3DataToWrite, string(gotArg3), \"arg3 in response does not meet expectation\")\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestRelayThriftArg2KeyValueIteration(t *testing.T) {\n\tctx, cancel := NewContext(testutils.Timeout(time.Second))\n\tdefer cancel()\n\n\trh := relaytest.NewStubRelayHost()\n\tframeCh := inspectFrames(rh)\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tSetRelayHost(rh)\n\n\ttestutils.WithTestServer(t, opts, func(tb testing.TB, ts *testutils.TestServer) {\n\t\tkv := map[string]string{\n\t\t\t\"key\":     \"val\",\n\t\t\t\"key2\":    \"valval\",\n\t\t\t\"longkey\": \"valvalvalval\",\n\t\t}\n\t\targ2Buf := thriftarg2test.BuildKVBuffer(kv)\n\n\t\tconst (\n\t\t\ttestMethod = \"echo\"\n\t\t\targ3Data   = \"arg3-here\"\n\t\t)\n\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\n\t\tclient := testutils.NewClient(t, nil /*opts*/)\n\t\tdefer client.Close()\n\n\t\tcall, err := client.BeginCall(ctx, ts.HostPort(), ts.ServiceName(), testMethod, &CallOptions{Format: Thrift})\n\t\trequire.NoError(t, err, \"BeginCall failed\")\n\t\trequire.NoError(t, NewArgWriter(call.Arg2Writer()).Write(arg2Buf), \"arg2 write failed\")\n\t\trequire.NoError(t, NewArgWriter(call.Arg3Writer()).Write([]byte(arg3Data)), \"arg3 write failed\")\n\n\t\tf := <-frameCh\n\t\titer, err := f.Arg2Iterator()\n\t\tgotKV := make(map[string]string)\n\t\tfor err == nil {\n\t\t\tgotKV[string(iter.Key())] = string(iter.Value())\n\t\t\titer, err = iter.Next()\n\t\t}\n\t\tassert.Equal(t, kv, gotKV)\n\t\tassert.Equal(t, io.EOF, err)\n\n\t\tgotArg2, gotArg3, err := raw.ReadArgsV2(call.Response())\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, string(arg2Buf), string(gotArg2), \"arg2 in response does not meet expectation\")\n\t\tassert.Equal(t, arg3Data, string(gotArg3), \"arg3 in response does not meet expectation\")\n\t})\n}\n\nfunc TestRelayConnectionTimeout(t *testing.T) {\n\tvar (\n\t\tminTimeout = testutils.Timeout(100 * time.Millisecond)\n\t\tmaxTimeout = testutils.Timeout(time.Minute)\n\t)\n\ttests := []struct {\n\t\tmsg            string\n\t\tcallTimeout    time.Duration\n\t\tmaxConnTimeout time.Duration\n\t\tminTime        time.Duration\n\t}{\n\t\t{\n\t\t\tmsg:         \"only call timeout is set\",\n\t\t\tcallTimeout: 2 * minTimeout,\n\t\t},\n\t\t{\n\t\t\tmsg:            \"call timeout < relay timeout\",\n\t\t\tcallTimeout:    2 * minTimeout,\n\t\t\tmaxConnTimeout: 2 * maxTimeout,\n\t\t},\n\t\t{\n\t\t\tmsg:            \"relay timeout < call timeout\",\n\t\t\tcallTimeout:    2 * maxTimeout,\n\t\t\tmaxConnTimeout: 2 * minTimeout,\n\t\t},\n\t\t{\n\t\t\tmsg:            \"relay timeout == call timeout\",\n\t\t\tcallTimeout:    2 * minTimeout,\n\t\t\tmaxConnTimeout: 2 * minTimeout,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\topts := testutils.NewOpts().\n\t\t\t\tSetRelayOnly().\n\t\t\t\tSetCheckFramePooling().\n\t\t\t\tSetRelayMaxConnectionTimeout(tt.maxConnTimeout).\n\t\t\t\tAddLogFilter(\"Failed during connection handshake.\", 1).\n\t\t\t\tAddLogFilter(\"Failed to connect to relay host.\", 1)\n\t\t\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\t\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\t\t\t\trequire.NoError(t, err, \"Failed to listen\")\n\t\t\t\tdefer ln.Close()\n\n\t\t\t\t// TCP listener will never complete the handshake and always timeout.\n\t\t\t\tts.RelayHost().Add(\"blocked\", ln.Addr().String())\n\n\t\t\t\tstart := time.Now()\n\n\t\t\t\tctx, cancel := NewContext(testutils.Timeout(tt.callTimeout))\n\t\t\t\tdefer cancel()\n\n\t\t\t\t// We expect connection error logs from the client.\n\t\t\t\tclient := ts.NewClient(nil /* opts */)\n\t\t\t\t_, _, _, err = raw.Call(ctx, client, ts.HostPort(), \"blocked\", \"echo\", nil, nil)\n\t\t\t\tassert.Equal(t, ErrTimeout, err)\n\n\t\t\t\ttaken := time.Since(start)\n\t\t\t\tif taken < minTimeout || taken > maxTimeout {\n\t\t\t\t\tt.Errorf(\"Took %v, expected [%v, %v]\", taken, minTimeout, maxTimeout)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestRelayTransferredBytes(t *testing.T) {\n\tconst (\n\t\tkb = 1024\n\n\t\t// The maximum delta between the payload size and the bytes on wire.\n\t\tprotocolBuffer = kb\n\t)\n\n\trh := relaytest.NewStubRelayHost()\n\topts := testutils.NewOpts().\n\t\tSetRelayHost(rh).\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling()\n\n\ttestutils.WithTestServer(t, opts, func(tb testing.TB, ts *testutils.TestServer) {\n\t\t// Note: Upcast to testing.T so we can use t.Run.\n\t\tt := tb.(*testing.T)\n\n\t\ts1 := ts.NewServer(testutils.NewOpts().SetServiceName(\"s1\"))\n\t\ts2 := ts.NewServer(testutils.NewOpts().SetServiceName(\"s2\"))\n\t\ttestutils.RegisterEcho(s1, nil)\n\t\ttestutils.RegisterEcho(s2, nil)\n\n\t\t// Add a handler that always returns an empty payload.\n\t\ttestutils.RegisterFunc(s2, \"swallow\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\tfmt.Println(\"swallow got\", len(args.Arg2)+len(args.Arg3))\n\t\t\treturn &raw.Res{}, nil\n\t\t})\n\n\t\t// Helper to make calls with specific payload sizes.\n\t\tmakeCall := func(src, dst *Channel, method string, arg2Size, arg3Size int) {\n\t\t\tctx, cancel := NewContext(testutils.Timeout(time.Second))\n\t\t\tdefer cancel()\n\n\t\t\targ2 := testutils.RandBytes(arg2Size)\n\t\t\targ3 := testutils.RandBytes(arg3Size)\n\n\t\t\t_, _, _, err := raw.Call(ctx, src, ts.HostPort(), dst.ServiceName(), method, arg2, arg3)\n\t\t\trequire.NoError(t, err)\n\t\t}\n\n\t\tt.Run(\"verify sent vs received\", func(t *testing.T) {\n\t\t\tmakeCall(s1, s2, \"swallow\", 4*1024, 4*1024)\n\n\t\t\tstatsMap := rh.Stats().Map()\n\t\t\tassert.InDelta(t, 8*kb, statsMap[\"s1->s2::swallow.sent-bytes\"], protocolBuffer, \"Unexpected sent bytes\")\n\t\t\tassert.InDelta(t, 0, statsMap[\"s1->s2::swallow.received-bytes\"], protocolBuffer, \"Unexpected sent bytes\")\n\t\t})\n\n\t\tt.Run(\"verify sent and received\", func(t *testing.T) {\n\t\t\tmakeCall(s1, s2, \"echo\", 4*kb, 4*kb)\n\n\t\t\tstatsMap := rh.Stats().Map()\n\t\t\tassert.InDelta(t, 8*kb, statsMap[\"s1->s2::echo.sent-bytes\"], protocolBuffer, \"Unexpected sent bytes\")\n\t\t\tassert.InDelta(t, 8*kb, statsMap[\"s1->s2::echo.received-bytes\"], protocolBuffer, \"Unexpected sent bytes\")\n\t\t})\n\n\t\tt.Run(\"verify large payload\", func(t *testing.T) {\n\t\t\tmakeCall(s1, s2, \"echo\", 128*1024, 128*1024)\n\n\t\t\tstatsMap := rh.Stats().Map()\n\t\t\tassert.InDelta(t, 256*kb, statsMap[\"s1->s2::echo.sent-bytes\"], protocolBuffer, \"Unexpected sent bytes\")\n\t\t\tassert.InDelta(t, 256*kb, statsMap[\"s1->s2::echo.received-bytes\"], protocolBuffer, \"Unexpected sent bytes\")\n\t\t})\n\n\t\tt.Run(\"verify reverse call\", func(t *testing.T) {\n\t\t\tmakeCall(s2, s1, \"echo\", 0, 64*kb)\n\n\t\t\tstatsMap := rh.Stats().Map()\n\t\t\tassert.InDelta(t, 64*kb, statsMap[\"s2->s1::echo.sent-bytes\"], protocolBuffer, \"Unexpected sent bytes\")\n\t\t\tassert.InDelta(t, 64*kb, statsMap[\"s2->s1::echo.received-bytes\"], protocolBuffer, \"Unexpected sent bytes\")\n\t\t})\n\t})\n}\n\nfunc TestRelayCallResponse(t *testing.T) {\n\tctx, cancel := NewContext(testutils.Timeout(time.Second))\n\tdefer cancel()\n\n\tkv := map[string]string{\n\t\t\"foo\": \"bar\",\n\t\t\"baz\": \"qux\",\n\t}\n\targ2Buf := thriftarg2test.BuildKVBuffer(kv)\n\n\trh := relaytest.NewStubRelayHost()\n\n\trh.SetRespFrameFn(func(frame relay.RespFrame) {\n\t\trequire.True(t, frame.OK(), \"Got unexpected response status\")\n\t\trequire.Equal(t, Thrift.String(), string(frame.ArgScheme()), \"Got unexpected scheme\")\n\n\t\titer, err := arg2.NewKeyValIterator(frame.Arg2())\n\t\trequire.NoError(t, err, \"Got unexpected iterator error\")\n\n\t\tgotKV := make(map[string]string)\n\t\tfor ; err == nil; iter, err = iter.Next() {\n\t\t\tgotKV[string(iter.Key())] = string(iter.Value())\n\t\t}\n\n\t\tassert.Equal(t, kv, gotKV, \"Got unexpected arg2 in response\")\n\t})\n\n\topts := testutils.NewOpts().\n\t\tSetRelayOnly().\n\t\tSetCheckFramePooling().\n\t\tSetRelayHost(rh)\n\n\ttestutils.WithTestServer(t, opts, func(tb testing.TB, ts *testutils.TestServer) {\n\t\tconst (\n\t\t\ttestMethod = \"echo\"\n\t\t\targ3Data   = \"arg3-here\"\n\t\t)\n\n\t\ttestutils.RegisterEcho(ts.Server(), nil)\n\n\t\tclient := testutils.NewClient(t, nil /*opts*/)\n\t\tdefer client.Close()\n\n\t\tcall, err := client.BeginCall(ctx, ts.HostPort(), ts.ServiceName(), testMethod, &CallOptions{Format: Thrift})\n\t\trequire.NoError(t, err, \"BeginCall failed\")\n\t\trequire.NoError(t, NewArgWriter(call.Arg2Writer()).Write(arg2Buf), \"arg2 write failed\")\n\t\trequire.NoError(t, NewArgWriter(call.Arg3Writer()).Write([]byte(arg3Data)), \"arg3 write failed\")\n\n\t\tgotArg2, gotArg3, err := raw.ReadArgsV2(call.Response())\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, string(arg2Buf), string(gotArg2), \"arg2 in response does not meet expectation\")\n\t\tassert.Equal(t, arg3Data, string(gotArg3), \"arg3 in response does not meet expectation\")\n\t})\n}\n\nfunc TestRelayAppendArg2SentBytes(t *testing.T) {\n\ttests := []struct {\n\t\tmsg           string\n\t\tappends       map[string]string\n\t\targ3          []byte\n\t\twantSentBytes int\n\t}{\n\t\t{\n\t\t\tmsg:           \"without appends\",\n\t\t\targ3:          []byte(\"hello, world\"),\n\t\t\twantSentBytes: 130,\n\t\t},\n\t\t{\n\t\t\tmsg:           \"with appends\",\n\t\t\targ3:          []byte(\"hello, world\"),\n\t\t\tappends:       map[string]string{\"baz\": \"qux\"},\n\t\t\twantSentBytes: 140, // 130 + 2 bytes size + 3 bytes key + 2 byts size + 3 bytes val = 137\n\t\t},\n\t\t{\n\t\t\tmsg:  \"with large appends that result in fragments\",\n\t\t\targ3: []byte(\"hello, world\"),\n\t\t\tappends: map[string]string{\n\t\t\t\t\"fee\": testutils.RandString(16 * 1024),\n\t\t\t\t\"fii\": testutils.RandString(16 * 1024),\n\t\t\t\t\"foo\": testutils.RandString(16 * 1024),\n\t\t\t\t\"fum\": testutils.RandString(16 * 1024),\n\t\t\t},\n\t\t\t// original data size = 130\n\t\t\t// appended arg2 size = 2 bytes number of keys + 4 * (2 bytes key size + 3 bytes key + 2 bytes val size + 16 * 1024 bytes val)\n\t\t\t// additional frame preamble = 16 bytes header + 1 byte flag + 1 byte checksum type + 4 bytes checksum size + 2 bytes size of remaining arg2\n\t\t\twantSentBytes: 130 + (2+3+2+16*1024)*4 + 16 + 1 + 1 + 4 + 2,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\trh := relaytest.NewStubRelayHost()\n\t\t\trh.SetFrameFn(func(f relay.CallFrame, conn *relay.Conn) {\n\t\t\t\tfor k, v := range tt.appends {\n\t\t\t\t\tf.Arg2Append([]byte(k), []byte(v))\n\t\t\t\t}\n\t\t\t})\n\n\t\t\topts := testutils.NewOpts().\n\t\t\t\tSetRelayOnly().\n\t\t\t\tSetCheckFramePooling().\n\t\t\t\tSetRelayHost(rh)\n\t\t\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\t\trly := ts.Relay()\n\t\t\t\tsvr := ts.Server()\n\t\t\t\ttestutils.RegisterEcho(svr, nil)\n\n\t\t\t\tclient := testutils.NewClient(t, nil)\n\t\t\t\tctx, cancel := NewContextBuilder(testutils.Timeout(time.Second)).\n\t\t\t\t\tSetFormat(Thrift).Build()\n\t\t\t\tdefer cancel()\n\n\t\t\t\tsendArgs := &raw.Args{\n\t\t\t\t\tArg2: thriftarg2test.BuildKVBuffer(map[string]string{\"foo\": \"bar\"}),\n\t\t\t\t\tArg3: tt.arg3,\n\t\t\t\t}\n\n\t\t\t\trecvArg2, recvArg3, _, err := raw.Call(ctx, client, rly.PeerInfo().HostPort, ts.ServiceName(), \"echo\", sendArgs.Arg2, sendArgs.Arg3)\n\t\t\t\trequire.NoError(t, err, \"Call from %v (%v) to %v (%v) failed\", client.ServiceName(), client.PeerInfo().HostPort, ts.ServiceName(), rly.PeerInfo().HostPort)\n\n\t\t\t\twantArg2 := map[string]string{\n\t\t\t\t\t\"foo\": \"bar\",\n\t\t\t\t}\n\t\t\t\tfor k, v := range tt.appends {\n\t\t\t\t\twantArg2[k] = v\n\t\t\t\t}\n\n\t\t\t\tassert.Equal(t, wantArg2, thriftarg2test.MustReadKVBuffer(t, recvArg2), \"Arg2 mismatch\")\n\t\t\t\tassert.Equal(t, recvArg3, []byte(\"hello, world\"), \"Arg3 mismatch\")\n\n\t\t\t\tsentBytes := rh.Stats().Map()[\"testService-client->testService::echo.sent-bytes\"]\n\t\t\t\tassert.Equal(t, tt.wantSentBytes, sentBytes)\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc inspectFrames(rh *relaytest.StubRelayHost) chan relay.CallFrame {\n\tframeCh := make(chan relay.CallFrame, 1)\n\trh.SetFrameFn(func(f relay.CallFrame, _ *relay.Conn) {\n\t\tframeCh <- testutils.CopyCallFrame(f)\n\t})\n\treturn frameCh\n}\n\ntype relayModifier interface {\n\tframeFn(cf relay.CallFrame, _ *relay.Conn)\n\tmodifyArg2(m map[string]string) map[string]string\n}\n\ntype noopRelayModifer struct{}\n\nfunc (nrm *noopRelayModifer) frameFn(_ relay.CallFrame, _ *relay.Conn) {}\n\nfunc (nrm *noopRelayModifer) modifyArg2(m map[string]string) map[string]string { return m }\n\ntype keyVal struct {\n\tkey, val string\n}\n\ntype arg2KeyValRelayModifier struct {\n\tkeyValPairs []keyVal\n}\n\nfunc addFixedKeyVal(kvPairs []keyVal) *arg2KeyValRelayModifier {\n\treturn &arg2KeyValRelayModifier{\n\t\tkeyValPairs: kvPairs,\n\t}\n}\n\nfunc fillFrameWithArg2(t *testing.T, checksumType ChecksumType, arg1 string, arg2 map[string]string, bytePosFromBoundary int) *arg2KeyValRelayModifier {\n\targ2Key := \"foo\"\n\targ2Len := 2 // nh\n\tfor k, v := range arg2 {\n\t\targ2Len += 2 + len(k) + 2 + len(v)\n\t}\n\n\t// Writing an arg adds nh+nk+len(key)+nv+len(val) bytes. calculate the size of val\n\t// so that we end at bytePosFromBoundary in the frame. remainingSpaceBeforeChecksum\n\t// is the number of bytes from the start of the frame up until the checkumType byte,\n\t// just before the checksum itself.\n\tconst remainingSpaceBeforeChecksum = 65441\n\tvalSize := remainingSpaceBeforeChecksum + bytePosFromBoundary - (checksumType.ChecksumSize() + 2 /* nArg1 */ + len(arg1) + arg2Len + 2 /* nk */ + len(arg2Key) + 2 /* nv */)\n\tif valSize < 0 {\n\t\tt.Fatalf(\"can't fill arg2 with key %q and %d bytes remaining\", arg2Key, bytePosFromBoundary)\n\t}\n\n\treturn &arg2KeyValRelayModifier{\n\t\tkeyValPairs: []keyVal{\n\t\t\t{key: arg2Key, val: testutils.RandString(valSize)},\n\t\t},\n\t}\n}\n\nfunc (rm *arg2KeyValRelayModifier) frameFn(cf relay.CallFrame, _ *relay.Conn) {\n\tfor _, kv := range rm.keyValPairs {\n\t\tcf.Arg2Append([]byte(kv.key), []byte(kv.val))\n\t}\n}\n\nfunc (rm *arg2KeyValRelayModifier) modifyArg2(m map[string]string) map[string]string {\n\tif m == nil {\n\t\tm = make(map[string]string)\n\t}\n\tfor _, kv := range rm.keyValPairs {\n\t\tm[kv.key] = kv.val\n\t}\n\treturn m\n}\n\nfunc TestRelayModifyArg2(t *testing.T) {\n\tconst kb = 1024\n\n\tchecksumTypes := []struct {\n\t\tmsg          string\n\t\tchecksumType ChecksumType\n\t}{\n\t\t{\"none\", ChecksumTypeNone},\n\t\t{\"crc32\", ChecksumTypeCrc32},\n\t\t{\"farmhash\", ChecksumTypeFarmhash},\n\t\t{\"crc32c\", ChecksumTypeCrc32C},\n\t}\n\n\tmodifyTests := []struct {\n\t\tmsg      string\n\t\tskip     string\n\t\tmodifier func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier\n\t}{\n\t\t{\n\t\t\tmsg: \"no change\",\n\t\t\tmodifier: func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier {\n\t\t\t\treturn &noopRelayModifer{}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"add zero-length key/value\",\n\t\t\tmodifier: func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier {\n\t\t\t\treturn addFixedKeyVal([]keyVal{{key: \"\", val: \"\"}})\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"add multiple zero-length key/value\",\n\t\t\tmodifier: func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier {\n\t\t\t\treturn addFixedKeyVal([]keyVal{\n\t\t\t\t\t{\"\", \"\"},\n\t\t\t\t\t{\"\", \"\"},\n\t\t\t\t\t{\"\", \"\"},\n\t\t\t\t})\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"add small key/value\",\n\t\t\tmodifier: func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier {\n\t\t\t\treturn addFixedKeyVal([]keyVal{\n\t\t\t\t\t{\"foo\", \"bar\"},\n\t\t\t\t\t{\"baz\", \"qux\"},\n\t\t\t\t})\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"fill the first frame until 2 bytes remain\",\n\t\t\tmodifier: func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier {\n\t\t\t\treturn fillFrameWithArg2(t, cst, arg1, arg2, -2)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"fill the first frame until 1 byte remain\",\n\t\t\tmodifier: func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier {\n\t\t\t\treturn fillFrameWithArg2(t, cst, arg1, arg2, -1)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"fill the first frame to its boundary\",\n\t\t\tmodifier: func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier {\n\t\t\t\treturn fillFrameWithArg2(t, cst, arg1, arg2, 0)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"fill the first frame to 1 byte over its boundary\",\n\t\t\tmodifier: func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier {\n\t\t\t\treturn fillFrameWithArg2(t, cst, arg1, arg2, 1)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"fill the first frame to 2 bytes over its boundary\",\n\t\t\tmodifier: func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier {\n\t\t\t\treturn fillFrameWithArg2(t, cst, arg1, arg2, 2)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"add large key/value which pushes arg2 into 2nd frame\",\n\t\t\tmodifier: func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier {\n\t\t\t\treturn addFixedKeyVal([]keyVal{\n\t\t\t\t\t{\"fee\", testutils.RandString(65535)},\n\t\t\t\t})\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"add large key/value which pushes arg2 into 2nd and 3rd frame\",\n\t\t\tmodifier: func(t *testing.T, cst ChecksumType, arg1 string, arg2 map[string]string) relayModifier {\n\t\t\t\treturn addFixedKeyVal([]keyVal{\n\t\t\t\t\t{\"fee\", testutils.RandString(65535)},\n\t\t\t\t\t{\"fi\", testutils.RandString(65535)},\n\t\t\t\t})\n\t\t\t},\n\t\t},\n\t}\n\n\t// TODO(cinchurge): we need to cover a combination of the following for the payloads:\n\t//   - no arg2, small arg2, large arg2 (3 or 4 cases that are close/on the boundary)\n\t//   - no arg3, small arg3, 16kb arg3, 32kb arg3, 64kb arg3, 128kb arg3, 1mb arg3\n\t//   - 2 bytes, 1 byte, and 0 bytes from the frame boundary for both arg2 and arg3\n\tpayloadTests := []struct {\n\t\tmsg  string\n\t\targ2 map[string]string\n\t\targ3 []byte\n\t}{\n\t\t{\n\t\t\tmsg:  \"no payload\",\n\t\t\targ2: nil, // empty map\n\t\t\targ3: []byte{},\n\t\t},\n\t\t{\n\t\t\t// TODO(cinchurge): ideally we'd like to do tests where arg2 is close to and on the\n\t\t\t// frame boundary, however since the corresponding arg2 size depends on the sizes of arg1\n\t\t\t// and the checksum, we're deferring this to a separate change.\n\t\t\tmsg: \"no payload + large arg2\",\n\t\t\targ2: map[string]string{\n\t\t\t\t\"foo\": testutils.RandString(60000),\n\t\t\t}, // empty map\n\t\t\targ3: []byte{},\n\t\t},\n\t\t{\n\t\t\tmsg: \"1kB payloads\",\n\t\t\targ2: map[string]string{\n\t\t\t\t\"existingKey\": \"existingValue\",\n\t\t\t},\n\t\t\targ3: testutils.RandBytes(kb),\n\t\t},\n\t\t{\n\t\t\tmsg: \"16kB payloads\",\n\t\t\targ2: map[string]string{\n\t\t\t\t\"existingKey\": \"existingValue\",\n\t\t\t},\n\t\t\targ3: testutils.RandBytes(16 * kb),\n\t\t},\n\t\t{\n\t\t\tmsg: \"32kB payloads\",\n\t\t\targ2: map[string]string{\n\t\t\t\t\"existingKey\": \"existingValue\",\n\t\t\t},\n\t\t\targ3: testutils.RandBytes(32 * kb),\n\t\t},\n\t\t{\n\t\t\tmsg: \"64kB payloads\",\n\t\t\targ2: map[string]string{\n\t\t\t\t\"existingKey\": \"existingValue\",\n\t\t\t},\n\t\t\targ3: testutils.RandBytes(64 * kb),\n\t\t},\n\t\t{\n\t\t\tmsg: \"128kB payloads\",\n\t\t\targ2: map[string]string{\n\t\t\t\t\"existingKey\": \"existingValue\",\n\t\t\t},\n\t\t\targ3: testutils.RandBytes(128 * kb),\n\t\t},\n\t\t{\n\t\t\tmsg: \"1MB payloads\",\n\t\t\targ2: map[string]string{\n\t\t\t\t\"existingKey\": \"existingValue\",\n\t\t\t},\n\t\t\targ3: testutils.RandBytes(1024 * kb),\n\t\t},\n\t}\n\n\tconst (\n\t\tformat      = Thrift\n\t\tnoErrMethod = \"EchoVerifyNoErr\"\n\t\terrMethod   = \"EchoVerifyErr\"\n\t)\n\n\tappErrTests := []struct {\n\t\tmsg        string\n\t\tmethod     string\n\t\twantAppErr bool\n\t}{\n\t\t{\n\t\t\tmsg:        \"no app error bit\",\n\t\t\tmethod:     noErrMethod,\n\t\t\twantAppErr: false,\n\t\t},\n\t\t{\n\t\t\tmsg:        \"app error bit\",\n\t\t\tmethod:     errMethod,\n\t\t\twantAppErr: true,\n\t\t},\n\t}\n\n\tfor _, mt := range modifyTests {\n\t\tfor _, csTest := range checksumTypes {\n\t\t\t// Make calls with different payloads and expected errors.\n\t\t\tfor _, aet := range appErrTests {\n\t\t\t\tfor _, tt := range payloadTests {\n\t\t\t\t\tt.Run(fmt.Sprintf(\"%s,checksum=%s,%s,%s\", mt.msg, csTest.msg, aet.msg, tt.msg), func(t *testing.T) {\n\t\t\t\t\t\tmodifier := mt.modifier(t, csTest.checksumType, aet.method, tt.arg2)\n\n\t\t\t\t\t\t// Create a relay that will modify the frame as per the test.\n\t\t\t\t\t\trelayHost := relaytest.NewStubRelayHost()\n\t\t\t\t\t\trelayHost.SetFrameFn(modifier.frameFn)\n\t\t\t\t\t\topts := testutils.NewOpts().\n\t\t\t\t\t\t\tSetRelayHost(relayHost).\n\t\t\t\t\t\t\tSetRelayOnly().\n\t\t\t\t\t\t\tSetCheckFramePooling()\n\t\t\t\t\t\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\t\t\t\t\t// Create a client that uses a specific checksumType.\n\t\t\t\t\t\t\tclientOpts := testutils.NewOpts().SetChecksumType(csTest.checksumType)\n\t\t\t\t\t\t\tclient := ts.NewClient(clientOpts)\n\t\t\t\t\t\t\tdefer client.Close()\n\n\t\t\t\t\t\t\t// Create a server echo verify endpoints (optionally returning an error).\n\t\t\t\t\t\t\tfor _, appErrTest := range appErrTests {\n\t\t\t\t\t\t\t\thandler := echoVerifyHandler{\n\t\t\t\t\t\t\t\t\tt:            t,\n\t\t\t\t\t\t\t\t\tverifyFormat: format,\n\t\t\t\t\t\t\t\t\tverifyCaller: client.ServiceName(),\n\t\t\t\t\t\t\t\t\tverifyMethod: appErrTest.method,\n\t\t\t\t\t\t\t\t\tappErr:       appErrTest.wantAppErr,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tts.Server().Register(raw.Wrap(handler), appErrTest.method)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tctx, cancel := NewContextBuilder(testutils.Timeout(time.Second)).\n\t\t\t\t\t\t\t\tSetFormat(format).Build()\n\t\t\t\t\t\t\tdefer cancel()\n\n\t\t\t\t\t\t\targ2Encoded := encodeThriftHeaders(t, tt.arg2)\n\n\t\t\t\t\t\t\tresArg2, resArg3, resp, err := raw.Call(ctx, client, ts.HostPort(), ts.ServiceName(), aet.method, arg2Encoded, tt.arg3)\n\t\t\t\t\t\t\trequire.NoError(t, err, \"%v: Received unexpected error\", tt.msg)\n\t\t\t\t\t\t\tassert.Equal(t, format, resp.Format(), \"%v: Unexpected error format\")\n\t\t\t\t\t\t\tassert.Equal(t, aet.wantAppErr, resp.ApplicationError(), \"%v: Unexpected app error\")\n\n\t\t\t\t\t\t\twantArg2 := modifier.modifyArg2(copyHeaders(tt.arg2))\n\n\t\t\t\t\t\t\tgotArg2Map := decodeThriftHeaders(t, resArg2)\n\t\t\t\t\t\t\tassert.Equal(t, wantArg2, gotArg2Map, \"%v: Unexpected arg2 headers\", tt.msg)\n\t\t\t\t\t\t\tassert.Equal(t, resArg3, tt.arg3, \"%v: Unexpected arg3\", tt.msg)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestRelayModifyArg2ShouldFail(t *testing.T) {\n\ttests := []struct {\n\t\tmsg     string\n\t\targ2    []byte\n\t\tformat  Format\n\t\twantErr string\n\t}{\n\t\t{\n\t\t\tmsg: \"large arg2, fragmented\",\n\t\t\targ2: thriftarg2test.BuildKVBuffer(map[string]string{\n\t\t\t\t\"fee\": testutils.RandString(16 * 1024),\n\t\t\t\t\"fi\":  testutils.RandString(16 * 1024),\n\t\t\t\t\"fo\":  testutils.RandString(16 * 1024),\n\t\t\t\t\"fum\": testutils.RandString(16 * 1024),\n\t\t\t}),\n\t\t\twantErr: \"relay-arg2-modify-failed: fragmented arg2\",\n\t\t},\n\t\t{\n\t\t\tmsg:    \"non-Thrift call\",\n\t\t\tformat: JSON,\n\t\t\targ2: thriftarg2test.BuildKVBuffer(map[string]string{\n\t\t\t\t\"fee\": testutils.RandString(16 * 1024),\n\t\t\t}),\n\t\t\twantErr: \"relay-arg2-modify-failed: cannot inspect or modify arg2 for non-Thrift calls\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\trh := relaytest.NewStubRelayHost()\n\t\t\trh.SetFrameFn(func(f relay.CallFrame, conn *relay.Conn) {\n\t\t\t\tf.Arg2Append([]byte(\"foo\"), []byte(\"bar\"))\n\t\t\t})\n\t\t\topts := testutils.NewOpts().\n\t\t\t\tSetRelayOnly().\n\t\t\t\tSetCheckFramePooling().\n\t\t\t\tSetRelayHost(rh).\n\t\t\t\tAddLogFilter(\"Failed to send call with modified arg2.\", 1)\n\n\t\t\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\t\t\trly := ts.Relay()\n\t\t\t\tcallee := ts.Server()\n\t\t\t\ttestutils.RegisterEcho(callee, nil)\n\n\t\t\t\tcaller := ts.NewServer(testutils.NewOpts())\n\t\t\t\ttestutils.RegisterEcho(caller, nil)\n\n\t\t\t\tbaseCtx := context.WithValue(context.Background(), \"foo\", \"bar\")\n\t\t\t\tctx, cancel := NewContextBuilder(time.Second).SetConnectBaseContext(baseCtx).Build()\n\t\t\t\tdefer cancel()\n\n\t\t\t\trequire.NoError(t, rly.Ping(ctx, caller.PeerInfo().HostPort))\n\n\t\t\t\terr := testutils.CallEcho(caller, ts.HostPort(), ts.ServiceName(), &raw.Args{\n\t\t\t\t\tFormat: tt.format,\n\t\t\t\t\tArg2:   tt.arg2,\n\t\t\t\t})\n\t\t\t\trequire.Error(t, err, \"should fail to send call with large arg2\")\n\t\t\t\tassert.Contains(t, err.Error(), tt.wantErr, \"unexpected error\")\n\n\t\t\t\t// Even after a failure, a simple call should still suceed (e.g., connection is left in a safe state).\n\t\t\t\terr = testutils.CallEcho(caller, ts.HostPort(), ts.ServiceName(), &raw.Args{\n\t\t\t\t\tFormat: Thrift,\n\t\t\t\t\tArg2:   encodeThriftHeaders(t, map[string]string{\"key\": \"value\"}),\n\t\t\t\t\tArg3:   testutils.RandBytes(100),\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err, \"Standard Thrift call should not fail\")\n\t\t\t})\n\t\t})\n\t}\n}\n\n// echoVerifyHandler is an echo handler with some added verification of\n// the call metadata (e.g., caller, format).\ntype echoVerifyHandler struct {\n\tt testing.TB\n\n\tappErr       bool\n\tverifyFormat Format\n\tverifyCaller string\n\tverifyMethod string\n}\n\nfunc (h echoVerifyHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\tassert.Equal(h.t, h.verifyFormat, args.Format, \"Unexpected format\")\n\tassert.Equal(h.t, h.verifyCaller, args.Caller, \"Unexpected caller\")\n\tassert.Equal(h.t, h.verifyMethod, args.Method, \"Unexpected method\")\n\n\treturn &raw.Res{\n\t\tArg2:  args.Arg2,\n\t\tArg3:  args.Arg3,\n\t\tIsErr: h.appErr,\n\t}, nil\n}\n\nfunc (h echoVerifyHandler) OnError(ctx context.Context, err error) {\n\th.t.Errorf(\"unexpected OnError: %v\", err)\n}\n\nfunc encodeThriftHeaders(t testing.TB, m map[string]string) []byte {\n\tvar buf bytes.Buffer\n\trequire.NoError(t, thrift.WriteHeaders(&buf, m), \"Failed to write headers\")\n\treturn buf.Bytes()\n}\n\nfunc decodeThriftHeaders(t testing.TB, bs []byte) map[string]string {\n\tr := bytes.NewReader(bs)\n\n\tm, err := thrift.ReadHeaders(r)\n\trequire.NoError(t, err, \"Failed to read headers\")\n\n\t// Ensure there are no remaining bytes left.\n\tremaining, err := ioutil.ReadAll(r)\n\trequire.NoError(t, err, \"failed to read from arg2 reader\")\n\tassert.Empty(t, remaining, \"expected no bytes after reading headers\")\n\n\treturn m\n}\n\nfunc copyHeaders(m map[string]string) map[string]string {\n\tif m == nil {\n\t\treturn nil\n\t}\n\n\tcopied := make(map[string]string, len(m))\n\tfor k, v := range m {\n\t\tcopied[k] = v\n\t}\n\treturn copied\n}\n"
  },
  {
    "path": "relay_timer_pool.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"math\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype relayTimerTrigger func(items *relayItems, id uint32, isOriginator bool)\n\ntype relayTimerPool struct {\n\tpool    sync.Pool\n\ttrigger relayTimerTrigger\n\tverify  bool\n}\n\ntype relayTimer struct {\n\tpool  *relayTimerPool // const\n\ttimer *time.Timer     // const\n\n\tactive   bool // mutated on Start/Stop\n\tstopped  bool // mutated on Stop\n\treleased bool // mutated on Get/Release.\n\n\t// Per-timer parameters passed back when the timer is triggered.\n\titems        *relayItems\n\tid           uint32\n\tisOriginator bool\n}\n\nfunc (rt *relayTimer) OnTimer() {\n\trt.verifyNotReleased()\n\titems, id, isOriginator := rt.items, rt.id, rt.isOriginator\n\trt.markTimerInactive()\n\trt.pool.trigger(items, id, isOriginator)\n}\n\nfunc newRelayTimerPool(trigger relayTimerTrigger, verify bool) *relayTimerPool {\n\treturn &relayTimerPool{\n\t\ttrigger: trigger,\n\t\tverify:  verify,\n\t}\n}\n\n// Get returns a relay timer that has not started. Timers must be started explicitly\n// using the Start function.\nfunc (tp *relayTimerPool) Get() *relayTimer {\n\ttimer, ok := tp.pool.Get().(*relayTimer)\n\tif ok {\n\t\ttimer.released = false\n\t\treturn timer\n\t}\n\n\trt := &relayTimer{\n\t\tpool: tp,\n\t}\n\t// Go timers are started by default. However, we need to separate creating\n\t// the timer and starting the timer for use in the relay code paths.\n\t// To make this work without more locks in the relayTimer, we create a Go timer\n\t// with a huge timeout so it doesn't run, then stop it so we can start it later.\n\trt.timer = time.AfterFunc(time.Duration(math.MaxInt64), rt.OnTimer)\n\tif !rt.timer.Stop() {\n\t\tpanic(\"relayTimer requires timers in stopped state, but failed to stop underlying timer\")\n\t}\n\treturn rt\n}\n\n// Put returns a relayTimer back to the pool.\nfunc (tp *relayTimerPool) Put(rt *relayTimer) {\n\tif tp.verify {\n\t\t// If we are trying to verify correct pool behavior, then we don't release\n\t\t// the timer, and instead ensure no methods are called after being released.\n\t\treturn\n\t}\n\ttp.pool.Put(rt)\n}\n\n// Start starts a timer with the given duration for the specified ID.\nfunc (rt *relayTimer) Start(d time.Duration, items *relayItems, id uint32, isOriginator bool) {\n\trt.verifyNotReleased()\n\tif rt.active {\n\t\tpanic(\"Tried to start an already-active timer\")\n\t}\n\n\trt.active = true\n\trt.stopped = false\n\trt.items = items\n\trt.id = id\n\trt.isOriginator = isOriginator\n\n\tif wasActive := rt.timer.Reset(d); wasActive {\n\t\tpanic(\"relayTimer's underlying timer was Started multiple times without Stop\")\n\t}\n}\n\nfunc (rt *relayTimer) markTimerInactive() {\n\trt.active = false\n\trt.items = nil\n\trt.id = 0\n\trt.items = nil\n\trt.isOriginator = false\n}\n\n// Stop stops the timer and returns whether the timer was stopped.\n// If the timer has been executed, it returns false, but in all other\n// cases, it returns true (even if the timer was stopped previously).\nfunc (rt *relayTimer) Stop() bool {\n\trt.verifyNotReleased()\n\n\tif rt.stopped {\n\t\treturn true\n\t}\n\n\tstopped := rt.timer.Stop()\n\tif stopped {\n\t\trt.stopped = true\n\t\trt.markTimerInactive()\n\t}\n\n\treturn stopped\n}\n\n// Release releases a timer back to the timer pool. The timer MUST have run or be\n// stopped before Release is called.\nfunc (rt *relayTimer) Release() {\n\trt.verifyNotReleased()\n\tif rt.active {\n\t\tpanic(\"only stopped or completed timers can be released\")\n\t}\n\trt.released = true\n\trt.pool.Put(rt)\n}\n\nfunc (rt *relayTimer) verifyNotReleased() {\n\tif rt.released {\n\t\tpanic(\"Released timer cannot be used\")\n\t}\n}\n"
  },
  {
    "path": "reqres.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\ntype errReqResWriterStateMismatch struct {\n\tstate         reqResWriterState\n\texpectedState reqResWriterState\n}\n\nfunc (e errReqResWriterStateMismatch) Error() string {\n\treturn fmt.Sprintf(\"attempting write outside of expected state, in %v expected %v\",\n\t\te.state, e.expectedState)\n}\n\ntype errReqResReaderStateMismatch struct {\n\tstate         reqResReaderState\n\texpectedState reqResReaderState\n}\n\nfunc (e errReqResReaderStateMismatch) Error() string {\n\treturn fmt.Sprintf(\"attempting read outside of expected state, in %v expected %v\",\n\t\te.state, e.expectedState)\n}\n\n// reqResWriterState defines the state of a request/response writer\ntype reqResWriterState int\n\nconst (\n\treqResWriterPreArg1 reqResWriterState = iota\n\treqResWriterPreArg2\n\treqResWriterPreArg3\n\treqResWriterComplete\n)\n\n//go:generate stringer -type=reqResWriterState\n\n// messageForFragment determines which message should be used for the given\n// fragment\ntype messageForFragment func(initial bool) message\n\n// A reqResWriter writes out requests/responses.  Exactly which it does is\n// determined by its messageForFragment function which returns the appropriate\n// message to use when building an initial or follow-on fragment.\ntype reqResWriter struct {\n\tconn               *Connection\n\tcontents           *fragmentingWriter\n\tmex                *messageExchange\n\tstate              reqResWriterState\n\tmessageForFragment messageForFragment\n\tlog                Logger\n\terr                error\n}\n\n//go:generate stringer -type=reqResReaderState\n\nfunc (w *reqResWriter) argWriter(last bool, inState reqResWriterState, outState reqResWriterState) (ArgWriter, error) {\n\tif w.err != nil {\n\t\treturn nil, w.err\n\t}\n\n\tif w.state != inState {\n\t\treturn nil, w.failed(errReqResWriterStateMismatch{state: w.state, expectedState: inState})\n\t}\n\n\targWriter, err := w.contents.ArgWriter(last)\n\tif err != nil {\n\t\treturn nil, w.failed(err)\n\t}\n\n\tw.state = outState\n\treturn argWriter, nil\n}\n\nfunc (w *reqResWriter) arg1Writer() (ArgWriter, error) {\n\treturn w.argWriter(false /* last */, reqResWriterPreArg1, reqResWriterPreArg2)\n}\n\nfunc (w *reqResWriter) arg2Writer() (ArgWriter, error) {\n\treturn w.argWriter(false /* last */, reqResWriterPreArg2, reqResWriterPreArg3)\n}\n\nfunc (w *reqResWriter) arg3Writer() (ArgWriter, error) {\n\treturn w.argWriter(true /* last */, reqResWriterPreArg3, reqResWriterComplete)\n}\n\n// newFragment creates a new fragment for marshaling into\nfunc (w *reqResWriter) newFragment(initial bool, checksum Checksum) (*writableFragment, error) {\n\tif err := w.mex.checkError(); err != nil {\n\t\treturn nil, w.failed(err)\n\t}\n\n\tmessage := w.messageForFragment(initial)\n\n\t// Create the frame\n\tframe := w.conn.opts.FramePool.Get()\n\tframe.Header.ID = w.mex.msgID\n\tframe.Header.messageType = message.messageType()\n\n\t// Write the message into the fragment, reserving flags and checksum bytes\n\twbuf := typed.NewWriteBuffer(frame.Payload[:])\n\tfragment := new(writableFragment)\n\tfragment.frame = frame\n\tfragment.flagsRef = wbuf.DeferByte()\n\tif err := message.write(wbuf); err != nil {\n\t\treturn nil, err\n\t}\n\twbuf.WriteSingleByte(byte(checksum.TypeCode()))\n\tfragment.checksumRef = wbuf.DeferBytes(checksum.Size())\n\tfragment.checksum = checksum\n\tfragment.contents = wbuf\n\treturn fragment, wbuf.Err()\n}\n\n// flushFragment sends a fragment to the peer over the connection\nfunc (w *reqResWriter) flushFragment(fragment *writableFragment) error {\n\tif w.err != nil {\n\t\treturn w.err\n\t}\n\n\tframe := fragment.frame\n\tframe.Header.SetPayloadSize(uint16(fragment.contents.BytesWritten()))\n\n\tif err := w.mex.checkError(); err != nil {\n\t\treturn w.failed(err)\n\t}\n\tselect {\n\tcase <-w.mex.ctx.Done():\n\t\treturn w.failed(GetContextError(w.mex.ctx.Err()))\n\tcase <-w.mex.errCh.c:\n\t\treturn w.failed(w.mex.errCh.err)\n\tcase w.conn.sendCh <- frame:\n\t\treturn nil\n\t}\n}\n\n// failed marks the writer as having failed\nfunc (w *reqResWriter) failed(err error) error {\n\tw.log.Debugf(\"writer failed: %v existing err: %v\", err, w.err)\n\tif w.err != nil {\n\t\treturn w.err\n\t}\n\n\tw.mex.shutdown()\n\tw.err = err\n\treturn w.err\n}\n\n// reqResReaderState defines the state of a request/response reader\ntype reqResReaderState int\n\nconst (\n\treqResReaderPreArg1 reqResReaderState = iota\n\treqResReaderPreArg2\n\treqResReaderPreArg3\n\treqResReaderComplete\n)\n\n// A reqResReader is capable of reading arguments from a request or response object.\ntype reqResReader struct {\n\tcontents           *fragmentingReader\n\tmex                *messageExchange\n\tstate              reqResReaderState\n\tmessageForFragment messageForFragment\n\tinitialFragment    *readableFragment\n\tpreviousFragment   *readableFragment\n\tlog                Logger\n\terr                error\n}\n\n// arg1Reader returns an ArgReader to read arg1.\nfunc (r *reqResReader) arg1Reader() (ArgReader, error) {\n\treturn r.argReader(false /* last */, reqResReaderPreArg1, reqResReaderPreArg2)\n}\n\n// arg2Reader returns an ArgReader to read arg2.\nfunc (r *reqResReader) arg2Reader() (ArgReader, error) {\n\treturn r.argReader(false /* last */, reqResReaderPreArg2, reqResReaderPreArg3)\n}\n\n// arg3Reader returns an ArgReader to read arg3.\nfunc (r *reqResReader) arg3Reader() (ArgReader, error) {\n\treturn r.argReader(true /* last */, reqResReaderPreArg3, reqResReaderComplete)\n}\n\n// argReader returns an ArgReader that can be used to read an argument. The\n// ReadCloser must be closed once the argument has been read.\nfunc (r *reqResReader) argReader(last bool, inState reqResReaderState, outState reqResReaderState) (ArgReader, error) {\n\tif r.state != inState {\n\t\treturn nil, r.failed(errReqResReaderStateMismatch{state: r.state, expectedState: inState})\n\t}\n\n\targReader, err := r.contents.ArgReader(last)\n\tif err != nil {\n\t\treturn nil, r.failed(err)\n\t}\n\n\tr.state = outState\n\treturn argReader, nil\n}\n\n// recvNextFragment receives the next fragment from the underlying message exchange.\nfunc (r *reqResReader) recvNextFragment(initial bool) (*readableFragment, error) {\n\tif r.initialFragment != nil {\n\t\tfragment := r.initialFragment\n\t\tr.initialFragment = nil\n\t\tr.previousFragment = fragment\n\t\treturn fragment, nil\n\t}\n\n\t// Wait for the appropriate message from the peer\n\tmessage := r.messageForFragment(initial)\n\tframe, err := r.mex.recvPeerFrameOfType(message.messageType())\n\tif err != nil {\n\t\tif err, ok := err.(errorMessage); ok {\n\t\t\t// If we received a serialized error from the other side, then we should go through\n\t\t\t// the normal doneReading path so stats get updated with this error.\n\t\t\tr.err = err.AsSystemError()\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn nil, r.failed(err)\n\t}\n\n\t// Parse the message and setup the fragment\n\tfragment, err := parseInboundFragment(r.mex.framePool, frame, message)\n\tif err != nil {\n\t\treturn nil, r.failed(err)\n\t}\n\n\tr.previousFragment = fragment\n\treturn fragment, nil\n}\n\n// releasePreviousFrament releases the last fragment returned by the reader if\n// it's still around. This operation is idempotent.\nfunc (r *reqResReader) releasePreviousFragment() {\n\tfragment := r.previousFragment\n\tr.previousFragment = nil\n\tif fragment != nil {\n\t\tfragment.done()\n\t}\n}\n\n// failed indicates the reader failed\nfunc (r *reqResReader) failed(err error) error {\n\tr.log.Debugf(\"reader failed: %v existing err: %v\", err, r.err)\n\tif r.err != nil {\n\t\treturn r.err\n\t}\n\n\tr.mex.shutdown()\n\tr.err = err\n\treturn r.err\n}\n\n// parseInboundFragment parses an incoming fragment based on the given message\nfunc parseInboundFragment(framePool FramePool, frame *Frame, message message) (*readableFragment, error) {\n\trbuf := typed.NewReadBuffer(frame.SizedPayload())\n\tfragment := new(readableFragment)\n\tfragment.flags = rbuf.ReadSingleByte()\n\tif err := message.read(rbuf); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfragment.checksumType = ChecksumType(rbuf.ReadSingleByte())\n\tfragment.checksum = rbuf.ReadBytes(fragment.checksumType.ChecksumSize())\n\tfragment.contents = rbuf\n\tfragment.onDone = func() {\n\t\tframePool.Release(frame)\n\t}\n\treturn fragment, rbuf.Err()\n}\n"
  },
  {
    "path": "reqresreaderstate_string.go",
    "content": "// generated by stringer -type=reqResReaderState; DO NOT EDIT\n\npackage tchannel\n\nimport \"fmt\"\n\nconst _reqResReaderState_name = \"reqResReaderPreArg1reqResReaderPreArg2reqResReaderPreArg3reqResReaderComplete\"\n\nvar _reqResReaderState_index = [...]uint8{0, 19, 38, 57, 77}\n\nfunc (i reqResReaderState) String() string {\n\tif i < 0 || i+1 >= reqResReaderState(len(_reqResReaderState_index)) {\n\t\treturn fmt.Sprintf(\"reqResReaderState(%d)\", i)\n\t}\n\treturn _reqResReaderState_name[_reqResReaderState_index[i]:_reqResReaderState_index[i+1]]\n}\n"
  },
  {
    "path": "reqreswriterstate_string.go",
    "content": "// generated by stringer -type=reqResWriterState; DO NOT EDIT\n\npackage tchannel\n\nimport \"fmt\"\n\nconst _reqResWriterState_name = \"reqResWriterPreArg1reqResWriterPreArg2reqResWriterPreArg3reqResWriterComplete\"\n\nvar _reqResWriterState_index = [...]uint8{0, 19, 38, 57, 77}\n\nfunc (i reqResWriterState) String() string {\n\tif i < 0 || i+1 >= reqResWriterState(len(_reqResWriterState_index)) {\n\t\treturn fmt.Sprintf(\"reqResWriterState(%d)\", i)\n\t}\n\treturn _reqResWriterState_name[_reqResWriterState_index[i]:_reqResWriterState_index[i+1]]\n}\n"
  },
  {
    "path": "retry.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"golang.org/x/net/context\"\n)\n\n// RetryOn represents the types of errors to retry on.\ntype RetryOn int\n\n//go:generate stringer -type=RetryOn\n\nconst (\n\t// RetryDefault is currently the same as RetryConnectionError.\n\tRetryDefault RetryOn = iota\n\n\t// RetryConnectionError retries on busy frames, declined frames, and connection errors.\n\tRetryConnectionError\n\n\t// RetryNever never retries any errors.\n\tRetryNever\n\n\t// RetryNonIdempotent will retry errors that occur before a request has been picked up.\n\t// E.g. busy frames and declined frames.\n\t// This should be used when making calls to non-idempotent endpoints.\n\tRetryNonIdempotent\n\n\t// RetryUnexpected will retry busy frames, declined frames, and unenxpected frames.\n\tRetryUnexpected\n\n\t// RetryIdempotent will retry all errors that can be retried. This should be used\n\t// for idempotent endpoints.\n\tRetryIdempotent\n)\n\n// RequestState is a global request state that persists across retries.\ntype RequestState struct {\n\t// Start is the time at which the request was initiated by the caller of RunWithRetry.\n\tStart time.Time\n\t// SelectedPeers is a set of host:ports that have been selected previously.\n\tSelectedPeers map[string]struct{}\n\t// Attempt is 1 for the first attempt, and so on.\n\tAttempt   int\n\tretryOpts *RetryOptions\n}\n\n// RetriableFunc is the type of function that can be passed to RunWithRetry.\ntype RetriableFunc func(context.Context, *RequestState) error\n\nfunc isNetError(err error) bool {\n\t// TODO(prashantv): Should TChannel internally these to ErrCodeNetwork before returning\n\t// them to the user?\n\t_, ok := err.(net.Error)\n\treturn ok\n}\n\nfunc getErrCode(err error) SystemErrCode {\n\tcode := GetSystemErrorCode(err)\n\tif isNetError(err) {\n\t\tcode = ErrCodeNetwork\n\t}\n\treturn code\n}\n\n// CanRetry returns whether an error can be retried for the given retry option.\nfunc (r RetryOn) CanRetry(err error) bool {\n\tif r == RetryNever {\n\t\treturn false\n\t}\n\tif r == RetryDefault {\n\t\tr = RetryConnectionError\n\t}\n\n\tcode := getErrCode(err)\n\n\tif code == ErrCodeBusy || code == ErrCodeDeclined {\n\t\treturn true\n\t}\n\t// Never retry bad requests, since it will probably cause another bad request.\n\tif code == ErrCodeBadRequest {\n\t\treturn false\n\t}\n\n\tswitch r {\n\tcase RetryConnectionError:\n\t\treturn code == ErrCodeNetwork\n\tcase RetryUnexpected:\n\t\treturn code == ErrCodeUnexpected\n\tcase RetryIdempotent:\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// RetryOptions are the retry options used to configure RunWithRetry.\ntype RetryOptions struct {\n\t// MaxAttempts is the maximum number of calls and retries that will be made.\n\t// If this is 0, the default number of attempts (5) is used.\n\tMaxAttempts int\n\n\t// RetryOn is the types of errors to retry on.\n\tRetryOn RetryOn\n\n\t// TimeoutPerAttempt is the per-retry timeout to use.\n\t// If this is zero, then the original timeout is used.\n\tTimeoutPerAttempt time.Duration\n}\n\nvar defaultRetryOptions = &RetryOptions{\n\tMaxAttempts: 5,\n}\n\nvar requestStatePool = sync.Pool{\n\tNew: func() interface{} { return &RequestState{} },\n}\n\nfunc getRetryOptions(ctx context.Context) *RetryOptions {\n\tparams := getTChannelParams(ctx)\n\tif params == nil {\n\t\treturn defaultRetryOptions\n\t}\n\n\topts := params.retryOptions\n\tif opts == nil {\n\t\treturn defaultRetryOptions\n\t}\n\n\tif opts.MaxAttempts == 0 {\n\t\topts.MaxAttempts = defaultRetryOptions.MaxAttempts\n\t}\n\treturn opts\n}\n\n// HasRetries will return true if there are more retries left.\nfunc (rs *RequestState) HasRetries(err error) bool {\n\tif rs == nil {\n\t\treturn false\n\t}\n\trOpts := rs.retryOpts\n\treturn rs.Attempt < rOpts.MaxAttempts && rOpts.RetryOn.CanRetry(err)\n}\n\n// SinceStart returns the time since the start of the request. If there is no request state,\n// then the fallback is returned.\nfunc (rs *RequestState) SinceStart(now time.Time, fallback time.Duration) time.Duration {\n\tif rs == nil {\n\t\treturn fallback\n\t}\n\treturn now.Sub(rs.Start)\n}\n\n// PrevSelectedPeers returns the previously selected peers for this request.\nfunc (rs *RequestState) PrevSelectedPeers() map[string]struct{} {\n\tif rs == nil {\n\t\treturn nil\n\t}\n\treturn rs.SelectedPeers\n}\n\n// AddSelectedPeer adds a given peer to the set of selected peers.\nfunc (rs *RequestState) AddSelectedPeer(hostPort string) {\n\tif rs == nil {\n\t\treturn\n\t}\n\n\thost := getHost(hostPort)\n\tif rs.SelectedPeers == nil {\n\t\trs.SelectedPeers = map[string]struct{}{\n\t\t\thostPort: {},\n\t\t\thost:     {},\n\t\t}\n\t} else {\n\t\trs.SelectedPeers[hostPort] = struct{}{}\n\t\trs.SelectedPeers[host] = struct{}{}\n\t}\n}\n\n// RetryCount returns the retry attempt this is. Essentially, Attempt - 1.\nfunc (rs *RequestState) RetryCount() int {\n\tif rs == nil {\n\t\treturn 0\n\t}\n\treturn rs.Attempt - 1\n}\n\n// RunWithRetry will take a function that makes the TChannel call, and will\n// rerun it as specifed in the RetryOptions in the Context.\nfunc (ch *Channel) RunWithRetry(runCtx context.Context, f RetriableFunc) error {\n\tvar err error\n\n\topts := getRetryOptions(runCtx)\n\trs := ch.getRequestState(opts)\n\tdefer requestStatePool.Put(rs)\n\n\tfor i := 0; i < opts.MaxAttempts; i++ {\n\t\trs.Attempt++\n\n\t\tif opts.TimeoutPerAttempt == 0 {\n\t\t\terr = f(runCtx, rs)\n\t\t} else {\n\t\t\tattemptCtx, cancel := context.WithTimeout(runCtx, opts.TimeoutPerAttempt)\n\t\t\terr = f(attemptCtx, rs)\n\t\t\tcancel()\n\t\t}\n\n\t\tif err == nil {\n\t\t\treturn nil\n\t\t}\n\t\tif !opts.RetryOn.CanRetry(err) {\n\t\t\tif ch.log.Enabled(LogLevelInfo) {\n\t\t\t\tch.log.WithFields(ErrField(err)).Info(\"Failed after non-retriable error.\")\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\n\t\tch.log.WithFields(\n\t\t\tErrField(err),\n\t\t\tLogField{\"attempt\", rs.Attempt},\n\t\t\tLogField{\"maxAttempts\", opts.MaxAttempts},\n\t\t).Info(\"Retrying request after retryable error.\")\n\t}\n\n\t// Too many retries, return the last error\n\treturn err\n}\n\nfunc (ch *Channel) getRequestState(retryOpts *RetryOptions) *RequestState {\n\trs := requestStatePool.Get().(*RequestState)\n\t*rs = RequestState{\n\t\tStart:     ch.timeNow(),\n\t\tretryOpts: retryOpts,\n\t}\n\treturn rs\n}\n\n// getHost returns the host part of a host:port. If no ':' is found, it returns the\n// original string. Note: This hand-rolled loop is faster than using strings.IndexByte.\nfunc getHost(hostPort string) string {\n\tfor i := 0; i < len(hostPort); i++ {\n\t\tif hostPort[i] == ':' {\n\t\t\treturn hostPort[:i]\n\t\t}\n\t}\n\treturn hostPort\n}\n"
  },
  {
    "path": "retry_request_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"golang.org/x/net/context\"\n)\n\nfunc TestRequestStateRetry(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(raw.Wrap(newTestHandler(t)), \"echo\")\n\n\t\tclosedHostPorts := make([]string, 4)\n\t\tfor i := range closedHostPorts {\n\t\t\thostPort, close := testutils.GetAcceptCloseHostPort(t)\n\t\t\tdefer close()\n\t\t\tclosedHostPorts[i] = hostPort\n\t\t}\n\n\t\t// Since we close connections remotely, there will be some warnings that we can ignore.\n\t\topts := testutils.NewOpts().DisableLogVerification()\n\t\tclient := ts.NewClient(opts)\n\t\tdefer client.Close()\n\t\tcounter := 0\n\n\t\tsc := client.GetSubChannel(ts.Server().ServiceName())\n\t\terr := client.RunWithRetry(ctx, func(ctx context.Context, rs *RequestState) error {\n\t\t\tdefer func() { counter++ }()\n\n\t\t\texpectedPeers := counter\n\t\t\tif expectedPeers > 0 {\n\t\t\t\t// An entry is also added for each host.\n\t\t\t\texpectedPeers++\n\t\t\t}\n\n\t\t\tassert.Equal(t, expectedPeers, len(rs.SelectedPeers), \"SelectedPeers should not be reused\")\n\n\t\t\tif counter < 4 {\n\t\t\t\tclient.Peers().Add(closedHostPorts[counter])\n\t\t\t} else {\n\t\t\t\tclient.Peers().Add(ts.HostPort())\n\t\t\t}\n\n\t\t\t_, err := raw.CallV2(ctx, sc, raw.CArgs{\n\t\t\t\tMethod:      \"echo\",\n\t\t\t\tCallOptions: &CallOptions{RequestState: rs},\n\t\t\t})\n\t\t\treturn err\n\t\t})\n\t\tassert.NoError(t, err, \"RunWithRetry should succeed\")\n\t\tassert.Equal(t, 5, counter, \"RunWithRetry should retry 5 times\")\n\t})\n}\n"
  },
  {
    "path": "retry_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\nfunc createFuncToRetry(t *testing.T, errors ...error) (RetriableFunc, *int) {\n\ti := 0\n\treturn func(_ context.Context, rs *RequestState) error {\n\t\tdefer func() { i++ }()\n\t\tif i >= len(errors) {\n\t\t\tt.Fatalf(\"Retry function has no error to return for this call\")\n\t\t}\n\t\tassert.Equal(t, i+1, rs.Attempt, \"Attempt count mismatch\")\n\n\t\terr := errors[i]\n\t\treturn err\n\t}, &i\n}\n\ntype testErrors struct {\n\tBusy       error\n\tDeclined   error\n\tTimeout    error\n\tNetwork    error\n\tConnection error\n\tBadRequest error\n\tUnexpected error\n\tCancelled  error\n\n\tall []error\n}\n\nfunc getTestErrors() testErrors {\n\terrs := testErrors{\n\t\tBusy:       ErrServerBusy,\n\t\tDeclined:   ErrChannelClosed,\n\t\tTimeout:    ErrTimeout,\n\t\tNetwork:    NewSystemError(ErrCodeNetwork, \"fake network error\"),\n\t\tConnection: net.UnknownNetworkError(\"fake connection error\"),\n\t\tBadRequest: ErrTimeoutRequired,\n\t\tUnexpected: NewSystemError(ErrCodeUnexpected, \"fake unexpected error\"),\n\t\tCancelled:  NewSystemError(ErrCodeCancelled, \"fake cancelled error\"),\n\t}\n\terrs.all = []error{errs.Busy, errs.Declined, errs.Timeout, errs.Network, errs.Connection,\n\t\terrs.BadRequest, errs.Unexpected, errs.Cancelled}\n\treturn errs\n}\n\nfunc TestCanRetry(t *testing.T) {\n\te := getTestErrors()\n\ttests := []struct {\n\t\tRetryOn RetryOn\n\t\tRetryOK []error\n\t}{\n\t\t{RetryNever, nil},\n\t\t{RetryDefault, []error{e.Busy, e.Declined, e.Network, e.Connection}},\n\t\t{RetryConnectionError, []error{e.Busy, e.Declined, e.Network, e.Connection}},\n\t\t{RetryNonIdempotent, []error{e.Busy, e.Declined}},\n\t\t{RetryUnexpected, []error{e.Busy, e.Declined, e.Unexpected}},\n\t\t{RetryIdempotent, []error{e.Busy, e.Declined, e.Timeout, e.Network, e.Connection, e.Unexpected, e.Cancelled}},\n\t}\n\n\tfor _, tt := range tests {\n\t\tretryOK := make(map[error]bool)\n\t\tfor _, err := range tt.RetryOK {\n\t\t\tretryOK[err] = true\n\t\t}\n\n\t\tfor _, err := range e.all {\n\t\t\texpectOK := retryOK[err]\n\t\t\tassert.Equal(t, expectOK, tt.RetryOn.CanRetry(err),\n\t\t\t\t\"%v.CanRetry(%v) expected %v\", tt.RetryOn, err, expectOK)\n\t\t}\n\t}\n}\n\nfunc TestNoRetry(t *testing.T) {\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\n\te := getTestErrors()\n\tretryOpts := &RetryOptions{RetryOn: RetryNever}\n\tfor _, fErr := range e.all {\n\t\tctx, cancel := NewContextBuilder(time.Second).SetRetryOptions(retryOpts).Build()\n\t\tdefer cancel()\n\n\t\tf, counter := createFuncToRetry(t, fErr)\n\t\terr := ch.RunWithRetry(ctx, f)\n\t\tassert.Equal(t, fErr, err)\n\t\tassert.Equal(t, 1, *counter, \"f should not be retried when retried are disabled\")\n\t}\n}\n\nfunc TestRetryTillMaxAttempts(t *testing.T) {\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\n\tsetErr := ErrServerBusy\n\trunTest := func(maxAttempts, numErrors, expectCounter int, expectErr error) {\n\t\tretryOpts := &RetryOptions{MaxAttempts: maxAttempts}\n\t\tctx, cancel := NewContextBuilder(time.Second).SetRetryOptions(retryOpts).Build()\n\t\tdefer cancel()\n\n\t\tvar errors []error\n\t\tfor i := 0; i < numErrors; i++ {\n\t\t\terrors = append(errors, setErr)\n\t\t}\n\t\terrors = append(errors, nil)\n\n\t\tf, counter := createFuncToRetry(t, errors...)\n\t\terr := ch.RunWithRetry(ctx, f)\n\t\tassert.Equal(t, expectErr, err,\n\t\t\t\"unexpected result for maxAttempts = %v numErrors = %v\", maxAttempts, numErrors)\n\t\tassert.Equal(t, expectCounter, *counter,\n\t\t\t\"expected f to be retried %v times with maxAttempts = %v numErrors = %v\",\n\t\t\texpectCounter, maxAttempts, numErrors)\n\t}\n\n\tfor numAttempts := 1; numAttempts < 5; numAttempts++ {\n\t\tfor numErrors := 0; numErrors < numAttempts+3; numErrors++ {\n\t\t\tvar expectErr error\n\t\t\tif numErrors >= numAttempts {\n\t\t\t\texpectErr = setErr\n\t\t\t}\n\n\t\t\texpectCount := numErrors + 1\n\t\t\tif expectCount > numAttempts {\n\t\t\t\texpectCount = numAttempts\n\t\t\t}\n\n\t\t\trunTest(numAttempts, numErrors, expectCount, expectErr)\n\t\t}\n\t}\n}\n\nfunc TestRetrySubContextNoTimeoutPerAttempt(t *testing.T) {\n\te := getTestErrors()\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\n\tcounter := 0\n\tch.RunWithRetry(ctx, func(sctx context.Context, _ *RequestState) error {\n\t\tcounter++\n\t\tassert.Equal(t, ctx, sctx, \"Sub-context should be the same\")\n\t\treturn e.Busy\n\t})\n\tassert.Equal(t, 5, counter, \"RunWithRetry did not run f enough times\")\n}\n\nfunc TestRetrySubContextTimeoutPerAttempt(t *testing.T) {\n\te := getTestErrors()\n\tctx, cancel := NewContextBuilder(time.Second).\n\t\tSetTimeoutPerAttempt(time.Millisecond).Build()\n\tdefer cancel()\n\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\n\tvar lastDeadline time.Time\n\n\tcounter := 0\n\tch.RunWithRetry(ctx, func(sctx context.Context, _ *RequestState) error {\n\t\tcounter++\n\n\t\tassert.NotEqual(t, ctx, sctx, \"Sub-context should be different\")\n\t\tdeadline, _ := sctx.Deadline()\n\t\tassert.True(t, deadline.After(lastDeadline), \"Deadline is invalid\")\n\t\tlastDeadline = deadline\n\n\t\toverallDeadline, _ := ctx.Deadline()\n\t\tassert.True(t, overallDeadline.After(deadline), \"Deadline is invalid\")\n\n\t\treturn e.Busy\n\t})\n\tassert.Equal(t, 5, counter, \"RunWithRetry did not run f enough times\")\n}\n\nfunc TestRetryNetConnect(t *testing.T) {\n\te := getTestErrors()\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tclosedAddr := testutils.GetClosedHostPort(t)\n\tlistenC, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\trequire.NoError(t, err, \"Listen failed\")\n\tdefer listenC.Close()\n\n\tcounter := 0\n\tf := func(ctx context.Context, rs *RequestState) error {\n\t\tcounter++\n\t\tif !rs.HasRetries(e.Connection) {\n\t\t\tc, err := net.Dial(\"tcp\", listenC.Addr().String())\n\t\t\tif err == nil {\n\t\t\t\tc.Close()\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\n\t\t_, err := net.Dial(\"tcp\", closedAddr)\n\t\treturn err\n\t}\n\n\tassert.NoError(t, ch.RunWithRetry(ctx, f), \"RunWithRetry should succeed\")\n\tassert.Equal(t, 5, counter, \"RunWithRetry should have run f 5 times\")\n}\n\nfunc TestRequestStateSince(t *testing.T) {\n\tbaseTime := time.Date(2015, 1, 2, 3, 4, 5, 6, time.UTC)\n\ttests := []struct {\n\t\trequestState *RequestState\n\t\tnow          time.Time\n\t\tfallback     time.Duration\n\t\texpected     time.Duration\n\t}{\n\t\t{\n\t\t\trequestState: nil,\n\t\t\tfallback:     3 * time.Millisecond,\n\t\t\texpected:     3 * time.Millisecond,\n\t\t},\n\t\t{\n\t\t\trequestState: &RequestState{Start: baseTime},\n\t\t\tnow:          baseTime.Add(7 * time.Millisecond),\n\t\t\tfallback:     5 * time.Millisecond,\n\t\t\texpected:     7 * time.Millisecond,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tgot := tt.requestState.SinceStart(tt.now, tt.fallback)\n\t\tassert.Equal(t, tt.expected, got, \"%+v.SinceStart(%v, %v) expected %v got %v\",\n\t\t\ttt.requestState, tt.now, tt.fallback, tt.expected, got)\n\t}\n}\n"
  },
  {
    "path": "retryon_string.go",
    "content": "// generated by stringer -type=RetryOn; DO NOT EDIT\n\npackage tchannel\n\nimport \"fmt\"\n\nconst _RetryOn_name = \"RetryDefaultRetryConnectionErrorRetryNeverRetryNonIdempotentRetryUnexpectedRetryIdempotent\"\n\nvar _RetryOn_index = [...]uint8{0, 12, 32, 42, 60, 75, 90}\n\nfunc (i RetryOn) String() string {\n\tif i < 0 || i+1 >= RetryOn(len(_RetryOn_index)) {\n\t\treturn fmt.Sprintf(\"RetryOn(%d)\", i)\n\t}\n\treturn _RetryOn_name[_RetryOn_index[i]:_RetryOn_index[i+1]]\n}\n"
  },
  {
    "path": "root_peer_list.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport \"sync\"\n\n// RootPeerList is the root peer list which is only used to connect to\n// peers and share peers between subchannels.\ntype RootPeerList struct {\n\tsync.RWMutex\n\n\tchannel             Connectable\n\tonPeerStatusChanged func(*Peer)\n\tpeersByHostPort     map[string]*Peer\n}\n\nfunc newRootPeerList(ch Connectable, onPeerStatusChanged func(*Peer)) *RootPeerList {\n\treturn &RootPeerList{\n\t\tchannel:             ch,\n\t\tonPeerStatusChanged: onPeerStatusChanged,\n\t\tpeersByHostPort:     make(map[string]*Peer),\n\t}\n}\n\n// newChild returns a new isolated peer list that shares the underlying peers\n// with the root peer list.\nfunc (l *RootPeerList) newChild() *PeerList {\n\treturn newPeerList(l)\n}\n\n// Add adds a peer to the root peer list if it does not exist, or return\n// an existing peer if it exists.\nfunc (l *RootPeerList) Add(hostPort string) *Peer {\n\tl.RLock()\n\n\tif p, ok := l.peersByHostPort[hostPort]; ok {\n\t\tl.RUnlock()\n\t\treturn p\n\t}\n\n\tl.RUnlock()\n\tl.Lock()\n\tdefer l.Unlock()\n\n\tif p, ok := l.peersByHostPort[hostPort]; ok {\n\t\treturn p\n\t}\n\n\tvar p *Peer\n\t// To avoid duplicate connections, only the root list should create new\n\t// peers. All other lists should keep refs to the root list's peers.\n\tp = newPeer(l.channel, hostPort, l.onPeerStatusChanged, l.onClosedConnRemoved)\n\tl.peersByHostPort[hostPort] = p\n\treturn p\n}\n\n// GetOrAdd returns a peer for the given hostPort, creating one if it doesn't yet exist.\nfunc (l *RootPeerList) GetOrAdd(hostPort string) *Peer {\n\tpeer, ok := l.Get(hostPort)\n\tif ok {\n\t\treturn peer\n\t}\n\n\treturn l.Add(hostPort)\n}\n\n// Get returns a peer for the given hostPort if it exists.\nfunc (l *RootPeerList) Get(hostPort string) (*Peer, bool) {\n\tl.RLock()\n\tp, ok := l.peersByHostPort[hostPort]\n\tl.RUnlock()\n\treturn p, ok\n}\n\nfunc (l *RootPeerList) onClosedConnRemoved(peer *Peer) {\n\thostPort := peer.HostPort()\n\tp, ok := l.Get(hostPort)\n\tif !ok {\n\t\t// It's possible that multiple connections were closed and removed at the same time,\n\t\t// so multiple goroutines might be removing the peer from the root peer list.\n\t\treturn\n\t}\n\n\tif p.canRemove() {\n\t\tl.Lock()\n\t\tdelete(l.peersByHostPort, hostPort)\n\t\tl.Unlock()\n\t\tl.channel.Logger().WithFields(\n\t\t\tLogField{\"remoteHostPort\", hostPort},\n\t\t).Debug(\"Removed peer from root peer list.\")\n\t}\n}\n\n// Copy returns a map of the peer list. This method should only be used for testing.\nfunc (l *RootPeerList) Copy() map[string]*Peer {\n\tl.RLock()\n\tdefer l.RUnlock()\n\n\tlistCopy := make(map[string]*Peer)\n\tfor k, v := range l.peersByHostPort {\n\t\tlistCopy[k] = v\n\t}\n\treturn listCopy\n}\n"
  },
  {
    "path": "scripts/install-thrift.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\nif [ -z \"${1}\" ]; then\n  echo \"usage: ${0} installDirPath\" >&2\n  exit 1\nfi\n\nBIN_FILE=\"thrift-1\"\nTAR_FILE=\"${BIN_FILE}-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m).tar.gz\"\nTAR_LOCATION=\"https://github.com/uber/tchannel-go/releases/download/thrift-v1.0.0-dev/${TAR_FILE}\"\n\nmkdir -p \"${1}\"\ncd \"${1}\"\nwget \"${TAR_LOCATION}\"\ntar xzf \"${TAR_FILE}\"\nrm -f \"${TAR_FILE}\"\nmv \"${BIN_FILE}\" \"thrift\"\n"
  },
  {
    "path": "scripts/vbumper/main.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// vbumper helps bump version numbers in the repository and in the CHANGELOG.\npackage main\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"html/template\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar (\n\t_changelogFile = flag.String(\"changelog-file\", \"CHANGELOG.md\", \"Filename of the changelog file\")\n\t_versionFile   = flag.String(\"version-file\", \"version.go\", \"Filename of where the version information is stored\")\n\t_version       = flag.String(\"version\", \"\", \"Version to mention in changelog and version.go\")\n\t_versionDate   = flag.String(\"version-date\", \"\", \"Date to use in the changelog, by default the current date\")\n\t_skipChangelog = flag.Bool(\"skip-changelog\", false, \"Skip updating the changelog\")\n)\n\nfunc main() {\n\t*_versionDate = time.Now().Format(\"2006-01-02\")\n\tflag.Parse()\n\n\tif *_version == \"\" {\n\t\tlog.Fatal(\"Please specify the version to release using --version\")\n\t}\n\t*_version = strings.TrimPrefix(*_version, \"v\")\n\n\tprevVersion, err := updateChangelog()\n\tif err != nil {\n\t\tlog.Fatal(\"failed to update changelog\", err)\n\t}\n\n\tif err := updateVersion(prevVersion); err != nil {\n\t\tlog.Fatal(\"failed to update version\", err)\n\t}\n}\n\nfunc updateVersion(prevVersion string) error {\n\tversionBytes, err := ioutil.ReadFile(*_versionFile)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tnewContents := insertNewVersion(string(versionBytes), prevVersion, *_version)\n\treturn ioutil.WriteFile(*_versionFile, []byte(newContents), 0666)\n}\n\nfunc insertNewVersion(contents, prevVersion, newVersion string) string {\n\t// Find the version string in the file\n\tversionStart := strings.Index(contents, prevVersion)\n\tversionLine := contents[versionStart:]\n\tversionEnd := strings.Index(versionLine, `\"`) + versionStart\n\treturn contents[:versionStart] + newVersion + contents[versionEnd:]\n}\n\nfunc updateChangelog() (oldVersion string, _ error) {\n\tchangelogBytes, err := ioutil.ReadFile(*_changelogFile)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tnewLog, oldVersion, err := insertNewChangelog(string(changelogBytes))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tnewLog, err = insertChangesLink(newLog, oldVersion, *_version)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif *_skipChangelog {\n\t\treturn oldVersion, nil\n\t}\n\n\treturn oldVersion, ioutil.WriteFile(*_changelogFile, []byte(newLog), 0666)\n}\n\nfunc insertNewChangelog(contents string) (string, string, error) {\n\tprevVersionHeader := strings.Index(contents, \"\\n## [\")\n\tif prevVersionHeader < 0 {\n\t\treturn \"\", \"\", errors.New(\"failed to find version header in changelog\")\n\t}\n\n\t// Skip the newline\n\tprevVersionHeader++\n\tversionLine := contents[prevVersionHeader:]\n\tprevVersionEnd := strings.Index(versionLine, \"]\")\n\tprevVersion := strings.TrimSpace(versionLine[4:prevVersionEnd])\n\n\t// The version tag has a \"v\" prefix.\n\tnewChanges, err := getNewChangelog(\"v\" + prevVersion)\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\n\tnewContents := contents[:prevVersionHeader] + newChanges + contents[prevVersionHeader:]\n\treturn newContents, prevVersion, nil\n}\n\nfunc getNewChangelog(prevVersion string) (string, error) {\n\tchanges, err := getChanges(prevVersion)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif len(changes) == 0 {\n\t\tchanges = []string{\"No changes yet\"}\n\t}\n\n\tbuf := &bytes.Buffer{}\n\t_changeTmpl.Execute(buf, struct {\n\t\tVersion string\n\t\tDate    string\n\t\tChanges []string\n\t}{\n\t\tVersion: *_version,\n\t\tDate:    *_versionDate,\n\t\tChanges: changes,\n\t})\n\treturn buf.String(), nil\n}\n\nvar _changeTmpl = template.Must(template.New(\"changelog\").Parse(\n\t`## [{{ .Version }}] - {{ .Date }}\n### Changed\n{{ range .Changes }}\n * {{ . -}}\n{{ end }}\n\n`))\n\nfunc getChanges(prevVersion string) ([]string, error) {\n\tcmd := exec.Command(\"git\", \"log\", \"--format=%s\", \"--no-merges\", prevVersion+\"..HEAD\")\n\tcmd.Stderr = os.Stderr\n\tout, err := cmd.Output()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlines := strings.Split(string(out), \"\\n\")\n\tnewLines := make([]string, 0, len(lines))\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tnewLines = append(newLines, line)\n\t}\n\treturn newLines, nil\n}\n\nfunc insertChangesLink(contents, prevVersion, version string) (string, error) {\n\tlinksMarker := strings.Index(contents, \"(Version Links)\")\n\tif linksMarker == -1 {\n\t\treturn \"\", errors.New(\"failed to find marker for version links section\")\n\t}\n\n\tnewLine := strings.IndexByte(contents[linksMarker:], '\\n')\n\tif newLine < 0 {\n\t\treturn \"\", errors.New(\"failed to find newline after version links section\")\n\t}\n\n\tinsertAt := linksMarker + newLine + 1\n\n\tlinkBlock := fmt.Sprintf(\"[%v]: %v\\n\", version, getChangesLink(prevVersion, version))\n\tnewContents := contents[:insertAt] + linkBlock + contents[insertAt:]\n\treturn newContents, nil\n}\n\nfunc getChangesLink(prevVersion, curVersion string) string {\n\t// Example link:\n\t// https://github.com/uber/tchannel-go/compare/v1.8.0...v1.8.1\n\treturn fmt.Sprintf(\"https://github.com/uber/tchannel-go/compare/v%v...v%v\", prevVersion, curVersion)\n}\n"
  },
  {
    "path": "sockio_bsd.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build aix || dragonfly || freebsd || netbsd || openbsd || solaris\n// +build aix dragonfly freebsd netbsd openbsd solaris\n\npackage tchannel\n\nimport \"golang.org/x/sys/unix\"\n\nfunc getSendQueueLen(fd uintptr) (int, error) {\n\treturn unix.IoctlGetInt(int(fd), unix.TIOCOUTQ)\n}\n"
  },
  {
    "path": "sockio_darwin.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build darwin\n// +build darwin\n\npackage tchannel\n\nimport \"golang.org/x/sys/unix\"\n\nfunc getSendQueueLen(fd uintptr) (int, error) {\n\t// https://www.unix.com/man-page/osx/2/getsockopt/\n\treturn unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_NWRITE)\n}\n"
  },
  {
    "path": "sockio_linux.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build linux\n// +build linux\n\npackage tchannel\n\nimport \"golang.org/x/sys/unix\"\n\nfunc getSendQueueLen(fd uintptr) (int, error) {\n\t// https://linux.die.net/man/7/tcp\n\treturn unix.IoctlGetInt(int(fd), unix.SIOCOUTQ)\n}\n"
  },
  {
    "path": "sockio_non_unix.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Opposite of sockio_unix.go\n//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris\n// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris\n\npackage tchannel\n\nfunc (c *Connection) sendBufSize() (sendBufUsage int, sendBufSize int, _ error) {\n\treturn -1, -1, errNoSyscallConn\n}\n"
  },
  {
    "path": "sockio_unix.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Match the golang/sys unix file, https://github.com/golang/sys/blob/master/unix/syscall_unix.go#L5\n//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris\n// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris\n\npackage tchannel\n\nimport (\n\t\"go.uber.org/multierr\"\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc (c *Connection) sendBufSize() (sendBufUsage int, sendBufSize int, _ error) {\n\tsendBufSize = -1\n\tsendBufUsage = -1\n\n\tif c.sysConn == nil {\n\t\treturn sendBufUsage, sendBufSize, errNoSyscallConn\n\t}\n\n\tvar sendBufLenErr, sendBufLimErr error\n\terrs := c.sysConn.Control(func(fd uintptr) {\n\t\tsendBufUsage, sendBufLenErr = getSendQueueLen(fd)\n\t\tsendBufSize, sendBufLimErr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF)\n\t})\n\n\terrs = multierr.Append(errs, sendBufLimErr)\n\terrs = multierr.Append(errs, sendBufLenErr)\n\treturn sendBufUsage, sendBufSize, errs\n}\n"
  },
  {
    "path": "stats/metrickey.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage stats\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n\t\"sync\"\n)\n\n// DefaultMetricPrefix is the default mapping for metrics to statsd keys.\n// It uses a \"tchannel\" prefix for all stats.\nfunc DefaultMetricPrefix(name string, tags map[string]string) string {\n\treturn MetricWithPrefix(\"tchannel.\", name, tags)\n}\n\nvar bufPool = sync.Pool{\n\tNew: func() interface{} { return &bytes.Buffer{} },\n}\n\n// MetricWithPrefix is the default mapping for metrics to statsd keys.\nfunc MetricWithPrefix(prefix, name string, tags map[string]string) string {\n\tbuf := bufPool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\n\tif prefix != \"\" {\n\t\tbuf.WriteString(prefix)\n\t}\n\tbuf.WriteString(name)\n\n\taddKeys := make([]string, 0, 5)\n\tswitch {\n\tcase strings.HasPrefix(name, \"outbound\"):\n\t\taddKeys = append(addKeys, \"service\", \"target-service\", \"target-endpoint\")\n\t\tif strings.HasPrefix(name, \"outbound.calls.retries\") {\n\t\t\taddKeys = append(addKeys, \"retry-count\")\n\t\t}\n\tcase strings.HasPrefix(name, \"inbound\"):\n\t\taddKeys = append(addKeys, \"calling-service\", \"service\", \"endpoint\")\n\t}\n\n\tfor _, k := range addKeys {\n\t\tbuf.WriteByte('.')\n\t\tv, ok := tags[k]\n\t\tif ok {\n\t\t\twriteClean(buf, v)\n\t\t} else {\n\t\t\tbuf.WriteString(\"no-\")\n\t\t\tbuf.WriteString(k)\n\t\t}\n\t}\n\n\tm := buf.String()\n\tbufPool.Put(buf)\n\treturn m\n}\n\n// writeClean writes v, after replacing special characters [{}/\\\\:\\s.] with '-'\nfunc writeClean(buf *bytes.Buffer, v string) {\n\tfor i := 0; i < len(v); i++ {\n\t\tc := v[i]\n\t\tswitch c {\n\t\tcase '{', '}', '/', '\\\\', ':', '.', ' ', '\\t', '\\r', '\\n':\n\t\t\tbuf.WriteByte('-')\n\t\tdefault:\n\t\t\tbuf.WriteByte(c)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "stats/metrickey_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage stats\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestDefaultMetricPrefix(t *testing.T) {\n\toutboundTags := map[string]string{\n\t\t\"service\":         \"callerS\",\n\t\t\"target-service\":  \"targetS\",\n\t\t\"target-endpoint\": \"targetE\",\n\t\t\"retry-count\":     \"retryN\",\n\t}\n\tinboundTags := map[string]string{\n\t\t\"service\":         \"targetS\",\n\t\t\"endpoint\":        \"targetE\",\n\t\t\"calling-service\": \"callerS\",\n\t}\n\n\ttests := []struct {\n\t\tname     string\n\t\ttags     map[string]string\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tname:     \"outbound.calls.sent\",\n\t\t\ttags:     outboundTags,\n\t\t\texpected: \"tchannel.outbound.calls.sent.callerS.targetS.targetE\",\n\t\t},\n\t\t{\n\t\t\tname:     \"outbound.calls.retries\",\n\t\t\ttags:     outboundTags,\n\t\t\texpected: \"tchannel.outbound.calls.retries.callerS.targetS.targetE.retryN\",\n\t\t},\n\t\t{\n\t\t\tname:     \"inbound.calls.recvd\",\n\t\t\ttags:     inboundTags,\n\t\t\texpected: \"tchannel.inbound.calls.recvd.callerS.targetS.targetE\",\n\t\t},\n\t\t{\n\t\t\tname:     \"inbound.calls.recvd\",\n\t\t\ttags:     nil,\n\t\t\texpected: \"tchannel.inbound.calls.recvd.no-calling-service.no-service.no-endpoint\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tassert.Equal(t, tt.expected, DefaultMetricPrefix(tt.name, tt.tags),\n\t\t\t\"DefaultMetricPrefix(%q, %v) failed\", tt.name, tt.tags)\n\t}\n}\n\nfunc TestClean(t *testing.T) {\n\ttests := []struct {\n\t\tkey      string\n\t\texpected string\n\t}{\n\t\t{\"metric\", \"metric\"},\n\t\t{\"met:ric\", \"met-ric\"},\n\t\t{\"met{}ric\", \"met--ric\"},\n\t\t{\"\\\\metric\", \"-metric\"},\n\t\t{\"/metric\", \"-metric\"},\n\t\t{\"  met.ric  \", \"--met-ric--\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tbuf := &bytes.Buffer{}\n\t\twriteClean(buf, tt.key)\n\t\tassert.Equal(t, tt.expected, buf.String(), \"clean(%q) failed\", tt.key)\n\t}\n}\n\nfunc BenchmarkMetricPrefix(b *testing.B) {\n\toutboundTags := map[string]string{\n\t\t\"service\":         \"callerS\",\n\t\t\"target-service\":  \"targetS\",\n\t\t\"target-endpoint\": \"targetE\",\n\t\t\"retry-count\":     \"retryN\",\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tMetricWithPrefix(\"\", \"outbound.calls.retries\", outboundTags)\n\t\tDefaultMetricPrefix(\"outbound.calls.retries\", outboundTags)\n\t}\n}\n"
  },
  {
    "path": "stats/statsdreporter.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage stats\n\nimport (\n\t\"time\"\n\n\t\"github.com/cactus/go-statsd-client/statsd\"\n\t\"github.com/uber/tchannel-go\"\n)\n\nconst samplingRate = 1.0\n\n// MetricKey is called to generate the statsd key for a given metric and tags.\nvar MetricKey = DefaultMetricPrefix\n\ntype statsdReporter struct {\n\tclient statsd.Statter\n}\n\n// NewStatsdReporter returns a StatsReporter that reports to statsd on the given addr.\nfunc NewStatsdReporter(addr, prefix string) (tchannel.StatsReporter, error) {\n\tclient, err := statsd.NewBufferedClient(addr, prefix, time.Second, 0)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewStatsdReporterClient(client), nil\n}\n\n// NewStatsdReporterClient returns a StatsReporter that reports stats to the given client.\nfunc NewStatsdReporterClient(client statsd.Statter) tchannel.StatsReporter {\n\treturn &statsdReporter{client}\n}\n\nfunc (r *statsdReporter) IncCounter(name string, tags map[string]string, value int64) {\n\t// TODO(prashant): Deal with errors in the client.\n\tr.client.Inc(MetricKey(name, tags), value, samplingRate)\n}\n\nfunc (r *statsdReporter) UpdateGauge(name string, tags map[string]string, value int64) {\n\tr.client.Gauge(MetricKey(name, tags), value, samplingRate)\n}\n\nfunc (r *statsdReporter) RecordTimer(name string, tags map[string]string, d time.Duration) {\n\tr.client.TimingDuration(MetricKey(name, tags), d, samplingRate)\n}\n"
  },
  {
    "path": "stats/tally.go",
    "content": "package stats\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber-go/tally\"\n)\n\ntype wrapper struct {\n\tsync.RWMutex\n\n\tscope  tally.Scope\n\tbyTags map[knownTags]*taggedScope\n}\n\ntype knownTags struct {\n\tdest       string\n\tsource     string\n\tprocedure  string\n\tretryCount string\n}\n\ntype taggedScope struct {\n\tsync.RWMutex\n\n\tscope tally.Scope // already tagged with some set of tags\n\n\tcounters map[string]tally.Counter\n\tgauges   map[string]tally.Gauge\n\ttimers   map[string]tally.Timer\n}\n\n// NewTallyReporter takes a tally.Scope and wraps it so it ca be used as a\n// StatsReporter. The list of metrics emitted is documented on:\n// https://tchannel.readthedocs.io/en/latest/metrics/\n// The metrics emitted are similar to YARPC, the tags emitted are:\n// source, dest, procedure, and retry-count.\nfunc NewTallyReporter(scope tally.Scope) tchannel.StatsReporter {\n\treturn &wrapper{\n\t\tscope:  scope,\n\t\tbyTags: make(map[knownTags]*taggedScope),\n\t}\n}\n\nfunc (w *wrapper) IncCounter(name string, tags map[string]string, value int64) {\n\tts := w.getTaggedScope(tags)\n\tts.getCounter(name).Inc(value)\n}\n\nfunc (w *wrapper) UpdateGauge(name string, tags map[string]string, value int64) {\n\tts := w.getTaggedScope(tags)\n\tts.getGauge(name).Update(float64(value))\n}\n\nfunc (w *wrapper) RecordTimer(name string, tags map[string]string, d time.Duration) {\n\tts := w.getTaggedScope(tags)\n\tts.getTimer(name).Record(d)\n}\n\nfunc (w *wrapper) getTaggedScope(tags map[string]string) *taggedScope {\n\tkt := convertTags(tags)\n\n\tw.RLock()\n\tts, ok := w.byTags[kt]\n\tw.RUnlock()\n\tif ok {\n\t\treturn ts\n\t}\n\n\tw.Lock()\n\tdefer w.Unlock()\n\n\t// Always double-check under the write-lock.\n\tif ts, ok := w.byTags[kt]; ok {\n\t\treturn ts\n\t}\n\n\tts = &taggedScope{\n\t\tscope:    w.scope.Tagged(kt.tallyTags()),\n\t\tcounters: make(map[string]tally.Counter),\n\t\tgauges:   make(map[string]tally.Gauge),\n\t\ttimers:   make(map[string]tally.Timer),\n\t}\n\tw.byTags[kt] = ts\n\treturn ts\n}\n\nfunc convertTags(tags map[string]string) knownTags {\n\tif ts, ok := tags[\"target-service\"]; ok {\n\t\t// Outbound call.\n\t\treturn knownTags{\n\t\t\tdest:       ts,\n\t\t\tsource:     tags[\"service\"],\n\t\t\tprocedure:  tags[\"target-endpoint\"],\n\t\t\tretryCount: tags[\"retry-count\"],\n\t\t}\n\t}\n\n\tif cs, ok := tags[\"calling-service\"]; ok {\n\t\t// Inbound call.\n\t\treturn knownTags{\n\t\t\tdest:       tags[\"service\"],\n\t\t\tsource:     cs,\n\t\t\tprocedure:  tags[\"endpoint\"],\n\t\t\tretryCount: tags[\"retry-count\"],\n\t\t}\n\t}\n\n\t// TChannel doesn't use any other tags, so ignore all others for now.\n\treturn knownTags{}\n}\n\n// Create a sub-scope for this set of known tags.\nfunc (kt knownTags) tallyTags() map[string]string {\n\ttallyTags := make(map[string]string, 5)\n\n\tif kt.dest != \"\" {\n\t\ttallyTags[\"dest\"] = kt.dest\n\t}\n\tif kt.source != \"\" {\n\t\ttallyTags[\"source\"] = kt.source\n\t}\n\tif kt.procedure != \"\" {\n\t\ttallyTags[\"procedure\"] = kt.procedure\n\t}\n\tif kt.retryCount != \"\" {\n\t\ttallyTags[\"retry-count\"] = kt.retryCount\n\t}\n\n\treturn tallyTags\n}\n\nfunc (ts *taggedScope) getCounter(name string) tally.Counter {\n\tts.RLock()\n\tcounter, ok := ts.counters[name]\n\tts.RUnlock()\n\tif ok {\n\t\treturn counter\n\t}\n\n\tts.Lock()\n\tdefer ts.Unlock()\n\n\t// No double-check under the lock, as overwriting the counter has\n\t// no impact.\n\tcounter = ts.scope.Counter(name)\n\tts.counters[name] = counter\n\treturn counter\n}\n\nfunc (ts *taggedScope) getGauge(name string) tally.Gauge {\n\tts.RLock()\n\tgauge, ok := ts.gauges[name]\n\tts.RUnlock()\n\tif ok {\n\t\treturn gauge\n\t}\n\n\tts.Lock()\n\tdefer ts.Unlock()\n\n\t// No double-check under the lock, as overwriting the counter has\n\t// no impact.\n\tgauge = ts.scope.Gauge(name)\n\tts.gauges[name] = gauge\n\treturn gauge\n}\n\nfunc (ts *taggedScope) getTimer(name string) tally.Timer {\n\tts.RLock()\n\ttimer, ok := ts.timers[name]\n\tts.RUnlock()\n\tif ok {\n\t\treturn timer\n\t}\n\n\tts.Lock()\n\tdefer ts.Unlock()\n\n\t// No double-check under the lock, as overwriting the counter has\n\t// no impact.\n\ttimer = ts.scope.Timer(name)\n\tts.timers[name] = timer\n\treturn timer\n}\n"
  },
  {
    "path": "stats/tally_test.go",
    "content": "package stats\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/uber-go/tally\"\n\t\"github.com/uber/tchannel-go/testutils\"\n)\n\nfunc TestConvertTags(t *testing.T) {\n\ttests := []struct {\n\t\ttags map[string]string\n\t\twant map[string]string\n\t}{\n\t\t{\n\t\t\ttags: nil,\n\t\t\twant: map[string]string{},\n\t\t},\n\t\t{\n\t\t\t// unknown tags are ignored.\n\t\t\ttags: map[string]string{\"foo\": \"bar\"},\n\t\t\twant: map[string]string{},\n\t\t},\n\t\t{\n\t\t\t// Outbound call\n\t\t\ttags: map[string]string{\n\t\t\t\t\"target-service\":  \"tsvc\",\n\t\t\t\t\"service\":         \"foo\",\n\t\t\t\t\"target-endpoint\": \"te\",\n\t\t\t\t\"retry-count\":     \"4\",\n\n\t\t\t\t// ignored tag\n\t\t\t\t\"foo\": \"bar\",\n\t\t\t},\n\t\t\twant: map[string]string{\n\t\t\t\t\"dest\":        \"tsvc\",\n\t\t\t\t\"source\":      \"foo\",\n\t\t\t\t\"procedure\":   \"te\",\n\t\t\t\t\"retry-count\": \"4\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// Inbound call\n\t\t\ttags: map[string]string{\n\t\t\t\t\"service\":         \"foo\",\n\t\t\t\t\"calling-service\": \"bar\",\n\t\t\t\t\"endpoint\":        \"ep\",\n\t\t\t},\n\t\t\twant: map[string]string{\n\t\t\t\t\"dest\":      \"foo\",\n\t\t\t\t\"source\":    \"bar\",\n\t\t\t\t\"procedure\": \"ep\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(fmt.Sprint(tt.tags), func(t *testing.T) {\n\t\t\tgot := convertTags(tt.tags)\n\t\t\tassert.Equal(t, tt.want, got.tallyTags())\n\t\t})\n\t}\n}\n\nfunc TestNewTallyReporter(t *testing.T) {\n\twant := tally.NewTestScope(\"\" /* prefix */, nil /* tags */)\n\tscope := tally.NewTestScope(\"\" /* prefix */, nil /* tags */)\n\twrapped := NewTallyReporter(scope)\n\n\tfor i := 0; i < 10; i++ {\n\t\twrapped.IncCounter(\"outbound.calls\", map[string]string{\n\t\t\t\"target-service\":  \"tsvc\",\n\t\t\t\"service\":         \"foo\",\n\t\t\t\"target-endpoint\": \"te\",\n\t\t}, 3)\n\t\twant.Tagged(map[string]string{\n\t\t\t\"dest\":      \"tsvc\",\n\t\t\t\"source\":    \"foo\",\n\t\t\t\"procedure\": \"te\",\n\t\t}).Counter(\"outbound.calls\").Inc(3)\n\n\t\twrapped.UpdateGauge(\"num-connections\", map[string]string{\n\t\t\t\"service\": \"foo\",\n\t\t}, 3)\n\t\twant.Gauge(\"num-connections\").Update(3)\n\n\t\twrapped.RecordTimer(\"inbound.call.latency\", map[string]string{\n\t\t\t\"service\":         \"foo\",\n\t\t\t\"calling-service\": \"bar\",\n\t\t\t\"endpoint\":        \"ep\",\n\t\t}, time.Second)\n\t\twant.Tagged(map[string]string{\n\t\t\t\"dest\":      \"foo\",\n\t\t\t\"source\":    \"bar\",\n\t\t\t\"procedure\": \"ep\",\n\t\t}).Timer(\"inbound.call.latency\").Record(time.Second)\n\t}\n\n\tassert.Equal(t, want.Snapshot(), scope.Snapshot())\n}\n\nfunc TestTallyIntegration(t *testing.T) {\n\tclientScope := tally.NewTestScope(\"\" /* prefix */, nil /* tags */)\n\tserverScope := tally.NewTestScope(\"\" /* prefix */, nil /* tags */)\n\n\t// Verify the tagged metrics from that call.\n\ttests := []struct {\n\t\tmsg      string\n\t\tscope    tally.TestScope\n\t\tcounters []string\n\t\ttimers   []string\n\t}{\n\t\t{\n\t\t\tmsg:   \"client metrics\",\n\t\t\tscope: clientScope,\n\t\t\tcounters: []string{\n\t\t\t\t\"outbound.calls.send+dest=testService,procedure=echo,source=testService-client\",\n\t\t\t\t\"outbound.calls.success+dest=testService,procedure=echo,source=testService-client\",\n\t\t\t},\n\t\t\ttimers: []string{\n\t\t\t\t\"outbound.calls.per-attempt.latency+dest=testService,procedure=echo,source=testService-client\",\n\t\t\t\t\"outbound.calls.latency+dest=testService,procedure=echo,source=testService-client\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg:   \"server metrics\",\n\t\t\tscope: serverScope,\n\t\t\tcounters: []string{\n\t\t\t\t\"inbound.calls.recvd+dest=testService,procedure=echo,source=testService-client\",\n\t\t\t\t\"inbound.calls.success+dest=testService,procedure=echo,source=testService-client\",\n\t\t\t},\n\t\t\ttimers: []string{\n\t\t\t\t\"inbound.calls.latency+dest=testService,procedure=echo,source=testService-client\",\n\t\t\t},\n\t\t},\n\t}\n\n\t// Use a closure so that the server/client are closed before we verify metrics.\n\t// Otherwise, we may attempt to verify metrics before they've been flushed by TChannel.\n\tfunc() {\n\t\tserver := testutils.NewServer(t, testutils.NewOpts().SetStatsReporter(NewTallyReporter(serverScope)))\n\t\tdefer server.Close()\n\t\ttestutils.RegisterEcho(server, nil)\n\n\t\tclient := testutils.NewClient(t, testutils.NewOpts().SetStatsReporter(NewTallyReporter(clientScope)))\n\t\tdefer client.Close()\n\n\t\ttestutils.AssertEcho(t, client, server.PeerInfo().HostPort, server.ServiceName())\n\t}()\n\n\tfor _, tt := range tests {\n\t\tsnapshot := tt.scope.Snapshot()\n\t\tfor _, counter := range tt.counters {\n\t\t\tassert.Contains(t, snapshot.Counters(), counter, \"missing counter\")\n\t\t}\n\t\tfor _, timer := range tt.timers {\n\t\t\tassert.Contains(t, snapshot.Timers(), timer, \"missing timer\")\n\t\t}\n\t}\n}\n\nfunc BenchmarkTallyCounter(b *testing.B) {\n\tscope := tally.NewTestScope(\"\" /* prefix */, nil /* tags */)\n\twrapped := NewTallyReporter(scope)\n\n\ttags := map[string]string{\n\t\t\"target-service\":  \"tsvc\",\n\t\t\"service\":         \"foo\",\n\t\t\"target-endpoint\": \"te\",\n\t}\n\tfor i := 0; i < b.N; i++ {\n\t\twrapped.IncCounter(\"outbound.calls\", tags, 1)\n\t}\n}\n"
  },
  {
    "path": "stats.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"log\"\n\t\"time\"\n)\n\n// StatsReporter is the the interface used to report stats.\ntype StatsReporter interface {\n\tIncCounter(name string, tags map[string]string, value int64)\n\tUpdateGauge(name string, tags map[string]string, value int64)\n\tRecordTimer(name string, tags map[string]string, d time.Duration)\n}\n\n// NullStatsReporter is a stats reporter that discards the statistics.\nvar NullStatsReporter StatsReporter = nullStatsReporter{}\n\ntype nullStatsReporter struct{}\n\nfunc (nullStatsReporter) IncCounter(name string, tags map[string]string, value int64)      {}\nfunc (nullStatsReporter) UpdateGauge(name string, tags map[string]string, value int64)     {}\nfunc (nullStatsReporter) RecordTimer(name string, tags map[string]string, d time.Duration) {}\n\n// SimpleStatsReporter is a stats reporter that reports stats to the log.\nvar SimpleStatsReporter StatsReporter = simpleStatsReporter{}\n\ntype simpleStatsReporter struct {\n\tcommonTags map[string]string\n}\n\nfunc (simpleStatsReporter) IncCounter(name string, tags map[string]string, value int64) {\n\tlog.Printf(\"Stats: IncCounter(%v, %v) +%v\", name, tags, value)\n}\n\nfunc (simpleStatsReporter) UpdateGauge(name string, tags map[string]string, value int64) {\n\tlog.Printf(\"Stats: UpdateGauge(%v, %v) = %v\", name, tags, value)\n}\n\nfunc (simpleStatsReporter) RecordTimer(name string, tags map[string]string, d time.Duration) {\n\tlog.Printf(\"Stats: RecordTimer(%v, %v) = %v\", name, tags, d)\n}\n"
  },
  {
    "path": "stats_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\nfunc tagsForOutboundCall(serverCh *Channel, clientCh *Channel, method string) map[string]string {\n\thost, _ := os.Hostname()\n\treturn map[string]string{\n\t\t\"app\":             clientCh.PeerInfo().ProcessName,\n\t\t\"host\":            host,\n\t\t\"service\":         clientCh.PeerInfo().ServiceName,\n\t\t\"target-service\":  serverCh.PeerInfo().ServiceName,\n\t\t\"target-endpoint\": method,\n\t}\n}\n\nfunc tagsForInboundCall(serverCh *Channel, clientCh *Channel, method string) map[string]string {\n\thost, _ := os.Hostname()\n\treturn map[string]string{\n\t\t\"app\":             serverCh.PeerInfo().ProcessName,\n\t\t\"host\":            host,\n\t\t\"service\":         serverCh.PeerInfo().ServiceName,\n\t\t\"calling-service\": clientCh.PeerInfo().ServiceName,\n\t\t\"endpoint\":        method,\n\t}\n}\n\n// statsHandler increments the server and client timers when handling requests.\ntype statsHandler struct {\n\t*testHandler\n\tclientClock *testutils.StubClock\n\tserverClock *testutils.StubClock\n}\n\nfunc (h *statsHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\th.clientClock.Elapse(100 * time.Millisecond)\n\th.serverClock.Elapse(70 * time.Millisecond)\n\treturn h.testHandler.Handle(ctx, args)\n}\n\nfunc TestStatsCalls(t *testing.T) {\n\tdefer testutils.SetTimeout(t, 2*time.Second)()\n\n\ttests := []struct {\n\t\tmethod  string\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tmethod: \"echo\",\n\t\t},\n\t\t{\n\t\t\tmethod:  \"app-error\",\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tinitialTime := time.Date(2015, 2, 1, 10, 10, 0, 0, time.UTC)\n\t\tclientClock := testutils.NewStubClock(initialTime)\n\t\tserverClock := testutils.NewStubClock(initialTime)\n\t\thandler := &statsHandler{\n\t\t\ttestHandler: newTestHandler(t),\n\t\t\tclientClock: clientClock,\n\t\t\tserverClock: serverClock,\n\t\t}\n\n\t\tclientStats := newRecordingStatsReporter()\n\t\tserverStats := newRecordingStatsReporter()\n\t\tserverOpts := testutils.NewOpts().\n\t\t\tSetStatsReporter(serverStats).\n\t\t\tSetTimeNow(serverClock.Now)\n\t\tWithVerifiedServer(t, serverOpts, func(serverCh *Channel, hostPort string) {\n\t\t\thandler := raw.Wrap(handler)\n\t\t\tserverCh.Register(handler, \"echo\")\n\t\t\tserverCh.Register(handler, \"app-error\")\n\n\t\t\tch := testutils.NewClient(t, testutils.NewOpts().\n\t\t\t\tSetStatsReporter(clientStats).\n\t\t\t\tSetTimeNow(clientClock.Now))\n\t\t\tdefer ch.Close()\n\n\t\t\tctx, cancel := NewContext(time.Second * 5)\n\t\t\tdefer cancel()\n\n\t\t\t_, _, resp, err := raw.Call(ctx, ch, hostPort, testutils.DefaultServerName, tt.method, nil, nil)\n\t\t\trequire.NoError(t, err, \"Call(%v) should fail\", tt.method)\n\t\t\tassert.Equal(t, tt.wantErr, resp.ApplicationError(), \"Call(%v) check application error\")\n\n\t\t\toutboundTags := tagsForOutboundCall(serverCh, ch, tt.method)\n\t\t\tinboundTags := tagsForInboundCall(serverCh, ch, tt.method)\n\n\t\t\tclientStats.Expected.IncCounter(\"outbound.calls.send\", outboundTags, 1)\n\t\t\tclientStats.Expected.RecordTimer(\"outbound.calls.per-attempt.latency\", outboundTags, 100*time.Millisecond)\n\t\t\tclientStats.Expected.RecordTimer(\"outbound.calls.latency\", outboundTags, 100*time.Millisecond)\n\t\t\tserverStats.Expected.IncCounter(\"inbound.calls.recvd\", inboundTags, 1)\n\t\t\tserverStats.Expected.RecordTimer(\"inbound.calls.latency\", inboundTags, 70*time.Millisecond)\n\n\t\t\tif tt.wantErr {\n\t\t\t\tclientStats.Expected.IncCounter(\"outbound.calls.per-attempt.app-errors\", outboundTags, 1)\n\t\t\t\tclientStats.Expected.IncCounter(\"outbound.calls.app-errors\", outboundTags, 1)\n\t\t\t\tserverStats.Expected.IncCounter(\"inbound.calls.app-errors\", inboundTags, 1)\n\t\t\t} else {\n\t\t\t\tclientStats.Expected.IncCounter(\"outbound.calls.success\", outboundTags, 1)\n\t\t\t\tserverStats.Expected.IncCounter(\"inbound.calls.success\", inboundTags, 1)\n\t\t\t}\n\t\t})\n\n\t\tclientStats.Validate(t)\n\t\tserverStats.Validate(t)\n\t}\n}\n\nfunc TestStatsWithRetries(t *testing.T) {\n\tdefer testutils.SetTimeout(t, 2*time.Second)()\n\ta := testutils.DurationArray\n\n\tinitialTime := time.Date(2015, 2, 1, 10, 10, 0, 0, time.UTC)\n\tclientClock := testutils.NewStubClock(initialTime)\n\n\tclientStats := newRecordingStatsReporter()\n\tch := testutils.NewClient(t, testutils.NewOpts().\n\t\tSetStatsReporter(clientStats).\n\t\tSetTimeNow(clientClock.Now))\n\tdefer ch.Close()\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\t// TODO why do we need this??\n\topts := testutils.NewOpts().NoRelay()\n\tWithVerifiedServer(t, opts, func(serverCh *Channel, hostPort string) {\n\t\tconst (\n\t\t\tperAttemptServer = 10 * time.Millisecond\n\t\t\tperAttemptClient = time.Millisecond\n\t\t\tperAttemptTotal  = perAttemptServer + perAttemptClient\n\t\t)\n\n\t\trespErr := make(chan error, 1)\n\t\ttestutils.RegisterFunc(serverCh, \"req\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\tclientClock.Elapse(perAttemptServer)\n\t\t\treturn &raw.Res{Arg2: args.Arg2, Arg3: args.Arg3}, <-respErr\n\t\t})\n\t\tch.Peers().Add(serverCh.PeerInfo().HostPort)\n\n\t\ttests := []struct {\n\t\t\texpectErr           error\n\t\t\tnumFailures         int\n\t\t\tnumAttempts         int\n\t\t\toverallLatency      time.Duration\n\t\t\tperAttemptLatencies []time.Duration\n\t\t}{\n\t\t\t{\n\t\t\t\tnumFailures:         0,\n\t\t\t\tnumAttempts:         1,\n\t\t\t\tperAttemptLatencies: a(perAttemptServer),\n\t\t\t\toverallLatency:      perAttemptTotal,\n\t\t\t},\n\t\t\t{\n\t\t\t\tnumFailures:         1,\n\t\t\t\tnumAttempts:         2,\n\t\t\t\tperAttemptLatencies: a(perAttemptServer, perAttemptServer),\n\t\t\t\toverallLatency:      2 * perAttemptTotal,\n\t\t\t},\n\t\t\t{\n\t\t\t\tnumFailures:         4,\n\t\t\t\tnumAttempts:         5,\n\t\t\t\tperAttemptLatencies: a(perAttemptServer, perAttemptServer, perAttemptServer, perAttemptServer, perAttemptServer),\n\t\t\t\toverallLatency:      5 * perAttemptTotal,\n\t\t\t},\n\t\t\t{\n\t\t\t\tnumFailures:         5,\n\t\t\t\tnumAttempts:         5,\n\t\t\t\texpectErr:           ErrServerBusy,\n\t\t\t\tperAttemptLatencies: a(perAttemptServer, perAttemptServer, perAttemptServer, perAttemptServer, perAttemptServer),\n\t\t\t\toverallLatency:      5 * perAttemptTotal,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tclientStats.Reset()\n\t\t\terr := ch.RunWithRetry(ctx, func(ctx context.Context, rs *RequestState) error {\n\t\t\t\tclientClock.Elapse(perAttemptClient)\n\t\t\t\tif rs.Attempt > tt.numFailures {\n\t\t\t\t\trespErr <- nil\n\t\t\t\t} else {\n\t\t\t\t\trespErr <- ErrServerBusy\n\t\t\t\t}\n\n\t\t\t\tsc := ch.GetSubChannel(serverCh.ServiceName())\n\t\t\t\t_, err := raw.CallV2(ctx, sc, raw.CArgs{\n\t\t\t\t\tMethod:      \"req\",\n\t\t\t\t\tCallOptions: &CallOptions{RequestState: rs},\n\t\t\t\t})\n\t\t\t\treturn err\n\t\t\t})\n\t\t\tassert.Equal(t, tt.expectErr, err, \"RunWithRetry unexpected error\")\n\n\t\t\toutboundTags := tagsForOutboundCall(serverCh, ch, \"req\")\n\t\t\tif tt.expectErr == nil {\n\t\t\t\tclientStats.Expected.IncCounter(\"outbound.calls.success\", outboundTags, 1)\n\t\t\t}\n\t\t\tclientStats.Expected.IncCounter(\"outbound.calls.send\", outboundTags, int64(tt.numAttempts))\n\t\t\tfor i, latency := range tt.perAttemptLatencies {\n\t\t\t\tclientStats.Expected.RecordTimer(\"outbound.calls.per-attempt.latency\", outboundTags, latency)\n\t\t\t\tif i > 0 {\n\t\t\t\t\ttags := tagsForOutboundCall(serverCh, ch, \"req\")\n\t\t\t\t\ttags[\"retry-count\"] = fmt.Sprint(i)\n\t\t\t\t\tclientStats.Expected.IncCounter(\"outbound.calls.retries\", tags, 1)\n\t\t\t\t}\n\t\t\t}\n\t\t\tclientStats.Expected.RecordTimer(\"outbound.calls.latency\", outboundTags, tt.overallLatency)\n\t\t\tclientStats.Validate(t)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "stats_utils_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\n// This file contains test setup logic, and is named with a _test.go suffix to\n// ensure it's only compiled with tests.\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype statsValue struct {\n\t// count is the counter value if this metric is a counter.\n\tcount int64\n\n\t// timers is the list of timer values if this metrics is a timer.\n\ttimers []time.Duration\n}\n\ntype recordingStatsReporter struct {\n\tsync.Mutex\n\n\t// Values is a map from the metricName -> map[tagMapAsString]*statsValue\n\tValues map[string]map[string]*statsValue\n\n\t// Expected stores expected counter values.\n\tExpected *recordingStatsReporter\n}\n\nfunc newRecordingStatsReporter() *recordingStatsReporter {\n\treturn &recordingStatsReporter{\n\t\tValues: make(map[string]map[string]*statsValue),\n\t\tExpected: &recordingStatsReporter{\n\t\t\tValues: make(map[string]map[string]*statsValue),\n\t\t},\n\t}\n}\n\n// keysMap returns the keys of the given map as a sorted list of strings.\n// If the map is not of the type map[string]* then the function will panic.\nfunc keysMap(m interface{}) []string {\n\tvar keys []string\n\tmapKeys := reflect.ValueOf(m).MapKeys()\n\tfor _, v := range mapKeys {\n\t\tkeys = append(keys, v.Interface().(string))\n\t}\n\tsort.Strings(keys)\n\treturn keys\n}\n\n// tagsToString converts a map of tags to a string that can be used as a map key.\nfunc tagsToString(tags map[string]string) string {\n\tvar vals []string\n\tfor _, k := range keysMap(tags) {\n\t\tvals = append(vals, fmt.Sprintf(\"%v = %v\", k, tags[k]))\n\t}\n\treturn strings.Join(vals, \", \")\n}\n\nfunc (r *recordingStatsReporter) getStat(name string, tags map[string]string) *statsValue {\n\tr.Lock()\n\tdefer r.Unlock()\n\n\ttagMap, ok := r.Values[name]\n\tif !ok {\n\t\ttagMap = make(map[string]*statsValue)\n\t\tr.Values[name] = tagMap\n\t}\n\n\ttagStr := tagsToString(tags)\n\tstatVal, ok := tagMap[tagStr]\n\tif !ok {\n\t\tstatVal = &statsValue{}\n\t\ttagMap[tagStr] = statVal\n\t}\n\n\treturn statVal\n}\n\nfunc (r *recordingStatsReporter) IncCounter(name string, tags map[string]string, value int64) {\n\tstatVal := r.getStat(name, tags)\n\tstatVal.count += value\n}\n\nfunc (r *recordingStatsReporter) RecordTimer(name string, tags map[string]string, d time.Duration) {\n\tstatVal := r.getStat(name, tags)\n\tstatVal.timers = append(statVal.timers, d)\n}\n\nfunc (r *recordingStatsReporter) Reset() {\n\tnewReporter := newRecordingStatsReporter()\n\tr.Values = newReporter.Values\n\tr.Expected = newReporter.Expected\n}\n\nfunc (r *recordingStatsReporter) Validate(t *testing.T) {\n\tr.Lock()\n\tdefer r.Unlock()\n\n\tassert.Equal(t, keysMap(r.Expected.Values), keysMap(r.Values),\n\t\t\"Metric keys are different\")\n\tr.validateExpectedLocked(t)\n}\n\n// ValidateExpected only validates metrics added to expected rather than all recorded metrics.\nfunc (r *recordingStatsReporter) ValidateExpected(t testing.TB) {\n\tr.Lock()\n\tdefer r.Unlock()\n\n\tr.validateExpectedLocked(t)\n}\n\nfunc (r *recordingStatsReporter) EnsureNotPresent(t testing.TB, counter string) {\n\tr.Lock()\n\tdefer r.Unlock()\n\n\tassert.NotContains(t, r.Values, counter, \"metric should not be present\")\n}\n\nfunc (r *recordingStatsReporter) validateExpectedLocked(t testing.TB) {\n\tfor counterKey, expectedCounter := range r.Expected.Values {\n\t\tcounter, ok := r.Values[counterKey]\n\t\tif !assert.True(t, ok, \"expected %v not found\", counterKey) {\n\t\t\tcontinue\n\t\t}\n\n\t\tassert.Equal(t, keysMap(expectedCounter), keysMap(counter),\n\t\t\t\"Metric %v has different reported tags\", counterKey)\n\t\tfor tags, stat := range counter {\n\t\t\texpectedStat, ok := expectedCounter[tags]\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tassert.Equal(t, expectedStat, stat,\n\t\t\t\t\"Metric %v with tags %v has mismatched value\", counterKey, tags)\n\t\t}\n\t}\n}\n\nfunc (r *recordingStatsReporter) UpdateGauge(name string, tags map[string]string, value int64) {}\n"
  },
  {
    "path": "stream_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\nconst (\n\tstreamRequestError = byte(255)\n\tstreamRequestClose = byte(254)\n)\n\nfunc makeRepeatedBytes(n byte) []byte {\n\tdata := make([]byte, int(n))\n\tfor i := byte(0); i < n; i++ {\n\t\tdata[i] = n\n\t}\n\treturn data\n}\n\nfunc writeFlushBytes(w ArgWriter, bs []byte) error {\n\tif _, err := w.Write(bs); err != nil {\n\t\treturn err\n\t}\n\treturn w.Flush()\n}\n\ntype streamHelper struct {\n\tt testing.TB\n}\n\n// startCall starts a call to echoStream and returns the arg3 reader and writer.\nfunc (h streamHelper) startCall(ctx context.Context, ch *Channel, hostPort, serviceName string) (ArgWriter, ArgReader) {\n\tcall, err := ch.BeginCall(ctx, hostPort, serviceName, \"echoStream\", nil)\n\trequire.NoError(h.t, err, \"BeginCall to echoStream failed\")\n\n\t// Write empty headers\n\trequire.NoError(h.t, NewArgWriter(call.Arg2Writer()).Write(nil), \"Write empty headers failed\")\n\n\t// Flush arg3 to force the call to start without any arg3.\n\twriter, err := call.Arg3Writer()\n\trequire.NoError(h.t, err, \"Arg3Writer failed\")\n\trequire.NoError(h.t, writer.Flush(), \"Arg3Writer flush failed\")\n\n\t// Read empty Headers\n\tresponse := call.Response()\n\tvar arg2 []byte\n\trequire.NoError(h.t, NewArgReader(response.Arg2Reader()).Read(&arg2), \"Read headers failed\")\n\trequire.False(h.t, response.ApplicationError(), \"echoStream failed due to application error\")\n\n\treader, err := response.Arg3Reader()\n\trequire.NoError(h.t, err, \"Arg3Reader failed\")\n\n\treturn writer, reader\n}\n\n// streamPartialHandler returns a streaming handler that has the following contract:\n// read a byte, write N bytes where N = the byte that was read.\n// The results are be written as soon as the byte is read.\nfunc streamPartialHandler(t testing.TB, reportErrors bool) HandlerFunc {\n\treturn func(ctx context.Context, call *InboundCall) {\n\t\tresponse := call.Response()\n\t\tonError := func(err error) {\n\t\t\tif reportErrors {\n\t\t\t\tt.Errorf(\"Handler error: %v\", err)\n\t\t\t}\n\t\t\tresponse.SendSystemError(fmt.Errorf(\"failed to read arg2\"))\n\t\t}\n\n\t\tvar arg2 []byte\n\t\tif err := NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil {\n\t\t\tonError(fmt.Errorf(\"failed to read arg2\"))\n\t\t\treturn\n\t\t}\n\n\t\tif err := NewArgWriter(response.Arg2Writer()).Write(nil); err != nil {\n\t\t\tonError(fmt.Errorf(\"\"))\n\t\t\treturn\n\t\t}\n\n\t\targReader, err := call.Arg3Reader()\n\t\tif err != nil {\n\t\t\tonError(fmt.Errorf(\"failed to read arg3\"))\n\t\t\treturn\n\t\t}\n\n\t\targWriter, err := response.Arg3Writer()\n\t\tif err != nil {\n\t\t\tonError(fmt.Errorf(\"arg3 writer failed\"))\n\t\t\treturn\n\t\t}\n\n\t\t// Flush arg3 which will force a frame with just arg2 to be sent.\n\t\t// The test reads arg2 before arg3 has been sent.\n\t\tif err := argWriter.Flush(); err != nil {\n\t\t\tonError(fmt.Errorf(\"arg3 flush failed\"))\n\t\t\treturn\n\t\t}\n\n\t\targ3 := make([]byte, 1)\n\t\tfor {\n\t\t\tn, err := argReader.Read(arg3)\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif n == 0 && err == nil {\n\t\t\t\terr = fmt.Errorf(\"read 0 bytes\")\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tonError(fmt.Errorf(\"arg3 Read failed: %v\", err))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Magic number to cause a failure\n\t\t\tif arg3[0] == streamRequestError {\n\t\t\t\t// Make sure that the reader is closed.\n\t\t\t\tif err := argReader.Close(); err != nil {\n\t\t\t\t\tonError(fmt.Errorf(\"request error failed to close argReader: %v\", err))\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tresponse.SendSystemError(errors.New(\"intentional failure\"))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif arg3[0] == streamRequestClose {\n\t\t\t\tif err := argWriter.Close(); err != nil {\n\t\t\t\t\tonError(err)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Write the number of bytes as specified by arg3[0]\n\t\t\tif _, err := argWriter.Write(makeRepeatedBytes(arg3[0])); err != nil {\n\t\t\t\tonError(fmt.Errorf(\"argWriter Write failed: %v\", err))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err := argWriter.Flush(); err != nil {\n\t\t\t\tonError(fmt.Errorf(\"argWriter flush failed: %v\", err))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif err := argReader.Close(); err != nil {\n\t\t\tonError(fmt.Errorf(\"argReader Close failed: %v\", err))\n\t\t\treturn\n\t\t}\n\n\t\tif err := argWriter.Close(); err != nil {\n\t\t\tonError(fmt.Errorf(\"arg3writer Close failed: %v\", err))\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc testStreamArg(t *testing.T, f func(argWriter ArgWriter, argReader ArgReader)) {\n\tdefer testutils.SetTimeout(t, 2*time.Second)()\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\thelper := streamHelper{t}\n\tWithVerifiedServer(t, nil, func(ch *Channel, hostPort string) {\n\t\tch.Register(streamPartialHandler(t, true /* report errors */), \"echoStream\")\n\n\t\targWriter, argReader := helper.startCall(ctx, ch, hostPort, ch.ServiceName())\n\t\tverifyBytes := func(n byte) {\n\t\t\trequire.NoError(t, writeFlushBytes(argWriter, []byte{n}), \"arg3 write failed\")\n\n\t\t\targ3 := make([]byte, int(n))\n\t\t\t_, err := io.ReadFull(argReader, arg3)\n\t\t\trequire.NoError(t, err, \"arg3 read failed\")\n\n\t\t\tassert.Equal(t, makeRepeatedBytes(n), arg3, \"arg3 result mismatch\")\n\t\t}\n\n\t\tverifyBytes(0)\n\t\tverifyBytes(5)\n\t\tverifyBytes(100)\n\t\tverifyBytes(1)\n\n\t\tf(argWriter, argReader)\n\t})\n}\n\nfunc TestStreamPartialArg(t *testing.T) {\n\ttestStreamArg(t, func(argWriter ArgWriter, argReader ArgReader) {\n\t\trequire.NoError(t, argWriter.Close(), \"arg3 close failed\")\n\n\t\t// Once closed, we expect the reader to return EOF\n\t\tn, err := io.Copy(ioutil.Discard, argReader)\n\t\tassert.Equal(t, int64(0), n, \"arg2 reader expected to EOF after arg3 writer is closed\")\n\t\tassert.NoError(t, err, \"Copy should not fail\")\n\t\tassert.NoError(t, argReader.Close(), \"close arg reader failed\")\n\t})\n}\n\nfunc TestStreamSendError(t *testing.T) {\n\ttestStreamArg(t, func(argWriter ArgWriter, argReader ArgReader) {\n\t\t// Send the magic number to request an error.\n\t\t_, err := argWriter.Write([]byte{streamRequestError})\n\t\trequire.NoError(t, err, \"arg3 write failed\")\n\t\trequire.NoError(t, argWriter.Close(), \"arg3 close failed\")\n\n\t\t// Now we expect an error on our next read.\n\t\t_, err = ioutil.ReadAll(argReader)\n\t\tassert.Error(t, err, \"ReadAll should fail\")\n\t\tassert.True(t, strings.Contains(err.Error(), \"intentional failure\"), \"err %v unexpected\", err)\n\t})\n}\n\nfunc TestStreamCancelled(t *testing.T) {\n\t// Since the cancel message is unimplemented, the relay does not know that the\n\t// call was cancelled, andwill block closing till the timeout.\n\topts := testutils.NewOpts().NoRelay()\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(streamPartialHandler(t, false /* report errors */), \"echoStream\")\n\n\t\tctx, cancel := NewContext(testutils.Timeout(time.Second))\n\t\tdefer cancel()\n\n\t\thelper := streamHelper{t}\n\t\tclient := ts.NewClient(nil)\n\t\tcancelContext := make(chan struct{})\n\n\t\targ3Writer, arg3Reader := helper.startCall(ctx, client, ts.HostPort(), ts.ServiceName())\n\t\tgo func() {\n\t\t\tfor i := 0; i < 10; i++ {\n\t\t\t\tassert.NoError(t, writeFlushBytes(arg3Writer, []byte{1}), \"Write failed\")\n\t\t\t}\n\n\t\t\t// Our reads and writes should fail now.\n\t\t\t<-cancelContext\n\t\t\tcancel()\n\n\t\t\t_, err := arg3Writer.Write([]byte{1})\n\t\t\t// The write will succeed since it's buffered.\n\t\t\tassert.NoError(t, err, \"Write after fail should be buffered\")\n\t\t\tassert.Error(t, arg3Writer.Flush(), \"writer.Flush should fail after cancel\")\n\t\t\tassert.Error(t, arg3Writer.Close(), \"writer.Close should fail after cancel\")\n\t\t}()\n\n\t\tfor i := 0; i < 10; i++ {\n\t\t\targ3 := make([]byte, 1)\n\t\t\tn, err := arg3Reader.Read(arg3)\n\t\t\tassert.Equal(t, 1, n, \"Read did not correct number of bytes\")\n\t\t\tassert.NoError(t, err, \"Read failed\")\n\t\t}\n\n\t\tclose(cancelContext)\n\n\t\tn, err := io.Copy(ioutil.Discard, arg3Reader)\n\t\tassert.EqualValues(t, 0, n, \"Read should not read any bytes after cancel\")\n\t\tassert.Error(t, err, \"Read should fail after cancel\")\n\t\tassert.Error(t, arg3Reader.Close(), \"reader.Close should fail after cancel\")\n\n\t\t// Close the client to clear out the pending exchange. Otherwise the test\n\t\t// waits for the timeout, causing a slowdown.\n\t\tclient.Close()\n\t})\n}\n\nfunc TestResponseClosedBeforeRequest(t *testing.T) {\n\ttestutils.WithTestServer(t, nil, func(t testing.TB, ts *testutils.TestServer) {\n\t\tts.Register(streamPartialHandler(t, false /* report errors */), \"echoStream\")\n\n\t\tctx, cancel := NewContext(testutils.Timeout(time.Second))\n\t\tdefer cancel()\n\n\t\thelper := streamHelper{t}\n\t\tch := ts.NewClient(nil)\n\t\tresponseClosed := make(chan struct{})\n\t\twriterDone := make(chan struct{})\n\n\t\targ3Writer, arg3Reader := helper.startCall(ctx, ch, ts.HostPort(), ts.Server().ServiceName())\n\t\tgo func() {\n\t\t\tdefer close(writerDone)\n\n\t\t\tfor i := 0; i < 10; i++ {\n\t\t\t\tassert.NoError(t, writeFlushBytes(arg3Writer, []byte{1}), \"Write failed\")\n\t\t\t}\n\n\t\t\t// Ignore the error of writeFlushBytes here since once we flush, the\n\t\t\t// remote side could receive and close the response before we've created\n\t\t\t// a new fragment (see fragmentingWriter.Flush). This could result\n\t\t\t// in the Flush returning a \"mex is already shutdown\" error.\n\t\t\twriteFlushBytes(arg3Writer, []byte{streamRequestClose})\n\n\t\t\t// Wait until our reader gets the EOF.\n\t\t\t<-responseClosed\n\n\t\t\t// Now our writes should fail, since the stream is shutdown\n\t\t\terr := writeFlushBytes(arg3Writer, []byte{1})\n\t\t\tif assert.Error(t, err, \"Req write should fail since response stream ended\") {\n\t\t\t\tassert.Contains(t, err.Error(), \"mex has been shutdown\")\n\t\t\t}\n\t\t}()\n\n\t\tfor i := 0; i < 10; i++ {\n\t\t\targ3 := make([]byte, 1)\n\t\t\tn, err := arg3Reader.Read(arg3)\n\t\t\tassert.Equal(t, 1, n, \"Read did not correct number of bytes\")\n\t\t\tassert.NoError(t, err, \"Read failed\")\n\t\t}\n\n\t\teofBuf := make([]byte, 1)\n\t\t_, err := arg3Reader.Read(eofBuf)\n\t\tassert.Equal(t, io.EOF, err, \"Response should EOF after request close\")\n\t\tassert.NoError(t, arg3Reader.Close(), \"Close should succeed\")\n\t\tclose(responseClosed)\n\t\t<-writerDone\n\t})\n}\n"
  },
  {
    "path": "stress_flag_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"flag\"\n\t\"testing\"\n)\n\n// This file contains functions for tests to access internal tchannel state.\n// Since it has a _test.go suffix, it is only compiled with tests in this package.\n\nvar flagStressTest = flag.Bool(\"stressTest\", false, \"Run stress tests (very slow)\")\n\n// CheckStress will skip the test if stress testing is not enabled.\nfunc CheckStress(t *testing.T) {\n\tif !*flagStressTest {\n\t\tt.Skip(\"Skipping long-running test as stressTest is not set\")\n\t}\n}\n"
  },
  {
    "path": "subchannel.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"golang.org/x/net/context\"\n)\n\n// SubChannelOption are used to set options for subchannels.\ntype SubChannelOption func(*SubChannel)\n\n// Isolated is a SubChannelOption that creates an isolated subchannel.\nfunc Isolated(s *SubChannel) {\n\ts.Lock()\n\ts.peers = s.topChannel.peers.newSibling()\n\ts.peers.SetStrategy(newLeastPendingCalculator())\n\ts.Unlock()\n}\n\n// SubChannel allows calling a specific service on a channel.\n// TODO(prashant): Allow creating a subchannel with default call options.\n// TODO(prashant): Allow registering handlers on a subchannel.\ntype SubChannel struct {\n\tsync.RWMutex\n\tserviceName        string\n\ttopChannel         *Channel\n\tdefaultCallOptions *CallOptions\n\tpeers              *PeerList\n\thandler            Handler\n\tlogger             Logger\n\tstatsReporter      StatsReporter\n}\n\n// Map of subchannel and the corresponding service\ntype subChannelMap struct {\n\tsync.RWMutex\n\tsubchannels map[string]*SubChannel\n}\n\nfunc newSubChannel(serviceName string, ch *Channel) *SubChannel {\n\tlogger := ch.Logger().WithFields(LogField{\"subchannel\", serviceName})\n\treturn &SubChannel{\n\t\tserviceName:   serviceName,\n\t\tpeers:         ch.peers,\n\t\ttopChannel:    ch,\n\t\thandler:       &handlerMap{}, // use handlerMap by default\n\t\tlogger:        logger,\n\t\tstatsReporter: ch.StatsReporter(),\n\t}\n}\n\n// ServiceName returns the service name that this subchannel is for.\nfunc (c *SubChannel) ServiceName() string {\n\treturn c.serviceName\n}\n\n// BeginCall starts a new call to a remote peer, returning an OutboundCall that can\n// be used to write the arguments of the call.\nfunc (c *SubChannel) BeginCall(ctx context.Context, methodName string, callOptions *CallOptions) (*OutboundCall, error) {\n\tif callOptions == nil {\n\t\tcallOptions = defaultCallOptions\n\t}\n\n\tpeer, err := c.peers.Get(callOptions.RequestState.PrevSelectedPeers())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn peer.BeginCall(ctx, c.ServiceName(), methodName, callOptions)\n}\n\n// Peers returns the PeerList for this subchannel.\nfunc (c *SubChannel) Peers() *PeerList {\n\treturn c.peers\n}\n\n// Isolated returns whether this subchannel is an isolated subchannel.\nfunc (c *SubChannel) Isolated() bool {\n\tc.RLock()\n\tdefer c.RUnlock()\n\treturn c.topChannel.Peers() != c.peers\n}\n\n// Register registers a handler on the subchannel for the given method.\n//\n// This function panics if the Handler for the SubChannel was overwritten with\n// SetHandler.\nfunc (c *SubChannel) Register(h Handler, methodName string) {\n\tr, ok := c.handler.(registrar)\n\tif !ok {\n\t\tpanic(fmt.Sprintf(\n\t\t\t\"handler for SubChannel(%v) configured with alternate root handler without Register method\",\n\t\t\tc.ServiceName(),\n\t\t))\n\t}\n\n\tr.Register(h, methodName)\n}\n\n// GetHandlers returns all handlers registered on this subchannel by method name.\n//\n// This function panics if the Handler for the SubChannel was overwritten with\n// SetHandler.\nfunc (c *SubChannel) GetHandlers() map[string]Handler {\n\thandlers, ok := c.handler.(*handlerMap)\n\tif !ok {\n\t\tpanic(fmt.Sprintf(\n\t\t\t\"handler for SubChannel(%v) was changed to disallow method registration\",\n\t\t\tc.ServiceName(),\n\t\t))\n\t}\n\n\thandlers.RLock()\n\thandlersMap := make(map[string]Handler, len(handlers.handlers))\n\tfor k, v := range handlers.handlers {\n\t\thandlersMap[k] = v\n\t}\n\thandlers.RUnlock()\n\treturn handlersMap\n}\n\n// SetHandler changes the SubChannel's underlying handler. This may be used to\n// set up a catch-all Handler for all requests received by this SubChannel.\n//\n// Methods registered on this SubChannel using Register() before calling\n// SetHandler() will be forgotten. Further calls to Register() on this\n// SubChannel after SetHandler() is called will cause panics.\nfunc (c *SubChannel) SetHandler(h Handler) {\n\tc.handler = h\n}\n\n// Logger returns the logger for this subchannel.\nfunc (c *SubChannel) Logger() Logger {\n\treturn c.logger\n}\n\n// StatsReporter returns the stats reporter for this subchannel.\nfunc (c *SubChannel) StatsReporter() StatsReporter {\n\treturn c.topChannel.StatsReporter()\n}\n\n// StatsTags returns the stats tags for this subchannel.\nfunc (c *SubChannel) StatsTags() map[string]string {\n\ttags := c.topChannel.StatsTags()\n\ttags[\"subchannel\"] = c.serviceName\n\treturn tags\n}\n\n// Tracer returns OpenTracing Tracer from the top channel.\nfunc (c *SubChannel) Tracer() opentracing.Tracer {\n\treturn c.topChannel.Tracer()\n}\n\n// Register a new subchannel for the given serviceName\nfunc (subChMap *subChannelMap) registerNewSubChannel(serviceName string, ch *Channel) (_ *SubChannel, added bool) {\n\tsubChMap.Lock()\n\tdefer subChMap.Unlock()\n\n\tif subChMap.subchannels == nil {\n\t\tsubChMap.subchannels = make(map[string]*SubChannel)\n\t}\n\n\tif sc, ok := subChMap.subchannels[serviceName]; ok {\n\t\treturn sc, false\n\t}\n\n\tsc := newSubChannel(serviceName, ch)\n\tsubChMap.subchannels[serviceName] = sc\n\treturn sc, true\n}\n\n// Get subchannel if, we have one\nfunc (subChMap *subChannelMap) get(serviceName string) (*SubChannel, bool) {\n\tsubChMap.RLock()\n\tsc, ok := subChMap.subchannels[serviceName]\n\tsubChMap.RUnlock()\n\treturn sc, ok\n}\n\n// GetOrAdd a subchannel for the given serviceName on the map\nfunc (subChMap *subChannelMap) getOrAdd(serviceName string, ch *Channel) (_ *SubChannel, added bool) {\n\tif sc, ok := subChMap.get(serviceName); ok {\n\t\treturn sc, false\n\t}\n\n\treturn subChMap.registerNewSubChannel(serviceName, ch)\n}\n\nfunc (subChMap *subChannelMap) updatePeer(p *Peer) {\n\tsubChMap.RLock()\n\tfor _, subCh := range subChMap.subchannels {\n\t\tif subCh.Isolated() {\n\t\t\tsubCh.RLock()\n\t\t\tsubCh.Peers().onPeerChange(p)\n\t\t\tsubCh.RUnlock()\n\t\t}\n\t}\n\tsubChMap.RUnlock()\n}\n"
  },
  {
    "path": "subchannel_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\ntype chanSet struct {\n\tmain     Registrar\n\tsub      Registrar\n\tisolated Registrar\n}\n\nfunc withNewSet(t *testing.T, f func(*testing.T, chanSet)) {\n\tch := testutils.NewClient(t, nil)\n\tdefer ch.Close()\n\tf(t, chanSet{\n\t\tmain:     ch,\n\t\tsub:      ch.GetSubChannel(\"hyperbahn\"),\n\t\tisolated: ch.GetSubChannel(\"ringpop\", Isolated),\n\t})\n}\n\n// Assert that two Registrars have references to the same Peer.\nfunc assertHaveSameRef(t *testing.T, r1, r2 Registrar) {\n\tp1, err := r1.Peers().Get(nil)\n\tassert.NoError(t, err, \"First registrar has no peers.\")\n\n\tp2, err := r2.Peers().Get(nil)\n\tassert.NoError(t, err, \"Second registrar has no peers.\")\n\n\tassert.True(t, p1 == p2, \"Registrars have references to different peers.\")\n}\n\nfunc assertNoPeer(t *testing.T, r Registrar) {\n\t_, err := r.Peers().Get(nil)\n\tassert.Equal(t, err, ErrNoPeers)\n}\n\nfunc TestMainAddVisibility(t *testing.T) {\n\twithNewSet(t, func(t *testing.T, set chanSet) {\n\t\t// Adding a peer to the main channel should be reflected in the\n\t\t// subchannel, but not the isolated subchannel.\n\t\tset.main.Peers().Add(\"127.0.0.1:3000\")\n\t\tassertHaveSameRef(t, set.main, set.sub)\n\t\tassertNoPeer(t, set.isolated)\n\t})\n}\n\nfunc TestSubchannelAddVisibility(t *testing.T) {\n\twithNewSet(t, func(t *testing.T, set chanSet) {\n\t\t// Adding a peer to a non-isolated subchannel should be reflected in\n\t\t// the main channel but not in isolated siblings.\n\t\tset.sub.Peers().Add(\"127.0.0.1:3000\")\n\t\tassertHaveSameRef(t, set.main, set.sub)\n\t\tassertNoPeer(t, set.isolated)\n\t})\n}\n\nfunc TestIsolatedAddVisibility(t *testing.T) {\n\twithNewSet(t, func(t *testing.T, set chanSet) {\n\t\t// Adding a peer to an isolated subchannel shouldn't change the main\n\t\t// channel or sibling channels.\n\t\tset.isolated.Peers().Add(\"127.0.0.1:3000\")\n\n\t\t_, err := set.isolated.Peers().Get(nil)\n\t\tassert.NoError(t, err)\n\n\t\tassertNoPeer(t, set.main)\n\t\tassertNoPeer(t, set.sub)\n\t})\n}\n\nfunc TestAddReusesPeers(t *testing.T) {\n\twithNewSet(t, func(t *testing.T, set chanSet) {\n\t\t// Adding to both a channel and an isolated subchannel shouldn't create\n\t\t// two separate peers.\n\t\tset.main.Peers().Add(\"127.0.0.1:3000\")\n\t\tset.isolated.Peers().Add(\"127.0.0.1:3000\")\n\n\t\tassertHaveSameRef(t, set.main, set.sub)\n\t\tassertHaveSameRef(t, set.main, set.isolated)\n\t})\n}\n\nfunc TestSetHandler(t *testing.T) {\n\t// Generate a Handler that expects only the given methods to be called.\n\tgenHandler := func(methods ...string) Handler {\n\t\tallowedMethods := make(map[string]struct{}, len(methods))\n\t\tfor _, m := range methods {\n\t\t\tallowedMethods[m] = struct{}{}\n\t\t}\n\n\t\treturn HandlerFunc(func(ctx context.Context, call *InboundCall) {\n\t\t\tmethod := call.MethodString()\n\t\t\tassert.Contains(t, allowedMethods, method, \"unexpected call to %q\", method)\n\t\t\terr := raw.WriteResponse(call.Response(), &raw.Res{Arg3: []byte(method)})\n\t\t\trequire.NoError(t, err)\n\t\t})\n\t}\n\n\tch := testutils.NewServer(t, testutils.NewOpts().\n\t\tAddLogFilter(\"Couldn't find handler\", 1, \"serviceName\", \"svc2\", \"method\", \"bar\"))\n\tdefer ch.Close()\n\n\t// Catch-all handler for the main channel that accepts foo, bar, and baz,\n\t// and a single registered handler for a different subchannel.\n\tch.GetSubChannel(\"svc1\").SetHandler(genHandler(\"foo\", \"bar\", \"baz\"))\n\tch.GetSubChannel(\"svc2\").Register(genHandler(\"foo\"), \"foo\")\n\n\tclient := testutils.NewClient(t, nil)\n\tclient.Peers().Add(ch.PeerInfo().HostPort)\n\tdefer client.Close()\n\n\ttests := []struct {\n\t\tService    string\n\t\tMethod     string\n\t\tShouldFail bool\n\t}{\n\t\t{\"svc1\", \"foo\", false},\n\t\t{\"svc1\", \"bar\", false},\n\t\t{\"svc1\", \"baz\", false},\n\n\t\t{\"svc2\", \"foo\", false},\n\t\t{\"svc2\", \"bar\", true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tc := client.GetSubChannel(tt.Service)\n\t\tctx, _ := NewContext(time.Second)\n\t\t_, data, _, err := raw.CallSC(ctx, c, tt.Method, nil, []byte(\"irrelevant\"))\n\n\t\tif tt.ShouldFail {\n\t\t\trequire.Error(t, err)\n\t\t} else {\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tt.Method, string(data))\n\t\t}\n\t}\n\n\tst := ch.IntrospectState(nil)\n\tassert.Equal(t, \"overriden\", st.SubChannels[\"svc1\"].Handler.Type.String())\n\tassert.Nil(t, st.SubChannels[\"svc1\"].Handler.Methods)\n\n\tassert.Equal(t, \"methods\", st.SubChannels[\"svc2\"].Handler.Type.String())\n\tassert.Equal(t, []string{\"foo\"}, st.SubChannels[\"svc2\"].Handler.Methods)\n}\n\nfunc TestGetHandlers(t *testing.T) {\n\tch := testutils.NewServer(t, nil)\n\tdefer ch.Close()\n\n\tvar handler1 HandlerFunc = func(_ context.Context, _ *InboundCall) {\n\t\tpanic(\"unexpected call\")\n\t}\n\tvar handler2 HandlerFunc = func(_ context.Context, _ *InboundCall) {\n\t\tpanic(\"unexpected call\")\n\t}\n\n\tch.Register(handler1, \"method1\")\n\tch.Register(handler2, \"method2\")\n\tch.GetSubChannel(\"foo\").Register(handler2, \"method1\")\n\n\ttests := []struct {\n\t\tserviceName string\n\t\twantMethods []string\n\t}{\n\t\t{\n\t\t\tserviceName: ch.ServiceName(),\n\t\t\twantMethods: []string{\"_gometa_introspect\", \"_gometa_runtime\", \"method1\", \"method2\"},\n\t\t},\n\t\t{\n\t\t\tserviceName: \"foo\",\n\t\t\twantMethods: []string{\"method1\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\thandlers := ch.GetSubChannel(tt.serviceName).GetHandlers()\n\t\tif !assert.Equal(t, len(tt.wantMethods), len(handlers),\n\t\t\t\"Unexpected number of methods found, expected %v, got %v\", tt.wantMethods, handlers) {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, method := range tt.wantMethods {\n\t\t\t_, ok := handlers[method]\n\t\t\tassert.True(t, ok, \"Expected to find method %v in handlers: %v\", method, handlers)\n\t\t}\n\t}\n}\n\nfunc TestCannotRegisterOrGetAfterSetHandler(t *testing.T) {\n\tch := testutils.NewServer(t, nil)\n\tdefer ch.Close()\n\n\tvar someHandler HandlerFunc = func(ctx context.Context, call *InboundCall) {\n\t\tpanic(\"unexpected call\")\n\t}\n\tvar anotherHandler HandlerFunc = func(ctx context.Context, call *InboundCall) {\n\t\tpanic(\"unexpected call\")\n\t}\n\n\tch.GetSubChannel(\"foo\").SetHandler(someHandler)\n\n\t// Registering against the original service should not panic but\n\t// registering against the \"foo\" service should panic since the handler\n\t// was overridden, and doesn't support Register.\n\tassert.NotPanics(t, func() { ch.Register(anotherHandler, \"bar\") })\n\tassert.NotPanics(t, func() { ch.GetSubChannel(\"svc\").GetHandlers() })\n\tassert.Panics(t, func() { ch.GetSubChannel(\"foo\").Register(anotherHandler, \"bar\") })\n\tassert.Panics(t, func() { ch.GetSubChannel(\"foo\").GetHandlers() })\n}\n\nfunc TestGetSubchannelOptionsOnNew(t *testing.T) {\n\tch := testutils.NewServer(t, nil)\n\tdefer ch.Close()\n\n\tpeers := ch.GetSubChannel(\"s\", Isolated).Peers()\n\twant := peers.Add(\"1.1.1.1:1\")\n\n\tpeers2 := ch.GetSubChannel(\"s\", Isolated).Peers()\n\tassert.Equal(t, peers, peers2, \"Get isolated subchannel should not clear existing peers\")\n\tpeer, err := peers2.Get(nil)\n\trequire.NoError(t, err, \"Should get peer\")\n\tassert.Equal(t, want, peer, \"Unexpected peer\")\n}\n\nfunc TestHandlerWithoutSubChannel(t *testing.T) {\n\topts := testutils.NewOpts().NoRelay()\n\topts.Handler = raw.Wrap(newTestHandler(t))\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tclient := ts.NewClient(nil)\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName())\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), \"larry\")\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), \"curly\")\n\t\ttestutils.AssertEcho(t, client, ts.HostPort(), \"moe\")\n\n\t\tassert.Panics(t, func() {\n\t\t\tts.Server().Register(raw.Wrap(newTestHandler(t)), \"nyuck\")\n\t\t})\n\t})\n}\n\ntype handlerWithRegister struct {\n\tregistered map[string]struct{}\n}\n\nfunc (handlerWithRegister) Handle(ctx context.Context, call *InboundCall) {\n\tpanic(\"Handle not expected to be called\")\n}\n\nfunc (hr *handlerWithRegister) Register(h Handler, methodName string) {\n\tif hr.registered == nil {\n\t\thr.registered = make(map[string]struct{})\n\t}\n\thr.registered[methodName] = struct{}{}\n}\n\nfunc TestHandlerCustomRegister(t *testing.T) {\n\thrTop := &handlerWithRegister{}\n\thrSC := &handlerWithRegister{}\n\n\topts := testutils.NewOpts()\n\topts.ChannelOptions.Handler = hrTop\n\tch := testutils.NewServer(t, opts)\n\tdefer ch.Close()\n\n\tvar unused HandlerFunc = func(_ context.Context, _ *InboundCall) {\n\t\tpanic(\"unexpected call\")\n\t}\n\n\tch.Register(unused, \"Top-Method\")\n\n\tsc := ch.GetSubChannel(\"sc\")\n\tsc.SetHandler(hrSC)\n\tsc.Register(unused, \"SC-Method\")\n\n\tassert.Equal(t, map[string]struct{}{\n\t\t\"Top-Method\": {},\n\t}, hrTop.registered, \"Register on top channel mismatch\")\n\tassert.Equal(t, map[string]struct{}{\n\t\t\"SC-Method\": {},\n\t}, hrSC.registered, \"Register on subchannel mismatch\")\n}\n"
  },
  {
    "path": "systemerrcode_string.go",
    "content": "// generated by stringer -type=SystemErrCode; DO NOT EDIT\n\npackage tchannel\n\nimport \"fmt\"\n\nconst (\n\t_SystemErrCode_name_0 = \"ErrCodeInvalidErrCodeTimeoutErrCodeCancelledErrCodeBusyErrCodeDeclinedErrCodeUnexpectedErrCodeBadRequestErrCodeNetwork\"\n\t_SystemErrCode_name_1 = \"ErrCodeProtocol\"\n)\n\nvar (\n\t_SystemErrCode_index_0 = [...]uint8{0, 14, 28, 44, 55, 70, 87, 104, 118}\n\t_SystemErrCode_index_1 = [...]uint8{0, 15}\n)\n\nfunc (i SystemErrCode) String() string {\n\tswitch {\n\tcase 0 <= i && i <= 7:\n\t\treturn _SystemErrCode_name_0[_SystemErrCode_index_0[i]:_SystemErrCode_index_0[i+1]]\n\tcase i == 255:\n\t\treturn _SystemErrCode_name_1\n\tdefault:\n\t\treturn fmt.Sprintf(\"SystemErrCode(%d)\", i)\n\t}\n}\n"
  },
  {
    "path": "tchannel_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/testutils/goroutines\"\n)\n\nfunc checkAllChannels() error {\n\tch, err := NewChannel(\"test-end\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar foundChannels bool\n\tallChannels := ch.IntrospectOthers(&IntrospectionOptions{})\n\tfor _, cs := range allChannels {\n\t\tif len(cs) != 0 {\n\t\t\tfoundChannels = true\n\t\t}\n\t}\n\n\tif !foundChannels {\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"unclosed channels:\\n%+v\", allChannels)\n}\n\nfunc TestMain(m *testing.M) {\n\tflag.Parse()\n\texitCode := m.Run()\n\n\tif exitCode == 0 {\n\t\t// Only do extra checks if the tests were successful.\n\t\tif err := goroutines.IdentifyLeaks(nil); err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Found goroutine leaks on successful test run: %v\", err)\n\t\t\texitCode = 1\n\t\t}\n\n\t\tif err := checkAllChannels(); err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Found unclosed channels on successful test run: %v\", err)\n\t\t\texitCode = 1\n\t\t}\n\t}\n\n\tos.Exit(exitCode)\n}\n"
  },
  {
    "path": "testutils/call.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/relay\"\n\t\"github.com/uber/tchannel-go/testutils/thriftarg2test\"\n\t\"github.com/uber/tchannel-go/thrift/arg2\"\n)\n\n// FakeIncomingCall implements IncomingCall interface.\n// Note: the F suffix for the fields is to clash with the method name.\ntype FakeIncomingCall struct {\n\t// CallerNameF is the calling service's name.\n\tCallerNameF string\n\n\t// ShardKeyF is the intended destination for this call.\n\tShardKeyF string\n\n\t// RemotePeerF is the calling service's peer info.\n\tRemotePeerF tchannel.PeerInfo\n\n\t// LocalPeerF is the local service's peer info.\n\tLocalPeerF tchannel.LocalPeerInfo\n\n\t// RoutingKeyF is the routing key.\n\tRoutingKeyF string\n\n\t// RoutingDelegateF is the routing delegate.\n\tRoutingDelegateF string\n}\n\n// CallerName returns the caller name as specified in the fake call.\nfunc (f *FakeIncomingCall) CallerName() string {\n\treturn f.CallerNameF\n}\n\n// ShardKey returns the shard key as specified in the fake call.\nfunc (f *FakeIncomingCall) ShardKey() string {\n\treturn f.ShardKeyF\n}\n\n// RoutingKey returns the routing delegate as specified in the fake call.\nfunc (f *FakeIncomingCall) RoutingKey() string {\n\treturn f.RoutingKeyF\n}\n\n// RoutingDelegate returns the routing delegate as specified in the fake call.\nfunc (f *FakeIncomingCall) RoutingDelegate() string {\n\treturn f.RoutingDelegateF\n}\n\n// LocalPeer returns the local peer information for this call.\nfunc (f *FakeIncomingCall) LocalPeer() tchannel.LocalPeerInfo {\n\treturn f.LocalPeerF\n}\n\n// RemotePeer returns the remote peer information for this call.\nfunc (f *FakeIncomingCall) RemotePeer() tchannel.PeerInfo {\n\treturn f.RemotePeerF\n}\n\n// CallOptions returns the incoming call options suitable for proxying a request.\nfunc (f *FakeIncomingCall) CallOptions() *tchannel.CallOptions {\n\treturn &tchannel.CallOptions{\n\t\tShardKey:        f.ShardKey(),\n\t\tRoutingKey:      f.RoutingKey(),\n\t\tRoutingDelegate: f.RoutingDelegate(),\n\t}\n}\n\n// NewIncomingCall creates an incoming call for tests.\nfunc NewIncomingCall(callerName string) tchannel.IncomingCall {\n\treturn &FakeIncomingCall{CallerNameF: callerName}\n}\n\n// FakeCallFrame is a stub implementation of the CallFrame interface.\ntype FakeCallFrame struct {\n\ttb   testing.TB\n\tTTLF time.Duration\n\n\tServiceF, MethodF, CallerF, RoutingKeyF, RoutingDelegateF string\n\n\tArg2StartOffsetVal, Arg2EndOffsetVal int\n\tIsArg2Fragmented                     bool\n\n\targ2KVIterator    arg2.KeyValIterator\n\thasArg2KVIterator error\n\n\tArg2Appends []relay.KeyVal\n}\n\nvar _ relay.CallFrame = &FakeCallFrame{}\n\n// TTL returns the TTL field.\nfunc (f *FakeCallFrame) TTL() time.Duration {\n\treturn f.TTLF\n}\n\n// Service returns the service name field.\nfunc (f *FakeCallFrame) Service() []byte {\n\treturn []byte(f.ServiceF)\n}\n\n// Method returns the method field.\nfunc (f *FakeCallFrame) Method() []byte {\n\treturn []byte(f.MethodF)\n}\n\n// Caller returns the caller field.\nfunc (f *FakeCallFrame) Caller() []byte {\n\treturn []byte(f.CallerF)\n}\n\n// RoutingKey returns the routing delegate field.\nfunc (f *FakeCallFrame) RoutingKey() []byte {\n\treturn []byte(f.RoutingKeyF)\n}\n\n// RoutingDelegate returns the routing delegate field.\nfunc (f *FakeCallFrame) RoutingDelegate() []byte {\n\treturn []byte(f.RoutingDelegateF)\n}\n\n// Arg2StartOffset returns the offset from start of payload to\n// the beginning of Arg2.\nfunc (f *FakeCallFrame) Arg2StartOffset() int {\n\treturn f.Arg2StartOffsetVal\n}\n\n// Arg2EndOffset returns the offset from start of payload to the end\n// of Arg2 and whether Arg2 is fragmented.\nfunc (f *FakeCallFrame) Arg2EndOffset() (int, bool) {\n\treturn f.Arg2EndOffsetVal, f.IsArg2Fragmented\n}\n\n// Arg2Iterator returns the iterator for reading Arg2 key value pair\n// of TChannel-Thrift Arg Scheme.\nfunc (f *FakeCallFrame) Arg2Iterator() (arg2.KeyValIterator, error) {\n\treturn f.arg2KVIterator, f.hasArg2KVIterator\n}\n\n// Arg2Append appends a key value pair to Arg2\nfunc (f *FakeCallFrame) Arg2Append(key, val []byte) {\n\tf.Arg2Appends = append(f.Arg2Appends, relay.KeyVal{Key: key, Val: val})\n}\n\n// CopyCallFrame copies the relay.CallFrame and returns a FakeCallFrame with\n// corresponding values\nfunc CopyCallFrame(f relay.CallFrame) *FakeCallFrame {\n\tendOffset, hasMore := f.Arg2EndOffset()\n\tcopyIterator, err := copyThriftArg2KVIterator(f)\n\treturn &FakeCallFrame{\n\t\tTTLF:               f.TTL(),\n\t\tServiceF:           string(f.Service()),\n\t\tMethodF:            string(f.Method()),\n\t\tCallerF:            string(f.Caller()),\n\t\tRoutingKeyF:        string(f.RoutingKey()),\n\t\tRoutingDelegateF:   string(f.RoutingDelegate()),\n\t\tArg2StartOffsetVal: f.Arg2StartOffset(),\n\t\tArg2EndOffsetVal:   endOffset,\n\t\tIsArg2Fragmented:   hasMore,\n\t\targ2KVIterator:     copyIterator,\n\t\thasArg2KVIterator:  err,\n\t}\n}\n\n// copyThriftArg2KVIterator uses the CallFrame Arg2Iterator to make a\n// deep-copy KeyValIterator.\nfunc copyThriftArg2KVIterator(f relay.CallFrame) (arg2.KeyValIterator, error) {\n\tkv := make(map[string]string)\n\tfor iter, err := f.Arg2Iterator(); err == nil; iter, err = iter.Next() {\n\t\tkv[string(iter.Key())] = string(iter.Value())\n\t}\n\treturn arg2.NewKeyValIterator(thriftarg2test.BuildKVBuffer(kv))\n}\n"
  },
  {
    "path": "testutils/channel.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/internal/testcert\"\n\t\"github.com/uber/tchannel-go/raw\"\n\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\n// NewServerChannel creates a TChannel that is listening and returns the channel.\n// Passed in options may be mutated (for post-verification of state).\nfunc NewServerChannel(opts *ChannelOpts) (*tchannel.Channel, error) {\n\topts = opts.Copy()\n\n\tl, err := getListener(opts.ServeTLS)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to listen: %v\", err)\n\t}\n\t_, port, err := net.SplitHostPort(l.Addr().String())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not get listening port from %v: %v\", l.Addr().String(), err)\n\t}\n\n\tserviceName := defaultString(opts.ServiceName, DefaultServerName)\n\topts.ProcessName = defaultString(opts.ProcessName, serviceName+\"-\"+port)\n\tupdateOptsLogger(opts)\n\tch, err := tchannel.NewChannel(serviceName, &opts.ChannelOptions)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"NewChannel failed: %v\", err)\n\t}\n\n\tif err := ch.Serve(l); err != nil {\n\t\treturn nil, fmt.Errorf(\"Serve failed: %v\", err)\n\t}\n\n\treturn ch, nil\n}\n\nvar totalClients atomic.Uint32\n\n// NewClientChannel creates a TChannel that is not listening.\n// Passed in options may be mutated (for post-verification of state).\nfunc NewClientChannel(opts *ChannelOpts) (*tchannel.Channel, error) {\n\topts = opts.Copy()\n\n\tclientNum := totalClients.Inc()\n\tserviceName := defaultString(opts.ServiceName, DefaultClientName)\n\topts.ProcessName = defaultString(opts.ProcessName, serviceName+\"-\"+fmt.Sprint(clientNum))\n\tupdateOptsLogger(opts)\n\treturn tchannel.NewChannel(serviceName, &opts.ChannelOptions)\n}\n\ntype rawFuncHandler struct {\n\tch tchannel.Registrar\n\tf  func(context.Context, *raw.Args) (*raw.Res, error)\n}\n\nfunc (h rawFuncHandler) OnError(ctx context.Context, err error) {\n\th.ch.Logger().WithFields(\n\t\ttchannel.LogField{Key: \"context\", Value: ctx},\n\t\ttchannel.ErrField(err),\n\t).Error(\"simpleHandler OnError.\")\n}\n\nfunc (h rawFuncHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\treturn h.f(ctx, args)\n}\n\n// RegisterFunc registers a function as a handler for the given method name.\nfunc RegisterFunc(ch tchannel.Registrar, name string,\n\tf func(ctx context.Context, args *raw.Args) (*raw.Res, error)) {\n\n\tch.Register(raw.Wrap(rawFuncHandler{ch, f}), name)\n}\n\n// IntrospectJSON returns the introspected state of the channel as a JSON string.\nfunc IntrospectJSON(ch *tchannel.Channel, opts *tchannel.IntrospectionOptions) string {\n\tstate := ch.IntrospectState(opts)\n\tmarshalled, err := json.MarshalIndent(state, \"\", \"  \")\n\tif err != nil {\n\t\treturn fmt.Sprintf(\"failed to marshal introspected state: %v\", err)\n\t}\n\n\treturn string(marshalled)\n}\n\nfunc getListener(serveTLS bool) (net.Listener, error) {\n\tif serveTLS {\n\t\treturn getTLSListener()\n\t}\n\n\treturn net.Listen(\"tcp\", \"127.0.0.1:0\")\n}\n\nfunc getTLSListener() (net.Listener, error) {\n\tcert, err := tls.X509KeyPair(testcert.TestCert, testcert.TestKey)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"testutils: getTLSListener: %v\", err))\n\t}\n\n\treturn tls.Listen(\"tcp\", \"127.0.0.1:0\", &tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t})\n}\n"
  },
  {
    "path": "testutils/channel_opts.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"flag\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/tos\"\n\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\nvar connectionLog = flag.Bool(\"connectionLog\", false, \"Enables connection logging in tests\")\n\n// Default service names for the test channels.\nconst (\n\tDefaultServerName = \"testService\"\n\tDefaultClientName = \"testService-client\"\n)\n\n// ChannelOpts contains options to create a test channel using WithServer\ntype ChannelOpts struct {\n\ttchannel.ChannelOptions\n\n\t// ServiceName defaults to DefaultServerName or DefaultClientName.\n\tServiceName string\n\n\t// LogVerification contains options for controlling the log verification.\n\tLogVerification LogVerification\n\n\t// DisableRelay disables the relay interposed between clients/servers.\n\t// By default, all tests are run with a relay interposed.\n\tDisableRelay bool\n\n\t// DisableServer disables creation of the TChannel server.\n\t// This is typically only used in relay tests when a custom server is required.\n\tDisableServer bool\n\n\t// OnlyRelay instructs TestServer the test must only be run with a relay.\n\tOnlyRelay bool\n\n\t// RunCount is the number of times the test should be run. Zero or\n\t// negative values are treated as a single run.\n\tRunCount int\n\n\t// CheckFramePooling indicates whether we should check for frame leaks or not.\n\t// This causes the same tests to be run twice, first with the default frame pool,\n\t// then with the recording frame pool, which will double the amount of time it takes\n\t// for the test.\n\tCheckFramePooling bool\n\n\t// postFns is a list of functions that are run after the test.\n\t// They are run even if the test fails.\n\tpostFns []func()\n\n\t// ServeTLS enables TLS support on server channel with test certs\n\tServeTLS bool\n}\n\n// LogVerification contains options to control the log verification.\ntype LogVerification struct {\n\tDisabled bool\n\n\tFilters []LogFilter\n}\n\n// LogFilter is a single substring match that can be ignored.\ntype LogFilter struct {\n\t// Filter specifies the substring match to search\n\t// for in the log message to skip raising an error.\n\tFilter string\n\n\t// Count is the maximum number of allowed warn+ logs matching\n\t// Filter before errors are raised.\n\tCount uint\n\n\t// FieldFilters specifies expected substring matches for fields.\n\tFieldFilters map[string]string\n}\n\n// Copy copies the channel options (so that they can be safely modified).\nfunc (o *ChannelOpts) Copy() *ChannelOpts {\n\tif o == nil {\n\t\treturn NewOpts()\n\t}\n\tcopiedOpts := *o\n\treturn &copiedOpts\n}\n\n// SetServiceName sets ServiceName.\nfunc (o *ChannelOpts) SetServiceName(svcName string) *ChannelOpts {\n\to.ServiceName = svcName\n\treturn o\n}\n\n// SetProcessName sets the ProcessName in ChannelOptions.\nfunc (o *ChannelOpts) SetProcessName(processName string) *ChannelOpts {\n\to.ProcessName = processName\n\treturn o\n}\n\n// SetStatsReporter sets StatsReporter in ChannelOptions.\nfunc (o *ChannelOpts) SetStatsReporter(statsReporter tchannel.StatsReporter) *ChannelOpts {\n\to.StatsReporter = statsReporter\n\treturn o\n}\n\n// SetFramePool sets FramePool in DefaultConnectionOptions.\nfunc (o *ChannelOpts) SetFramePool(framePool tchannel.FramePool) *ChannelOpts {\n\to.DefaultConnectionOptions.FramePool = framePool\n\treturn o\n}\n\n// SetHealthChecks sets HealthChecks in DefaultConnectionOptions.\nfunc (o *ChannelOpts) SetHealthChecks(healthChecks tchannel.HealthCheckOptions) *ChannelOpts {\n\to.DefaultConnectionOptions.HealthChecks = healthChecks\n\treturn o\n}\n\n// SetSendBufferSize sets the SendBufferSize in DefaultConnectionOptions.\nfunc (o *ChannelOpts) SetSendBufferSize(bufSize int) *ChannelOpts {\n\to.DefaultConnectionOptions.SendBufferSize = bufSize\n\treturn o\n}\n\n// SetSendBufferSizeOverrides sets the SendBufferOverrides in DefaultConnectionOptions.\nfunc (o *ChannelOpts) SetSendBufferSizeOverrides(overrides []tchannel.SendBufferSizeOverride) *ChannelOpts {\n\to.DefaultConnectionOptions.SendBufferSizeOverrides = overrides\n\treturn o\n}\n\n// SetTosPriority set TosPriority in DefaultConnectionOptions.\nfunc (o *ChannelOpts) SetTosPriority(tosPriority tos.ToS) *ChannelOpts {\n\to.DefaultConnectionOptions.TosPriority = tosPriority\n\treturn o\n}\n\n// SetChecksumType sets the ChecksumType in DefaultConnectionOptions.\nfunc (o *ChannelOpts) SetChecksumType(checksumType tchannel.ChecksumType) *ChannelOpts {\n\to.DefaultConnectionOptions.ChecksumType = checksumType\n\treturn o\n}\n\n// SetTimeNow sets TimeNow in ChannelOptions.\nfunc (o *ChannelOpts) SetTimeNow(timeNow func() time.Time) *ChannelOpts {\n\to.TimeNow = timeNow\n\treturn o\n}\n\n// SetTimeTicker sets TimeTicker in ChannelOptions.\nfunc (o *ChannelOpts) SetTimeTicker(timeTicker func(d time.Duration) *time.Ticker) *ChannelOpts {\n\to.TimeTicker = timeTicker\n\treturn o\n}\n\n// DisableLogVerification disables log verification for this channel.\nfunc (o *ChannelOpts) DisableLogVerification() *ChannelOpts {\n\to.LogVerification.Disabled = true\n\treturn o\n}\n\n// NoRelay disables running the test with a relay interposed.\nfunc (o *ChannelOpts) NoRelay() *ChannelOpts {\n\to.DisableRelay = true\n\treturn o\n}\n\n// SetRelayOnly instructs TestServer to only run with a relay in front of this channel.\nfunc (o *ChannelOpts) SetRelayOnly() *ChannelOpts {\n\to.OnlyRelay = true\n\treturn o\n}\n\n// SetDisableServer disables creation of the TChannel server.\n// This is typically only used in relay tests when a custom server is required.\nfunc (o *ChannelOpts) SetDisableServer() *ChannelOpts {\n\to.DisableServer = true\n\treturn o\n}\n\n// SetRunCount sets the number of times run the test.\nfunc (o *ChannelOpts) SetRunCount(n int) *ChannelOpts {\n\to.RunCount = n\n\treturn o\n}\n\n// AddLogFilter sets an allowed filter for warning/error logs and sets\n// the maximum number of times that log can occur.\nfunc (o *ChannelOpts) AddLogFilter(filter string, maxCount uint, fields ...string) *ChannelOpts {\n\tfieldFilters := make(map[string]string)\n\tfor i := 0; i < len(fields); i += 2 {\n\t\tfieldFilters[fields[i]] = fields[i+1]\n\t}\n\n\to.LogVerification.Filters = append(o.LogVerification.Filters, LogFilter{\n\t\tFilter:       filter,\n\t\tCount:        maxCount,\n\t\tFieldFilters: fieldFilters,\n\t})\n\treturn o\n}\n\nfunc (o *ChannelOpts) addPostFn(f func()) {\n\to.postFns = append(o.postFns, f)\n}\n\n// SetRelayHost sets the channel's RelayHost, which enables relaying.\nfunc (o *ChannelOpts) SetRelayHost(rh tchannel.RelayHost) *ChannelOpts {\n\to.ChannelOptions.RelayHost = rh\n\treturn o\n}\n\n// SetRelayLocal sets the channel's relay local handlers for service names\n// that should be handled by the relay channel itself.\nfunc (o *ChannelOpts) SetRelayLocal(relayLocal ...string) *ChannelOpts {\n\to.ChannelOptions.RelayLocalHandlers = relayLocal\n\treturn o\n}\n\n// SetRelayMaxTimeout sets the maximum allowable timeout for relayed calls.\nfunc (o *ChannelOpts) SetRelayMaxTimeout(d time.Duration) *ChannelOpts {\n\to.ChannelOptions.RelayMaxTimeout = d\n\treturn o\n}\n\n// SetRelayMaxConnectionTimeout sets the maximum timeout for connection attempts.\nfunc (o *ChannelOpts) SetRelayMaxConnectionTimeout(d time.Duration) *ChannelOpts {\n\to.ChannelOptions.RelayMaxConnectionTimeout = d\n\treturn o\n}\n\n// SetRelayMaxTombs sets the maximum number of tombs tracked in the relayer.\nfunc (o *ChannelOpts) SetRelayMaxTombs(maxTombs uint64) *ChannelOpts {\n\to.ChannelOptions.RelayMaxTombs = maxTombs\n\treturn o\n}\n\n// SetOnPeerStatusChanged sets the callback for channel status change\n// noficiations.\nfunc (o *ChannelOpts) SetOnPeerStatusChanged(f func(*tchannel.Peer)) *ChannelOpts {\n\to.ChannelOptions.OnPeerStatusChanged = f\n\treturn o\n}\n\n// SetMaxIdleTime sets a threshold after which idle connections will\n// automatically get dropped. See idle_sweep.go for more details.\nfunc (o *ChannelOpts) SetMaxIdleTime(d time.Duration) *ChannelOpts {\n\to.ChannelOptions.MaxIdleTime = d\n\treturn o\n}\n\n// SetIdleCheckInterval sets the frequency of the periodic poller that removes\n// stale connections from the channel.\nfunc (o *ChannelOpts) SetIdleCheckInterval(d time.Duration) *ChannelOpts {\n\to.ChannelOptions.IdleCheckInterval = d\n\treturn o\n}\n\n// SetDialer sets the dialer used for outbound connections\nfunc (o *ChannelOpts) SetDialer(f func(context.Context, string, string) (net.Conn, error)) *ChannelOpts {\n\to.ChannelOptions.Dialer = f\n\treturn o\n}\n\n// SetConnContext sets the connection's ConnContext function\nfunc (o *ChannelOpts) SetConnContext(f func(context.Context, net.Conn) context.Context) *ChannelOpts {\n\to.ConnContext = f\n\treturn o\n}\n\n// SetCheckFramePooling sets a flag to enable frame pooling checks such as leaks or bad releases\nfunc (o *ChannelOpts) SetCheckFramePooling() *ChannelOpts {\n\to.CheckFramePooling = true\n\treturn o\n}\n\n// SetServeTLS sets the ServeTLS flag to enable/disable TLS for test server\nfunc (o *ChannelOpts) SetServeTLS(serveTLS bool) *ChannelOpts {\n\to.ServeTLS = serveTLS\n\treturn o\n}\n\nfunc defaultString(v string, defaultValue string) string {\n\tif v == \"\" {\n\t\treturn defaultValue\n\t}\n\treturn v\n}\n\n// NewOpts returns a new ChannelOpts that can be used in a chained fashion.\nfunc NewOpts() *ChannelOpts { return &ChannelOpts{} }\n\n// DefaultOpts will return opts if opts is non-nil, NewOpts otherwise.\nfunc DefaultOpts(opts *ChannelOpts) *ChannelOpts {\n\tif opts == nil {\n\t\treturn NewOpts()\n\t}\n\treturn opts\n}\n\n// WrapLogger wraps the given logger with extra verification.\nfunc (v *LogVerification) WrapLogger(t testing.TB, l tchannel.Logger) tchannel.Logger {\n\treturn errorLogger{l, t, v, &errorLoggerState{\n\t\tmatchCount: make([]atomic.Uint32, len(v.Filters)),\n\t}}\n}\n"
  },
  {
    "path": "testutils/channel_t.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"testing\"\n\n\t\"github.com/uber/tchannel-go\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc updateOptsLogger(opts *ChannelOpts) {\n\tif opts.Logger == nil && *connectionLog {\n\t\topts.Logger = tchannel.SimpleLogger\n\t}\n}\n\nfunc updateOptsForTest(t testing.TB, opts *ChannelOpts) {\n\tupdateOptsLogger(opts)\n\n\t// If there's no logger, then register the test logger which will record\n\t// everything to a buffer, and print out the buffer if the test fails.\n\tif opts.Logger == nil {\n\t\ttl := newTestLogger(t)\n\t\topts.Logger = tl\n\t\topts.addPostFn(tl.report)\n\t}\n\n\tif !opts.LogVerification.Disabled {\n\t\topts.Logger = opts.LogVerification.WrapLogger(t, opts.Logger)\n\t}\n}\n\n// WithServer sets up a TChannel that is listening and runs the given function with the channel.\nfunc WithServer(t testing.TB, opts *ChannelOpts, f func(ch *tchannel.Channel, hostPort string)) {\n\topts = opts.Copy()\n\tupdateOptsForTest(t, opts)\n\tch := NewServer(t, opts)\n\tf(ch, ch.PeerInfo().HostPort)\n\tch.Close()\n}\n\n// NewServer returns a new TChannel server that listens on :0.\nfunc NewServer(t testing.TB, opts *ChannelOpts) *tchannel.Channel {\n\treturn newServer(t, opts.Copy())\n}\n\n// newServer must be passed non-nil opts that may be mutated to include\n// post-verification steps.\nfunc newServer(t testing.TB, opts *ChannelOpts) *tchannel.Channel {\n\tupdateOptsForTest(t, opts)\n\tch, err := NewServerChannel(opts)\n\trequire.NoError(t, err, \"NewServerChannel failed\")\n\treturn ch\n}\n\n// NewClient returns a new TChannel that is not listening.\nfunc NewClient(t testing.TB, opts *ChannelOpts) *tchannel.Channel {\n\treturn newClient(t, opts.Copy())\n}\n\n// newClient must be passed non-nil opts that may be mutated to include\n// post-verification steps.\nfunc newClient(t testing.TB, opts *ChannelOpts) *tchannel.Channel {\n\tupdateOptsForTest(t, opts)\n\tch, err := NewClientChannel(opts)\n\trequire.NoError(t, err, \"NewServerChannel failed\")\n\treturn ch\n}\n"
  },
  {
    "path": "testutils/conn.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"net\"\n\t\"testing\"\n)\n\n// GetClosedHostPort will return a host:port that will refuse connections.\nfunc GetClosedHostPort(t testing.TB) string {\n\tlistener, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\tt.Fatalf(\"net.Listen failed: %v\", err)\n\t\treturn \"\"\n\t}\n\n\tif err := listener.Close(); err != nil {\n\t\tt.Fatalf(\"listener.Close failed\")\n\t\treturn \"\"\n\t}\n\n\treturn listener.Addr().String()\n}\n\n// GetAcceptCloseHostPort returns a host:port that will accept a connection then\n// immediately close it. The returned function can be used to stop the listener.\nfunc GetAcceptCloseHostPort(t testing.TB) (string, func()) {\n\tlistener, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\tt.Fatalf(\"net.Listen failed: %v\", err)\n\t\treturn \"\", nil\n\t}\n\n\tgo func() {\n\t\tfor {\n\t\t\tconn, err := listener.Accept()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconn.Close()\n\t\t}\n\t}()\n\n\treturn listener.Addr().String(), func() {\n\t\tif err := listener.Close(); err != nil {\n\t\t\tt.Fatalf(\"listener.Close failed\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "testutils/counter.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"sync\"\n\n\t\"go.uber.org/atomic\"\n)\n\n// Decrement is the interface returned by Decrementor.\ntype Decrement interface {\n\t// Single returns whether any more tokens are remaining.\n\tSingle() bool\n\n\t// Multiple tries to get n tokens. It returns the actual amount of tokens\n\t// available to use. If this is 0, it means there are no tokens left.\n\tMultiple(n int) int\n}\n\ntype decrementor struct {\n\tn atomic.Int64\n}\n\nfunc (d *decrementor) Single() bool {\n\treturn d.n.Dec() >= 0\n}\n\nfunc (d *decrementor) Multiple(n int) int {\n\tdecBy := -1 * int64(n)\n\tdecremented := d.n.Add(decBy)\n\tif decremented <= decBy {\n\t\t// Already out of tokens before this decrement.\n\t\treturn 0\n\t} else if decremented < 0 {\n\t\t// Not enough tokens, return how many tokens we actually could decrement.\n\t\treturn n + int(decremented)\n\t}\n\n\treturn n\n}\n\n// Decrementor returns a function that can be called from multiple goroutines and ensures\n// it will only return true n times.\nfunc Decrementor(n int) Decrement {\n\treturn &decrementor{\n\t\tn: *atomic.NewInt64(int64(n)),\n\t}\n}\n\n// Batch returns a slice with n broken into batches of size batchSize.\nfunc Batch(n, batchSize int) []int {\n\tfullBatches := n / batchSize\n\tbatches := make([]int, 0, fullBatches+1)\n\tfor i := 0; i < fullBatches; i++ {\n\t\tbatches = append(batches, batchSize)\n\t}\n\tif remaining := n % batchSize; remaining > 0 {\n\t\tbatches = append(batches, remaining)\n\t}\n\treturn batches\n}\n\n// Buckets splits n over the specified number of buckets.\nfunc Buckets(n int, numBuckets int) []int {\n\tperBucket := n / numBuckets\n\n\tbuckets := make([]int, numBuckets)\n\tfor i := range buckets {\n\t\tbuckets[i] = perBucket\n\t\tif i == 0 {\n\t\t\tbuckets[i] += n % numBuckets\n\t\t}\n\t}\n\treturn buckets\n}\n\n// RunN runs the given f n times (and passes the run's index) and waits till they complete.\n// It starts n-1 goroutines, and runs one instance in the current goroutine.\nfunc RunN(n int, f func(i int)) {\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < n-1; i++ {\n\t\twg.Add(1)\n\t\tgo func(i int) {\n\t\t\tdefer wg.Done()\n\t\t\tf(i)\n\t\t}(i)\n\t}\n\tf(n - 1)\n\twg.Wait()\n}\n"
  },
  {
    "path": "testutils/counter_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"math/rand\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc testDecrementor(t *testing.T, f func(dec Decrement) int) {\n\tconst count = 10000\n\tconst numGoroutines = 100\n\n\tdec := Decrementor(count)\n\tresults := make(chan int, numGoroutines)\n\n\tfor i := 0; i < numGoroutines; i++ {\n\t\tgo func() {\n\t\t\tresults <- f(dec)\n\t\t}()\n\t}\n\n\tvar total int\n\tfor i := 0; i < numGoroutines; i++ {\n\t\ttotal += <-results\n\t}\n\tassert.Equal(t, count, total, \"Count mismatch\")\n}\n\nfunc TestDecrementSingle(t *testing.T) {\n\ttestDecrementor(t, func(dec Decrement) int {\n\t\tcount := 0\n\t\tfor dec.Single() {\n\t\t\tcount++\n\t\t}\n\t\treturn count\n\t})\n}\n\nfunc TestDecrementMultiple(t *testing.T) {\n\ttestDecrementor(t, func(dec Decrement) int {\n\t\tcount := 0\n\t\tfor {\n\t\t\ttokens := dec.Multiple(rand.Intn(100) + 1)\n\t\t\tif tokens == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcount += tokens\n\t\t}\n\t\treturn count\n\t})\n}\n\nfunc TestBatch(t *testing.T) {\n\ttests := []struct {\n\t\tn     int\n\t\tbatch int\n\t\twant  []int\n\t}{\n\t\t{40, 10, []int{10, 10, 10, 10}},\n\t\t{5, 10, []int{5}},\n\t\t{45, 10, []int{10, 10, 10, 10, 5}},\n\t}\n\n\tfor _, tt := range tests {\n\t\tgot := Batch(tt.n, tt.batch)\n\t\tassert.Equal(t, tt.want, got, \"Batch(%v, %v) unexpected result\", tt.n, tt.batch)\n\t}\n}\n\nfunc TestBuckets(t *testing.T) {\n\ttests := []struct {\n\t\tn       int\n\t\tbuckets int\n\t\twant    []int\n\t}{\n\t\t{2, 3, []int{2, 0, 0}},\n\t\t{3, 3, []int{1, 1, 1}},\n\t\t{4, 3, []int{2, 1, 1}},\n\t}\n\n\tfor _, tt := range tests {\n\t\tgot := Buckets(tt.n, tt.buckets)\n\t\tassert.Equal(t, tt.want, got, \"Buckets(%v, %v) unexpected result\", tt.n, tt.buckets)\n\t}\n}\n"
  },
  {
    "path": "testutils/data.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"encoding/base32\"\n\t\"encoding/binary\"\n\t\"math/rand\"\n\t\"sync\"\n)\n\n// This file contains functions for tests to access internal tchannel state.\n// Since it has a _test.go suffix, it is only compiled with tests in this package.\n\nvar (\n\trandCache []byte\n\trandMut   sync.RWMutex\n)\n\nfunc checkCacheSize(n int) {\n\t// Start with a reasonably large cache.\n\tif n < 1024 {\n\t\tn = 1024\n\t}\n\n\trandMut.RLock()\n\tcurSize := len(randCache)\n\trandMut.RUnlock()\n\n\t// The cache needs to be at least twice as large as the requested size.\n\tif curSize >= n*2 {\n\t\treturn\n\t}\n\n\tresizeCache(n)\n}\n\nfunc resizeCache(n int) {\n\trandMut.Lock()\n\tdefer randMut.Unlock()\n\n\t// Double check under the write lock\n\tif len(randCache) >= n*2 {\n\t\treturn\n\t}\n\n\tnewSize := (n * 2 / 8) * 8\n\tnewCache := make([]byte, newSize)\n\tcopied := copy(newCache, randCache)\n\tfor i := copied; i < newSize; i += 8 {\n\t\tn := rand.Int63()\n\t\tbinary.BigEndian.PutUint64(newCache[i:], uint64(n))\n\t}\n\trandCache = newCache\n}\n\n// RandBytes returns n random byte slice that points to a shared random byte array.\n// Since the underlying random array is shared, the returned byte slice must NOT be modified.\nfunc RandBytes(n int) []byte {\n\tconst maxSize = 2 * 1024 * 1024\n\tdata := make([]byte, 0, n)\n\tfor i := 0; i < n; i += maxSize {\n\t\ts := n - i\n\t\tif s > maxSize {\n\t\t\ts = maxSize\n\t\t}\n\t\tdata = append(data, randBytes(s)...)\n\t}\n\treturn data\n}\n\n// RandString returns a random alphanumeric string for testing.\nfunc RandString(n int) string {\n\tencoding := base32.StdEncoding\n\tnumBytes := encoding.DecodedLen(n) + 5\n\treturn base32.StdEncoding.EncodeToString(RandBytes(numBytes))[:n]\n}\n\nfunc randBytes(n int) []byte {\n\tcheckCacheSize(n)\n\n\trandMut.RLock()\n\tstartAt := rand.Intn(len(randCache) - n)\n\tbs := randCache[startAt : startAt+n]\n\trandMut.RUnlock()\n\treturn bs\n}\n"
  },
  {
    "path": "testutils/echo.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"golang.org/x/net/context\"\n)\n\nconst (\n\t_defaultTimeout = 300 * time.Millisecond\n)\n\n// CallEcho calls the \"echo\" endpoint from the given src to target.\nfunc CallEcho(\n\tsrc *tchannel.Channel,\n\ttargetHostPort string,\n\ttargetService string,\n\targs *raw.Args,\n) error {\n\treturn CallEchoWithContext(\n\t\tcontext.Background(),\n\t\tsrc,\n\t\ttargetHostPort,\n\t\ttargetService,\n\t\targs,\n\t)\n}\n\n// CallEchoWithContext calls the \"echo\" endpoint from the given src to target,\n// using any deadline within the given context.Context.\nfunc CallEchoWithContext(\n\tctx context.Context,\n\tsrc *tchannel.Channel,\n\ttargetHostPort string,\n\ttargetService string,\n\targs *raw.Args,\n) error {\n\tif args == nil {\n\t\targs = &raw.Args{}\n\t}\n\n\ttimeout := _defaultTimeout\n\tdl, ok := ctx.Deadline()\n\tif ok {\n\t\ttimeout = time.Until(dl)\n\t}\n\n\tctx, cancel := tchannel.NewContextBuilder(Timeout(timeout)).\n\t\tSetConnectBaseContext(ctx).\n\t\tSetFormat(args.Format).\n\t\tBuild()\n\tdefer cancel()\n\n\t_, _, _, err := raw.Call(\n\t\tctx,\n\t\tsrc,\n\t\ttargetHostPort,\n\t\ttargetService,\n\t\t\"echo\",\n\t\targs.Arg2,\n\t\targs.Arg3,\n\t)\n\n\treturn err\n}\n\n// AssertEcho calls the \"echo\" endpoint with random data, and asserts\n// that the returned data matches the arguments \"echo\" was called with.\nfunc AssertEcho(tb testing.TB, src *tchannel.Channel, targetHostPort, targetService string) {\n\tctx, cancel := tchannel.NewContext(Timeout(_defaultTimeout))\n\tdefer cancel()\n\n\targs := &raw.Args{\n\t\tArg2: RandBytes(1000),\n\t\tArg3: RandBytes(1000),\n\t}\n\n\targ2, arg3, _, err := raw.Call(ctx, src, targetHostPort, targetService, \"echo\", args.Arg2, args.Arg3)\n\tif !assert.NoError(tb, err, \"Call from %v (%v) to %v (%v) failed\", src.ServiceName(), src.PeerInfo().HostPort, targetService, targetHostPort) {\n\t\treturn\n\t}\n\n\tassert.Equal(tb, args.Arg2, arg2, \"Arg2 mismatch\")\n\tassert.Equal(tb, args.Arg3, arg3, \"Arg3 mismatch\")\n}\n\n// RegisterEcho registers an echo endpoint on the given channel. The optional provided\n// function is run before the handler returns.\nfunc RegisterEcho(src tchannel.Registrar, f func()) {\n\tRegisterFunc(src, \"echo\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\tif f != nil {\n\t\t\tf()\n\t\t}\n\t\treturn &raw.Res{Arg2: args.Arg2, Arg3: args.Arg3}, nil\n\t})\n}\n\n// Ping sends a ping from src to target.\nfunc Ping(src, target *tchannel.Channel) error {\n\tctx, cancel := tchannel.NewContext(Timeout(_defaultTimeout))\n\tdefer cancel()\n\n\treturn src.Ping(ctx, target.PeerInfo().HostPort)\n}\n"
  },
  {
    "path": "testutils/goroutines/stacks.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage goroutines\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Stack represents a single Goroutine's stack.\ntype Stack struct {\n\tid            int\n\tstate         string\n\tfirstFunction string\n\tfullStack     *bytes.Buffer\n}\n\n// ID returns the goroutine ID.\nfunc (s Stack) ID() int {\n\treturn s.id\n}\n\n// State returns the Goroutine's state.\nfunc (s Stack) State() string {\n\treturn s.state\n}\n\n// Full returns the full stack trace for this goroutine.\nfunc (s Stack) Full() []byte {\n\treturn s.fullStack.Bytes()\n}\n\nfunc (s Stack) String() string {\n\treturn fmt.Sprintf(\n\t\t\"Goroutine %v in state %v, with %v on top of the stack:\\n%s\",\n\t\ts.id, s.state, s.firstFunction, s.Full())\n}\n\nfunc getStacks(all bool) []Stack {\n\tvar stacks []Stack\n\n\tvar curStack *Stack\n\tstackReader := bufio.NewReader(bytes.NewReader(getStackBuffer(all)))\n\tfor {\n\t\tline, err := stackReader.ReadString('\\n')\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\tpanic(\"stack reader failed\")\n\t\t}\n\n\t\t// If we see the goroutine header, start a new stack.\n\t\tisFirstLine := false\n\t\tif strings.HasPrefix(line, \"goroutine \") {\n\t\t\t// flush any previous stack\n\t\t\tif curStack != nil {\n\t\t\t\tstacks = append(stacks, *curStack)\n\t\t\t}\n\t\t\tid, goState := parseGoStackHeader(line)\n\t\t\tcurStack = &Stack{\n\t\t\t\tid:        id,\n\t\t\t\tstate:     goState,\n\t\t\t\tfullStack: &bytes.Buffer{},\n\t\t\t}\n\t\t\tisFirstLine = true\n\t\t}\n\t\tcurStack.fullStack.WriteString(line)\n\t\tif !isFirstLine && curStack.firstFunction == \"\" {\n\t\t\tcurStack.firstFunction = parseFirstFunc(line)\n\t\t}\n\t}\n\n\tif curStack != nil {\n\t\tstacks = append(stacks, *curStack)\n\t}\n\treturn stacks\n}\n\n// GetAll returns the stacks for all running goroutines.\nfunc GetAll() []Stack {\n\treturn getStacks(true)\n}\n\n// GetCurrentStack returns the stack for the current goroutine.\nfunc GetCurrentStack() Stack {\n\treturn getStacks(false)[0]\n}\n\nfunc getStackBuffer(all bool) []byte {\n\tfor i := 4096; ; i *= 2 {\n\t\tbuf := make([]byte, i)\n\t\tif n := runtime.Stack(buf, all); n < i {\n\t\t\treturn buf\n\t\t}\n\t}\n}\n\nfunc parseFirstFunc(line string) string {\n\tline = strings.TrimSpace(line)\n\tif idx := strings.LastIndex(line, \"(\"); idx > 0 {\n\t\treturn line[:idx]\n\t}\n\treturn line\n}\n\n// parseGoStackHeader parses a stack header that looks like:\n// goroutine 643 [runnable]:\\n\n// And returns the goroutine ID, and the state.\nfunc parseGoStackHeader(line string) (goroutineID int, state string) {\n\tline = strings.TrimSuffix(line, \":\\n\")\n\tparts := strings.SplitN(line, \" \", 3)\n\tif len(parts) != 3 {\n\t\tpanic(fmt.Sprintf(\"unexpected stack header format: %v\", line))\n\t}\n\n\tid, err := strconv.Atoi(parts[1])\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"failed to parse goroutine ID: %v\", parts[1]))\n\t}\n\n\tstate = strings.TrimSuffix(strings.TrimPrefix(parts[2], \"[\"), \"]\")\n\treturn id, state\n}\n"
  },
  {
    "path": "testutils/goroutines/verify.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage goroutines\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\n// filterStacks will filter any stacks excluded by the given VerifyOpts.\nfunc filterStacks(stacks []Stack, skipID int, opts *VerifyOpts) []Stack {\n\tfiltered := stacks[:0]\n\tfor _, stack := range stacks {\n\t\tif stack.ID() == skipID || shouldIgnore(stack) {\n\t\t\tcontinue\n\t\t}\n\t\tif opts.ShouldSkip(stack) {\n\t\t\tcontinue\n\t\t}\n\t\tfiltered = append(filtered, stack)\n\t}\n\treturn filtered\n}\n\nfunc shouldIgnore(s Stack) bool {\n\tswitch funcName := s.firstFunction; funcName {\n\tcase \"testing.RunTests\", \"testing.(*T).Run\":\n\t\treturn strings.HasPrefix(s.State(), \"chan receive\")\n\tcase \"runtime.goexit\":\n\t\treturn strings.HasPrefix(s.State(), \"syscall\")\n\tcase \"os/signal.signal_recv\":\n\t\t// The signal package automatically starts a goroutine when it's imported.\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// IdentifyLeaks looks for extra goroutines, and returns a descriptive error if\n// it finds any.\nfunc IdentifyLeaks(opts *VerifyOpts) error {\n\tcur := GetCurrentStack().id\n\n\tconst maxAttempts = 50\n\tvar stacks []Stack\n\tfor i := 0; i < maxAttempts; i++ {\n\t\tstacks = GetAll()\n\t\tstacks = filterStacks(stacks, cur, opts)\n\n\t\tif len(stacks) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tif i > maxAttempts/2 {\n\t\t\ttime.Sleep(time.Duration(i) * time.Millisecond)\n\t\t} else {\n\t\t\truntime.Gosched()\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"found unexpected goroutines:\\n%s\", stacks)\n}\n\n// VerifyNoLeaks calls IdentifyLeaks and fails the test if it finds any leaked\n// goroutines.\nfunc VerifyNoLeaks(t testing.TB, opts *VerifyOpts) {\n\tif err := IdentifyLeaks(opts); err != nil {\n\t\tt.Error(err.Error())\n\t}\n}\n"
  },
  {
    "path": "testutils/goroutines/verify_opts.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage goroutines\n\nimport \"bytes\"\n\n// VerifyOpts contains\ntype VerifyOpts struct {\n\t// Excludes is a list of strings that will exclude a stack from being considered a leak.\n\tExcludes []string\n}\n\n// ShouldSkip returns whether the given stack should be skipped when doing verification.\nfunc (opts *VerifyOpts) ShouldSkip(s Stack) bool {\n\tif opts == nil || len(opts.Excludes) == 0 {\n\t\treturn false\n\t}\n\n\tfor _, exclude := range opts.Excludes {\n\t\tif bytes.Contains(s.Full(), []byte(exclude)) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "testutils/lists.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport \"time\"\n\n// StrArray will return an array with the given strings.\nfunc StrArray(ss ...string) []string {\n\treturn ss\n}\n\n// StrMap returns a map where the keys are the given strings.\nfunc StrMap(ss ...string) map[string]struct{} {\n\tm := make(map[string]struct{}, len(ss))\n\tfor _, v := range ss {\n\t\tm[v] = struct{}{}\n\t}\n\treturn m\n}\n\n// DurationArray returns an array with the given durations.\nfunc DurationArray(dd ...time.Duration) []time.Duration {\n\treturn dd\n}\n"
  },
  {
    "path": "testutils/logfilter_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/uber/tchannel-go\"\n)\n\nfunc TestLogFilterMatches(t *testing.T) {\n\tmsgFilter := LogFilter{\n\t\tFilter: \"msgFilter\",\n\t}\n\n\tfieldsFilter := LogFilter{\n\t\tFilter: \"msgFilter\",\n\t\tFieldFilters: map[string]string{\n\t\t\t\"f1\": \"v1\",\n\t\t\t\"f2\": \"v2\",\n\t\t},\n\t}\n\n\t// fields takes a varargs list of strings which it reads as:\n\t// key, value, key, value...\n\tfields := func(vals ...string) []tchannel.LogField {\n\t\tfs := make([]tchannel.LogField, len(vals)/2)\n\t\tfor i := 0; i < len(vals); i += 2 {\n\t\t\tfs[i/2] = tchannel.LogField{\n\t\t\t\tKey:   vals[i],\n\t\t\t\tValue: vals[i+1],\n\t\t\t}\n\t\t}\n\t\treturn fs\n\t}\n\n\ttests := []struct {\n\t\tFilter  LogFilter\n\t\tMessage string\n\t\tFields  []tchannel.LogField\n\t\tMatch   bool\n\t}{\n\t\t{\n\t\t\tFilter:  msgFilter,\n\t\t\tMessage: \"random message\",\n\t\t\tMatch:   false,\n\t\t},\n\t\t{\n\t\t\tFilter:  msgFilter,\n\t\t\tMessage: \"msgFilter\",\n\t\t\tMatch:   true,\n\t\t},\n\t\t{\n\t\t\t// Case matters.\n\t\t\tFilter:  msgFilter,\n\t\t\tMessage: \"msgfilter\",\n\t\t\tMatch:   false,\n\t\t},\n\t\t{\n\t\t\tFilter:  msgFilter,\n\t\t\tMessage: \"abc msgFilterdef\",\n\t\t\tMatch:   true,\n\t\t},\n\t\t{\n\t\t\tFilter:  fieldsFilter,\n\t\t\tMessage: \"random message\",\n\t\t\tFields:  fields(\"f1\", \"v1\", \"f2\", \"v2\"),\n\t\t\tMatch:   false,\n\t\t},\n\t\t{\n\t\t\tFilter:  fieldsFilter,\n\t\t\tMessage: \"msgFilter\",\n\t\t\tFields:  fields(\"f1\", \"v1\", \"f2\", \"v2\"),\n\t\t\tMatch:   true,\n\t\t},\n\t\t{\n\t\t\t// Field mismatch should not match.\n\t\t\tFilter:  fieldsFilter,\n\t\t\tMessage: \"msgFilter\",\n\t\t\tFields:  fields(\"f1\", \"v0\", \"f2\", \"v2\"),\n\t\t\tMatch:   false,\n\t\t},\n\t\t{\n\t\t\t// Missing field should not match.\n\t\t\tFilter:  fieldsFilter,\n\t\t\tMessage: \"msgFilter\",\n\t\t\tFields:  fields(\"f2\", \"v2\"),\n\t\t\tMatch:   false,\n\t\t},\n\t\t{\n\t\t\t// Extra fields are OK.\n\t\t\tFilter:  fieldsFilter,\n\t\t\tMessage: \"msgFilter\",\n\t\t\tFields:  fields(\"f1\", \"v0\", \"f2\", \"v2\", \"f3\", \"v3\"),\n\t\t\tMatch:   false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tgot := tt.Filter.Matches(tt.Message, tt.Fields)\n\t\tassert.Equal(t, tt.Match, got, \"Filter %+v .Matches(%v, %v) mismatch\",\n\t\t\ttt.Filter, tt.Message, tt.Fields)\n\t}\n}\n"
  },
  {
    "path": "testutils/logger.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\n\t\"go.uber.org/atomic\"\n)\n\n// writer is shared between multiple loggers, and serializes acccesses to\n// the underlying buffer.\ntype writer struct {\n\tsync.Mutex\n\tbuf *bytes.Buffer\n}\n\n// testLogger is a logger that writes all output to a buffer, and can report\n// the logs if the test has failed.\ntype testLogger struct {\n\tt      testing.TB\n\tfields tchannel.LogFields\n\tw      *writer\n}\n\ntype errorLoggerState struct {\n\tmatchCount []atomic.Uint32\n}\n\ntype errorLogger struct {\n\ttchannel.Logger\n\tt testing.TB\n\tv *LogVerification\n\ts *errorLoggerState\n}\n\nfunc newWriter() *writer {\n\treturn &writer{buf: &bytes.Buffer{}}\n}\n\nfunc (w *writer) withLock(f func(*bytes.Buffer)) {\n\tw.Lock()\n\tf(w.buf)\n\tw.Unlock()\n}\n\n// Matches returns true if the message and fields match the filter.\nfunc (f LogFilter) Matches(msg string, fields tchannel.LogFields) bool {\n\t// First check the message and ensure it contains Filter\n\tif !strings.Contains(msg, f.Filter) {\n\t\treturn false\n\t}\n\n\t// if there are no field filters, then the message match is enough.\n\tif len(f.FieldFilters) == 0 {\n\t\treturn true\n\t}\n\n\tfieldsMap := make(map[string]interface{})\n\tfor _, field := range fields {\n\t\tfieldsMap[field.Key] = field.Value\n\t}\n\n\tfor k, filter := range f.FieldFilters {\n\t\tvalue, ok := fieldsMap[k]\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\n\t\tif !strings.Contains(fmt.Sprint(value), filter) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\nfunc newTestLogger(t testing.TB) testLogger {\n\treturn testLogger{t, nil, newWriter()}\n}\n\nfunc (l testLogger) Enabled(level tchannel.LogLevel) bool {\n\treturn true\n}\n\nfunc (l testLogger) log(prefix string, msg string) {\n\tlogLine := fmt.Sprintf(\"%s [%v] %v %v\\n\", time.Now().Format(\"15:04:05.000000\"), prefix, msg, l.Fields())\n\tl.w.withLock(func(w *bytes.Buffer) {\n\t\tw.WriteString(logLine)\n\t})\n}\n\nfunc (l testLogger) Fatal(msg string) {\n\tl.log(\"F\", msg)\n}\n\nfunc (l testLogger) Error(msg string) {\n\tl.log(\"E\", msg)\n}\n\nfunc (l testLogger) Warn(msg string) {\n\tl.log(\"W\", msg)\n}\n\nfunc (l testLogger) Info(msg string) {\n\tl.log(\"I\", msg)\n}\n\nfunc (l testLogger) Infof(msg string, args ...interface{}) {\n\tl.log(\"I\", fmt.Sprintf(msg, args...))\n}\n\nfunc (l testLogger) Debug(msg string) {\n\tl.log(\"D\", msg)\n}\n\nfunc (l testLogger) Debugf(msg string, args ...interface{}) {\n\tl.log(\"D\", fmt.Sprintf(msg, args...))\n}\n\nfunc (l testLogger) Fields() tchannel.LogFields {\n\treturn l.fields\n}\n\nfunc (l testLogger) WithFields(fields ...tchannel.LogField) tchannel.Logger {\n\texisting := len(l.Fields())\n\tnewFields := make(tchannel.LogFields, existing+len(fields))\n\tcopy(newFields, l.Fields())\n\tcopy(newFields[existing:], fields)\n\treturn testLogger{l.t, newFields, l.w}\n}\n\nfunc (l testLogger) report() {\n\tif os.Getenv(\"LOGS_ON_FAILURE\") == \"\" {\n\t\treturn\n\t}\n\n\tif l.t.Failed() {\n\t\tl.w.withLock(func(w *bytes.Buffer) {\n\t\t\tl.t.Logf(\"Debug logs:\\n%s\", w.String())\n\t\t})\n\t}\n}\n\n// checkFilters returns whether the message can be ignored by the filters.\nfunc (l errorLogger) checkFilters(msg string) bool {\n\tmatch := -1\n\tfor i, filter := range l.v.Filters {\n\t\tif filter.Matches(msg, l.Fields()) {\n\t\t\tmatch = i\n\t\t}\n\t}\n\n\tif match == -1 {\n\t\treturn false\n\t}\n\n\tmatchCount := l.s.matchCount[match].Inc()\n\treturn uint(matchCount) <= l.v.Filters[match].Count\n}\n\nfunc (l errorLogger) checkErr(prefix, msg string) {\n\tif l.checkFilters(msg) {\n\t\treturn\n\t}\n\n\tl.t.Errorf(\"Unexpected log: %v: %s %v\", prefix, msg, l.Logger.Fields())\n}\n\nfunc (l errorLogger) Fatal(msg string) {\n\tl.checkErr(\"[Fatal]\", msg)\n\tl.Logger.Fatal(msg)\n}\n\nfunc (l errorLogger) Error(msg string) {\n\tl.checkErr(\"[Error]\", msg)\n\tl.Logger.Error(msg)\n}\n\nfunc (l errorLogger) Warn(msg string) {\n\tl.checkErr(\"[Warn]\", msg)\n\tl.Logger.Warn(msg)\n}\n\nfunc (l errorLogger) WithFields(fields ...tchannel.LogField) tchannel.Logger {\n\treturn errorLogger{l.Logger.WithFields(fields...), l.t, l.v, l.s}\n}\n"
  },
  {
    "path": "testutils/mockhyperbahn/hyperbahn.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage mockhyperbahn\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/hyperbahn\"\n\ththrift \"github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn\"\n\t\"github.com/uber/tchannel-go/json\"\n\t\"github.com/uber/tchannel-go/relay/relaytest\"\n\t\"github.com/uber/tchannel-go/thrift\"\n)\n\n// Mock is up a mock Hyperbahn server for tests.\ntype Mock struct {\n\tsync.RWMutex\n\n\tch              *tchannel.Channel\n\trespCh          chan int\n\tadvertised      []string\n\tdiscoverResults map[string][]string\n}\n\n// New returns a mock Hyperbahn server that can be used for testing.\nfunc New() (*Mock, error) {\n\tstubHost := relaytest.NewStubRelayHost()\n\tch, err := tchannel.NewChannel(\"hyperbahn\", &tchannel.ChannelOptions{\n\t\tRelayHost:          stubHost,\n\t\tRelayLocalHandlers: []string{\"hyperbahn\"},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmh := &Mock{\n\t\tch:              ch,\n\t\trespCh:          make(chan int),\n\t\tdiscoverResults: make(map[string][]string),\n\t}\n\tif err := json.Register(ch, json.Handlers{\"ad\": mh.adHandler}, nil); err != nil {\n\t\treturn nil, err\n\t}\n\n\tthriftServer := thrift.NewServer(ch)\n\tthriftServer.Register(hthrift.NewTChanHyperbahnServer(mh))\n\n\treturn mh, ch.ListenAndServe(\"127.0.0.1:0\")\n}\n\n// SetDiscoverResult sets the given hostPorts as results for the Discover call.\nfunc (h *Mock) SetDiscoverResult(serviceName string, hostPorts []string) {\n\th.Lock()\n\tdefer h.Unlock()\n\n\th.discoverResults[serviceName] = hostPorts\n}\n\n// Discover returns the IPs for a discovery query if some were set using SetDiscoverResult.\n// Otherwise, it returns an error.\nfunc (h *Mock) Discover(ctx thrift.Context, query *hthrift.DiscoveryQuery) (*hthrift.DiscoveryResult_, error) {\n\th.RLock()\n\tdefer h.RUnlock()\n\n\thostPorts, ok := h.discoverResults[query.ServiceName]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"no discovery results set for %v\", query.ServiceName)\n\t}\n\n\tpeers, err := toServicePeers(hostPorts)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid discover result set: %v\", err)\n\t}\n\n\treturn &hthrift.DiscoveryResult_{\n\t\tPeers: peers,\n\t}, nil\n}\n\n// Configuration returns a hyperbahn.Configuration object used to configure a\n// hyperbahn.Client to talk to this mock server.\nfunc (h *Mock) Configuration() hyperbahn.Configuration {\n\treturn hyperbahn.Configuration{\n\t\tInitialNodes: []string{h.ch.PeerInfo().HostPort},\n\t}\n}\n\n// Channel returns the underlying tchannel that implements relaying.\nfunc (h *Mock) Channel() *tchannel.Channel {\n\treturn h.ch\n}\n\nfunc (h *Mock) adHandler(ctx json.Context, req *hyperbahn.AdRequest) (*hyperbahn.AdResponse, error) {\n\tcallerHostPort := tchannel.CurrentCall(ctx).RemotePeer().HostPort\n\th.Lock()\n\tfor _, s := range req.Services {\n\t\th.advertised = append(h.advertised, s.Name)\n\t\tsc := h.ch.GetSubChannel(s.Name, tchannel.Isolated)\n\t\tsc.Peers().Add(callerHostPort)\n\t}\n\th.Unlock()\n\n\tselect {\n\tcase n := <-h.respCh:\n\t\tif n == 0 {\n\t\t\treturn nil, errors.New(\"error\")\n\t\t}\n\t\treturn &hyperbahn.AdResponse{ConnectionCount: n}, nil\n\tdefault:\n\t\t// Return a default response\n\t\treturn &hyperbahn.AdResponse{ConnectionCount: 3}, nil\n\t}\n}\n\n// GetAdvertised returns the list of services registered.\nfunc (h *Mock) GetAdvertised() []string {\n\th.RLock()\n\tdefer h.RUnlock()\n\n\treturn h.advertised\n}\n\n// Close stops the mock Hyperbahn server.\nfunc (h *Mock) Close() {\n\th.ch.Close()\n}\n\n// QueueError queues an error to be returned on the next advertise call.\nfunc (h *Mock) QueueError() {\n\th.respCh <- 0\n}\n\n// QueueResponse queues a response from Hyperbahn.\n// numConnections must be greater than 0.\nfunc (h *Mock) QueueResponse(numConnections int) {\n\tif numConnections <= 0 {\n\t\tpanic(\"QueueResponse must have numConnections > 0\")\n\t}\n\n\th.respCh <- numConnections\n}\n"
  },
  {
    "path": "testutils/mockhyperbahn/hyperbahn_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage mockhyperbahn_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/hyperbahn\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\t\"github.com/uber/tchannel-go/testutils/mockhyperbahn\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/atomic\"\n)\n\nvar config = struct {\n\thyperbahnConfig hyperbahn.Configuration\n}{}\n\n// setupServer is the application code we are attempting to test.\nfunc setupServer() (*hyperbahn.Client, error) {\n\tch, err := tchannel.NewChannel(\"myservice\", nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := ch.ListenAndServe(\"127.0.0.1:0\"); err != nil {\n\t\treturn nil, err\n\t}\n\n\tclient, err := hyperbahn.NewClient(ch, config.hyperbahnConfig, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn client, client.Advertise()\n}\n\nfunc newAdvertisedEchoServer(t *testing.T, name string, mockHB *mockhyperbahn.Mock, f func()) *tchannel.Channel {\n\tserver := testutils.NewServer(t, &testutils.ChannelOpts{\n\t\tServiceName: name,\n\t})\n\ttestutils.RegisterEcho(server, f)\n\n\thbClient, err := hyperbahn.NewClient(server, mockHB.Configuration(), nil)\n\trequire.NoError(t, err, \"Failed to set up Hyperbahn client\")\n\trequire.NoError(t, hbClient.Advertise(), \"Advertise failed\")\n\n\treturn server\n}\n\nfunc TestMockHyperbahn(t *testing.T) {\n\tmh, err := mockhyperbahn.New()\n\trequire.NoError(t, err, \"mock hyperbahn failed\")\n\tdefer mh.Close()\n\n\tconfig.hyperbahnConfig = mh.Configuration()\n\t_, err = setupServer()\n\trequire.NoError(t, err, \"setupServer failed\")\n\tassert.Equal(t, []string{\"myservice\"}, mh.GetAdvertised())\n}\n\nfunc TestMockDiscovery(t *testing.T) {\n\tmh, err := mockhyperbahn.New()\n\trequire.NoError(t, err, \"mock hyperbahn failed\")\n\tdefer mh.Close()\n\n\tpeers := []string{\n\t\t\"1.3.5.7:1456\",\n\t\t\"255.255.255.255:25\",\n\t}\n\tmh.SetDiscoverResult(\"discover-svc\", peers)\n\n\tconfig.hyperbahnConfig = mh.Configuration()\n\tclient, err := setupServer()\n\trequire.NoError(t, err, \"setupServer failed\")\n\n\tgotPeers, err := client.Discover(\"discover-svc\")\n\trequire.NoError(t, err, \"Discover failed\")\n\tassert.Equal(t, peers, gotPeers, \"Discover returned invalid peers\")\n}\n\nfunc TestMockForwards(t *testing.T) {\n\tmockHB, err := mockhyperbahn.New()\n\trequire.NoError(t, err, \"Failed to set up mock hyperbahm\")\n\n\tcalled := false\n\tserver := newAdvertisedEchoServer(t, \"svr\", mockHB, func() {\n\t\tcalled = true\n\t})\n\tdefer server.Close()\n\tclient := newAdvertisedEchoServer(t, \"client\", mockHB, nil)\n\tdefer client.Close()\n\n\tctx, cancel := tchannel.NewContext(time.Second)\n\tdefer cancel()\n\n\t_, _, _, err = raw.CallSC(ctx, client.GetSubChannel(\"svr\"), \"echo\", nil, nil)\n\trequire.NoError(t, err, \"Call failed\")\n\trequire.True(t, called, \"Advertised server was not called\")\n}\n\nfunc TestMockIgnoresDown(t *testing.T) {\n\tmockHB, err := mockhyperbahn.New()\n\trequire.NoError(t, err, \"Failed to set up mock hyperbahm\")\n\n\tvar (\n\t\tmoe1Called atomic.Bool\n\t\tmoe2Called atomic.Bool\n\t)\n\n\tmoe1 := newAdvertisedEchoServer(t, \"moe\", mockHB, func() { moe1Called.Store(true) })\n\tdefer moe1.Close()\n\tmoe2 := newAdvertisedEchoServer(t, \"moe\", mockHB, func() { moe2Called.Store(true) })\n\tdefer moe2.Close()\n\tclient := newAdvertisedEchoServer(t, \"client\", mockHB, nil)\n\n\tctx, cancel := tchannel.NewContext(time.Second)\n\tdefer cancel()\n\n\tfor i := 0; i < 20; i++ {\n\t\t_, _, _, err = raw.CallSC(ctx, client.GetSubChannel(\"moe\"), \"echo\", nil, nil)\n\t\tassert.NoError(t, err, \"Call failed\")\n\t}\n\n\trequire.True(t, moe1Called.Load(), \"moe1 not called\")\n\trequire.True(t, moe2Called.Load(), \"moe2 not called\")\n\n\t// If moe2 is brought down, all calls should now be sent to moe1.\n\tmoe2.Close()\n\n\t// Wait for the mock HB to have 0 connections to moe\n\tok := testutils.WaitFor(time.Second, func() bool {\n\t\tin, out := mockHB.Channel().Peers().GetOrAdd(moe2.PeerInfo().HostPort).NumConnections()\n\t\treturn in+out == 0\n\t})\n\trequire.True(t, ok, \"Failed waiting for mock HB to have 0 connections\")\n\n\t// Make sure that all calls succeed (they should all go to moe2)\n\tmoe1Called.Store(false)\n\tmoe2Called.Store(false)\n\tfor i := 0; i < 20; i++ {\n\t\t_, _, _, err = raw.CallSC(ctx, client.GetSubChannel(\"moe\"), \"echo\", nil, nil)\n\t\tassert.NoError(t, err, \"Call failed\")\n\t}\n\n\trequire.True(t, moe1Called.Load(), \"moe1 not called\")\n\trequire.False(t, moe2Called.Load(), \"moe2 should not be called after Close\")\n}\n"
  },
  {
    "path": "testutils/mockhyperbahn/utils.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage mockhyperbahn\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\n\ththrift \"github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn\"\n)\n\nfunc toServicePeer(hostPort string) (*hthrift.ServicePeer, error) {\n\thost, port, err := net.SplitHostPort(hostPort)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid hostPort %v: %v\", hostPort, err)\n\t}\n\n\tip := net.ParseIP(host)\n\tif ip == nil {\n\t\treturn nil, fmt.Errorf(\"host %v is not an ip\", host)\n\t}\n\tip = ip.To4()\n\n\tif len(ip) != net.IPv4len {\n\t\treturn nil, fmt.Errorf(\"ip %v is not a v4 ip, expected length to be %v, got %v\",\n\t\t\thost, net.IPv4len, len(ip))\n\t}\n\n\tportInt, err := strconv.Atoi(port)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid port %v: %v\", port, err)\n\t}\n\n\t// We have 4 bytes for the IP, use that as an int.\n\tipInt := int32(uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3]))\n\treturn &hthrift.ServicePeer{\n\t\tIP:   &hthrift.IpAddress{Ipv4: &ipInt},\n\t\tPort: int32(portInt),\n\t}, nil\n}\n\nfunc toServicePeers(hostPorts []string) ([]*hthrift.ServicePeer, error) {\n\tvar peers []*hthrift.ServicePeer\n\tfor _, hostPort := range hostPorts {\n\t\tpeer, err := toServicePeer(hostPort)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tpeers = append(peers, peer)\n\t}\n\n\treturn peers, nil\n}\n"
  },
  {
    "path": "testutils/now.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\n// StubClock is a fake wall-clock, exposing a Now() method that returns a\n// test-controlled time.\ntype StubClock struct {\n\tmu  sync.Mutex\n\tcur time.Time\n}\n\n// NewStubClock returns a fake wall-clock object\nfunc NewStubClock(initial time.Time) *StubClock {\n\treturn &StubClock{\n\t\tcur: initial,\n\t}\n}\n\n// Now returns the current time stored in StubClock\nfunc (c *StubClock) Now() time.Time {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\treturn c.cur\n}\n\n// Elapse increments the time returned by Now()\nfunc (c *StubClock) Elapse(addAmt time.Duration) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tc.cur = c.cur.Add(addAmt)\n}\n"
  },
  {
    "path": "testutils/random_bench_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"testing\"\n)\n\nfunc benchmarkRandom(b *testing.B, numBytes int) {\n\tvar bs []byte\n\tfor i := 0; i < b.N; i++ {\n\t\trandCache = nil\n\t\tbs = RandBytes(numBytes)\n\t}\n\tio.Copy(ioutil.Discard, bytes.NewReader(bs))\n}\n\nfunc BenchmarkRandom256(b *testing.B) {\n\tbenchmarkRandom(b, 256)\n}\n\nfunc BenchmarkRandom1024(b *testing.B) {\n\tbenchmarkRandom(b, 1024)\n}\n\nfunc BenchmarkRandom4096(b *testing.B) {\n\tbenchmarkRandom(b, 4096)\n}\n\nfunc BenchmarkRandom16384(b *testing.B) {\n\tbenchmarkRandom(b, 16384)\n}\n\nfunc BenchmarkRandom32768(b *testing.B) {\n\tbenchmarkRandom(b, 32768)\n}\n"
  },
  {
    "path": "testutils/relay.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/uber/tchannel-go\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/atomic\"\n)\n\ntype frameRelay struct {\n\tsync.Mutex // protects conns\n\n\tt           testing.TB\n\tdestination string\n\trelayFunc   func(outgoing bool, f *tchannel.Frame) *tchannel.Frame\n\tclosed      atomic.Uint32\n\tconns       []net.Conn\n\twg          sync.WaitGroup\n}\n\nfunc (r *frameRelay) listen() (listenHostPort string, cancel func()) {\n\tconn, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\trequire.NoError(r.t, err, \"net.Listen failed\")\n\n\tgo func() {\n\t\tfor {\n\t\t\tc, err := conn.Accept()\n\t\t\tif err != nil {\n\t\t\t\tif r.closed.Load() == 0 {\n\t\t\t\t\tr.t.Errorf(\"Accept failed: %v\", err)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tr.Lock()\n\t\t\tr.conns = append(r.conns, c)\n\t\t\tr.Unlock()\n\n\t\t\tr.relayConn(c)\n\t\t}\n\t}()\n\n\treturn conn.Addr().String(), func() {\n\t\tr.closed.Inc()\n\t\tconn.Close()\n\t\tr.Lock()\n\t\tfor _, c := range r.conns {\n\t\t\tc.Close()\n\t\t}\n\t\tr.Unlock()\n\t\t// Wait for all the outbound connections we created to close.\n\t\tr.wg.Wait()\n\t}\n}\n\nfunc (r *frameRelay) relayConn(c net.Conn) {\n\toutC, err := net.Dial(\"tcp\", r.destination)\n\tif !assert.NoError(r.t, err, \"relay connection failed\") {\n\t\treturn\n\t}\n\tr.Lock()\n\tdefer r.Unlock()\n\n\tif r.closed.Load() > 0 {\n\t\toutC.Close()\n\t\treturn\n\t}\n\n\tr.conns = append(r.conns, outC)\n\n\tr.wg.Add(2)\n\tgo r.relayBetween(true /* outgoing */, c, outC)\n\tgo r.relayBetween(false /* outgoing */, outC, c)\n}\n\nfunc (r *frameRelay) relayBetween(outgoing bool, c net.Conn, outC net.Conn) {\n\tdefer r.wg.Done()\n\n\tframe := tchannel.NewFrame(tchannel.MaxFramePayloadSize)\n\tfor {\n\t\terr := frame.ReadIn(c)\n\t\tif err == io.EOF {\n\t\t\t// Connection gracefully closed.\n\t\t\treturn\n\t\t}\n\t\tif err != nil && r.closed.Load() > 0 {\n\t\t\t// Once the relay is shutdown, we expect connection errors.\n\t\t\treturn\n\t\t}\n\t\tif !assert.NoError(r.t, err, \"read frame failed\") {\n\t\t\treturn\n\t\t}\n\n\t\toutFrame := r.relayFunc(outgoing, frame)\n\t\tif outFrame == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\terr = outFrame.WriteOut(outC)\n\t\tif err != nil && r.closed.Load() > 0 {\n\t\t\t// Once the relay is shutdown, we expect connection errors.\n\t\t\treturn\n\t\t}\n\t\tif !assert.NoError(r.t, err, \"write frame failed\") {\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// FrameRelay sets up a relay that can modify frames using relayFunc.\nfunc FrameRelay(t testing.TB, destination string, relayFunc func(outgoing bool, f *tchannel.Frame) *tchannel.Frame) (listenHostPort string, cancel func()) {\n\trelay := &frameRelay{\n\t\tt:           t,\n\t\tdestination: destination,\n\t\trelayFunc:   relayFunc,\n\t}\n\treturn relay.listen()\n}\n"
  },
  {
    "path": "testutils/sleep.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport \"time\"\n\n// SleepStub stubs a function variable that points to time.Sleep. It returns\n// two channels to control the sleep stub, and a function to close the channels.\n// Once the stub is closed, any further sleeps will cause panics.\n// The two channels returned are:\n// <-chan time.Duration which will contain arguments that the stub was called with.\n// chan<- struct{} that should be written to when you want the Sleep to return.\nfunc SleepStub(funcVar *func(time.Duration)) (\n\targCh <-chan time.Duration, unblockCh chan<- struct{}, closeFn func()) {\n\n\targs := make(chan time.Duration)\n\tblock := make(chan struct{})\n\t*funcVar = func(t time.Duration) {\n\t\targs <- t\n\t\t<-block\n\t}\n\tcloseSleepChans := func() {\n\t\tclose(args)\n\t\tclose(block)\n\t}\n\treturn args, block, closeSleepChans\n}\n\n// ResetSleepStub resets a Sleep stub.\nfunc ResetSleepStub(funcVar *func(time.Duration)) {\n\t*funcVar = time.Sleep\n}\n"
  },
  {
    "path": "testutils/test_server.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/relay/relaytest\"\n\t\"github.com/uber/tchannel-go/testutils/goroutines\"\n\t\"go.uber.org/multierr\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/atomic\"\n\t\"golang.org/x/net/context\"\n)\n\n// Has a previous test already leaked a goroutine?\nvar _leakedGoroutine atomic.Bool\n\n// A TestServer encapsulates a TChannel server, a client factory, and functions\n// to ensure that we're not leaking resources.\ntype TestServer struct {\n\ttesting.TB\n\n\t// References to specific channels (if any, as they can be disabled)\n\trelayCh  *tchannel.Channel\n\tserverCh *tchannel.Channel\n\n\t// relayHost is the relayer's StubRelayHost (if any).\n\trelayHost *relaytest.StubRelayHost\n\n\t// relayStats is the backing stats for the relay.\n\t// Note: if a user passes a custom RelayHosts that does not implement\n\t// relayStatter, then this will be nil, and relay stats cannot be verified.\n\trelayStats *relaytest.MockStats\n\n\t// channels is the list of channels created for this TestServer. The first\n\t// element is always the initial server.\n\tchannels []*tchannel.Channel\n\n\t// channelState the initial runtime state for all channels created\n\t// as part of the TestServer (including the server).\n\tchannelStates map[*tchannel.Channel]*tchannel.RuntimeState\n\n\tintrospectOpts *tchannel.IntrospectionOptions\n\tverifyOpts     *goroutines.VerifyOpts\n\tpostFns        []func()\n}\n\ntype relayStatter interface {\n\tStats() *relaytest.MockStats\n}\n\n// NewTestServer constructs a TestServer.\nfunc NewTestServer(t testing.TB, opts *ChannelOpts) *TestServer {\n\tts := &TestServer{\n\t\tTB:            t,\n\t\tchannelStates: make(map[*tchannel.Channel]*tchannel.RuntimeState),\n\t\tintrospectOpts: &tchannel.IntrospectionOptions{\n\t\t\tIncludeExchanges:  true,\n\t\t\tIncludeTombstones: true,\n\t\t},\n\t}\n\n\tif !opts.DisableServer {\n\t\t// Remove any relay options, since those should only be applied to addRelay.\n\t\tserverOpts := opts.Copy()\n\t\tserverOpts.RelayHost = nil\n\t\tts.serverCh = ts.NewServer(serverOpts)\n\t}\n\n\tif opts == nil || !opts.DisableRelay {\n\t\tts.addRelay(opts)\n\t}\n\n\treturn ts\n}\n\n// runSubTest runs the specified function as a sub-test of a testing.T or\n// testing.B if the types match.\nfunc runSubTest(t testing.TB, name string, f func(testing.TB)) {\n\tswitch t := t.(type) {\n\tcase *testing.T:\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tf(t)\n\t\t})\n\tcase *testing.B:\n\t\tt.Run(name, func(b *testing.B) {\n\t\t\tf(b)\n\t\t})\n\tdefault:\n\t\tf(t)\n\t}\n}\n\n// WithTestServer creates a new TestServer, runs the passed function, and then\n// verifies that no resources were leaked.\nfunc WithTestServer(t testing.TB, chanOpts *ChannelOpts, f func(testing.TB, *TestServer)) {\n\trunTest := func(t testing.TB, chanOpts *ChannelOpts) {\n\t\trunCount := chanOpts.RunCount\n\t\tif runCount < 1 {\n\t\t\trunCount = 1\n\t\t}\n\n\t\tfor i := 0; i < runCount; i++ {\n\t\t\tif t.Failed() {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Run without the relay, unless OnlyRelay was set.\n\t\t\tif !chanOpts.OnlyRelay {\n\t\t\t\trunSubTest(t, \"no relay\", func(t testing.TB) {\n\t\t\t\t\tnoRelayOpts := chanOpts.Copy()\n\t\t\t\t\tnoRelayOpts.DisableRelay = true\n\t\t\t\t\twithServer(t, noRelayOpts, f)\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// Run with the relay, unless the user has disabled it.\n\t\t\tif !chanOpts.DisableRelay {\n\t\t\t\trunSubTest(t, \"with relay\", func(t testing.TB) {\n\t\t\t\t\twithServer(t, chanOpts.Copy(), f)\n\t\t\t\t})\n\n\t\t\t\t// Re-run the same test with timer verification if this is a relay-only test.\n\t\t\t\tif chanOpts.OnlyRelay {\n\t\t\t\t\trunSubTest(t, \"with relay and timer verification\", func(t testing.TB) {\n\t\t\t\t\t\tverifyOpts := chanOpts.Copy()\n\t\t\t\t\t\tverifyOpts.RelayTimerVerification = true\n\t\t\t\t\t\twithServer(t, verifyOpts, f)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tchanOptsCopy := chanOpts.Copy()\n\trunTest(t, chanOptsCopy)\n\n\tif os.Getenv(\"DISABLE_FRAME_POOLING_CHECKS\") == \"\" && chanOptsCopy.CheckFramePooling {\n\t\trunSubTest(t, \"check frame leaks\", func(t testing.TB) {\n\t\t\tpool := tchannel.NewCheckedFramePoolForTest()\n\n\t\t\trunTest(t, chanOpts.Copy().SetFramePool(pool))\n\n\t\t\tresult := pool.CheckEmpty()\n\t\t\tif len(result.Unreleased) > 0 {\n\t\t\t\tt.Errorf(\"Frame pool has %v unreleased frames, errors:\\n%v\\n\",\n\t\t\t\t\tlen(result.Unreleased), strings.Join(result.Unreleased, \"\\n\"))\n\t\t\t}\n\t\t\tif len(result.BadReleases) > 0 {\n\t\t\t\tt.Errorf(\"Frame pool has %v bad releases, errors:\\n%v\\n\",\n\t\t\t\t\tlen(result.BadReleases), strings.Join(result.BadReleases, \"\\n\"))\n\t\t\t}\n\t\t})\n\t}\n}\n\n// SetVerifyOpts specifies the options we'll use during teardown to verify that\n// no goroutines were leaked.\nfunc (ts *TestServer) SetVerifyOpts(opts *goroutines.VerifyOpts) {\n\tts.verifyOpts = opts\n}\n\n// HasServer returns whether this TestServer has a TChannel server, as\n// the server may have been disabled with the DisableServer option.\nfunc (ts *TestServer) HasServer() bool {\n\treturn ts.serverCh != nil\n}\n\n// Server returns the underlying TChannel for the server (i.e., the channel on\n// which we're registering handlers).\n//\n// To support test cases with relays interposed between clients and servers,\n// callers should use the Client(), HostPort(), ServiceName(), and Register()\n// methods instead of accessing the server channel explicitly.\nfunc (ts *TestServer) Server() *tchannel.Channel {\n\trequire.True(ts, ts.HasServer(), \"Cannot use Server as it was disabled\")\n\treturn ts.serverCh\n}\n\n// HasRelay indicates whether this TestServer has a relay interposed between the\n// server and clients.\nfunc (ts *TestServer) HasRelay() bool {\n\treturn ts.relayCh != nil\n}\n\n// Relay returns the relay channel, if one is present.\nfunc (ts *TestServer) Relay() *tchannel.Channel {\n\trequire.True(ts, ts.HasRelay(), \"Cannot use Relay, not present in current test\")\n\treturn ts.relayCh\n}\n\n// RelayHost returns the stub RelayHost for mapping service names to peers.\nfunc (ts *TestServer) RelayHost() *relaytest.StubRelayHost {\n\treturn ts.relayHost\n}\n\n// HostPort returns the host:port for clients to connect to. Note that this may\n// not be the same as the host:port of the server channel.\nfunc (ts *TestServer) HostPort() string {\n\tif ts.HasRelay() {\n\t\treturn ts.Relay().PeerInfo().HostPort\n\t}\n\treturn ts.Server().PeerInfo().HostPort\n}\n\n// ServiceName returns the service name of the server channel.\nfunc (ts *TestServer) ServiceName() string {\n\treturn ts.Server().PeerInfo().ServiceName\n}\n\n// Register registers a handler on the server channel.\nfunc (ts *TestServer) Register(h tchannel.Handler, methodName string) {\n\tts.Server().Register(h, methodName)\n}\n\n// RegisterFunc registers a function as a handler for the given method name.\n//\n// TODO: Delete testutils.RegisterFunc in favor of this test server.\nfunc (ts *TestServer) RegisterFunc(name string, f func(context.Context, *raw.Args) (*raw.Res, error)) {\n\tts.Register(raw.Wrap(rawFuncHandler{ts.Server(), f}), name)\n}\n\n// CloseAndVerify closes all channels verifying each channel as it is closed.\n// It then verifies that no goroutines were leaked.\nfunc (ts *TestServer) CloseAndVerify() {\n\t// Verify channels before they are closed to ensure that we catch any\n\t// unexpected pending exchanges.\n\tvar verify sync.WaitGroup\n\tfor i := len(ts.channels) - 1; i >= 0; i-- {\n\t\tch := ts.channels[i]\n\n\t\tverify.Add(1)\n\t\tgo func() {\n\t\t\tdefer verify.Done()\n\t\t\tch.Logger().Debugf(\"TEST: TestServer is verifying channel\")\n\t\t\tts.verify(ch)\n\t\t}()\n\t}\n\tverify.Wait()\n\n\t// Close the connection, then verify again to ensure connection close didn't\n\t// cause any unexpected issues.\n\tvar closeVerify sync.WaitGroup\n\tfor i := len(ts.channels) - 1; i >= 0; i-- {\n\t\tch := ts.channels[i]\n\n\t\tcloseVerify.Add(1)\n\t\tgo func() {\n\t\t\tdefer closeVerify.Done()\n\t\t\tch.Logger().Debugf(\"TEST: TestServer is closing and verifying channel\")\n\t\t\tts.close(ch)\n\t\t\tts.verify(ch)\n\t\t}()\n\t}\n\tcloseVerify.Wait()\n\n\tif ts.relayCh != nil {\n\t\tts.close(ts.relayCh)\n\t\tts.verify(ts.relayCh)\n\t}\n\n\t// Verify that there's no goroutine leaks after all tests are complete.\n\tts.verifyNoGoroutinesLeaked()\n}\n\n// AssertRelayStats checks that the relayed call graph matches expectations. If\n// there's no relay, AssertRelayStats is a no-op.\nfunc (ts *TestServer) AssertRelayStats(expected *relaytest.MockStats) {\n\tif !ts.HasRelay() {\n\t\treturn\n\t}\n\n\tif ts.relayStats == nil {\n\t\tts.TB.Error(\"Cannot verify relay stats, passed in RelayStats does not implement relayStatter\")\n\t\treturn\n\t}\n\n\tts.relayStats.AssertEqual(ts, expected)\n}\n\n// NewClient returns a client that with log verification.\n// TODO: Verify message exchanges and leaks for client channels as well.\nfunc (ts *TestServer) NewClient(opts *ChannelOpts) *tchannel.Channel {\n\treturn ts.addChannel(newClient, opts.Copy())\n}\n\n// NewServer returns a server with log and channel state verification.\n//\n// Note: The same default service name is used if one isn't specified.\nfunc (ts *TestServer) NewServer(opts *ChannelOpts) *tchannel.Channel {\n\tch := ts.addChannel(newServer, opts.Copy())\n\tif ts.relayHost != nil {\n\t\tts.relayHost.Add(ch.ServiceName(), ch.PeerInfo().HostPort)\n\t}\n\treturn ch\n}\n\n// addRelay adds a relay in front of the test server, altering public methods as\n// necessary to route traffic through the relay.\nfunc (ts *TestServer) addRelay(parentOpts *ChannelOpts) {\n\topts := parentOpts.Copy()\n\n\trelayHost := opts.ChannelOptions.RelayHost\n\tif relayHost == nil {\n\t\tts.relayHost = relaytest.NewStubRelayHost()\n\t\trelayHost = ts.relayHost\n\t} else if relayHost, ok := relayHost.(*relaytest.StubRelayHost); ok {\n\t\tts.relayHost = relayHost\n\t}\n\n\topts.ServiceName = \"relay\"\n\topts.ChannelOptions.RelayHost = relayHost\n\n\tts.relayCh = ts.addChannel(newServer, opts)\n\tif ts.relayHost != nil && ts.HasServer() {\n\t\tts.relayHost.Add(ts.Server().ServiceName(), ts.Server().PeerInfo().HostPort)\n\t}\n\n\tif statter, ok := relayHost.(relayStatter); ok {\n\t\tts.relayStats = statter.Stats()\n\t}\n}\n\nfunc (ts *TestServer) addChannel(createChannel func(t testing.TB, opts *ChannelOpts) *tchannel.Channel, opts *ChannelOpts) *tchannel.Channel {\n\tch := createChannel(ts, opts)\n\tts.postFns = append(ts.postFns, opts.postFns...)\n\tts.channels = append(ts.channels, ch)\n\tts.channelStates[ch] = comparableState(ch, ts.introspectOpts)\n\treturn ch\n}\n\n// close closes all channels in most-recently-created order.\n// it waits for the channels to close.\nfunc (ts *TestServer) close(ch *tchannel.Channel) {\n\tch.Close()\n\n\ttimeout := Timeout(time.Second)\n\tselect {\n\tcase <-time.After(timeout):\n\t\tts.Errorf(\"Channel %p did not close after %v, last state: %v\", ch, timeout, ch.State())\n\n\t\t// The introspected state might help debug why the channel isn't closing.\n\t\tts.Logf(\"Introspected state:\\n%s\", IntrospectJSON(ch, &tchannel.IntrospectionOptions{\n\t\t\tIncludeExchanges:  true,\n\t\t\tIncludeTombstones: true,\n\t\t}))\n\tcase <-ch.ClosedChan():\n\t}\n}\n\nfunc (ts *TestServer) verify(ch *tchannel.Channel) {\n\tif ts.Failed() {\n\t\treturn\n\t}\n\n\t// Tests may end with running background goroutines that are cleaning up, so give\n\t// them some time to finish before running verifications.\n\tvar errs error\n\tWaitFor(time.Second, func() bool {\n\t\terrs = multierr.Combine(\n\t\t\tts.verifyExchangesCleared(ch),\n\t\t\tts.verifyRelaysEmpty(ch),\n\t\t)\n\t\treturn errs == nil\n\t})\n\n\tif errs == nil {\n\t\treturn\n\t}\n\n\t// If verification fails, get the marshalled state.\n\tassert.NoError(ts, errs, \"Verification failed. Channel state:\\n%v\", IntrospectJSON(ch, nil /* opts */))\n}\n\n// AddPostFn registers a function that will be executed after channels are closed.\nfunc (ts *TestServer) AddPostFn(fn func()) {\n\tts.postFns = append(ts.postFns, fn)\n}\n\nfunc (ts *TestServer) post() {\n\tif !ts.Failed() {\n\t\tfor _, ch := range ts.channels {\n\t\t\tts.verifyNoStateLeak(ch)\n\t\t}\n\t}\n\tfor _, fn := range ts.postFns {\n\t\tfn()\n\t}\n}\n\nfunc (ts *TestServer) verifyNoStateLeak(ch *tchannel.Channel) {\n\tinitial := ts.channelStates[ch]\n\tfinal := comparableState(ch, ts.introspectOpts)\n\tassert.Equal(ts.TB, initial, final, \"Runtime state has leaks\")\n}\n\nfunc (ts *TestServer) verifyExchangesCleared(ch *tchannel.Channel) error {\n\t// Ensure that all the message exchanges are empty.\n\tserverState := ch.IntrospectState(ts.introspectOpts)\n\tif exchangesLeft := describeLeakedExchanges(serverState); exchangesLeft != \"\" {\n\t\treturn fmt.Errorf(\"found uncleared message exchanges on %q:\\n%v\", ch.ServiceName(), exchangesLeft)\n\t}\n\n\treturn nil\n}\n\nfunc (ts *TestServer) verifyRelaysEmpty(ch *tchannel.Channel) error {\n\tvar errs error\n\tstate := ch.IntrospectState(ts.introspectOpts)\n\tfor _, peerState := range state.RootPeers {\n\t\tvar connStates []tchannel.ConnectionRuntimeState\n\t\tconnStates = append(connStates, peerState.InboundConnections...)\n\t\tconnStates = append(connStates, peerState.OutboundConnections...)\n\t\tfor _, connState := range connStates {\n\t\t\tn := connState.Relayer.Count\n\t\t\tif n != 0 {\n\t\t\t\terrs = multierr.Append(errs, fmt.Errorf(\"found %v left-over items in relayer for %v\", n, connState.LocalHostPort))\n\t\t\t}\n\t\t}\n\t}\n\n\treturn errs\n}\n\nfunc (ts *TestServer) verifyNoGoroutinesLeaked() {\n\tif _leakedGoroutine.Load() {\n\t\tts.Log(\"Skipping check for leaked goroutines because of a previous leak.\")\n\t\treturn\n\t}\n\terr := goroutines.IdentifyLeaks(ts.verifyOpts)\n\tif err == nil {\n\t\t// No leaks, nothing to do.\n\t\treturn\n\t}\n\tif isFirstLeak := _leakedGoroutine.CAS(false, true); !isFirstLeak {\n\t\tts.Log(\"Skipping check for leaked goroutines because of a previous leak.\")\n\t\treturn\n\t}\n\tif ts.Failed() {\n\t\t// If we've already failed this test, don't pollute the test output with\n\t\t// more failures.\n\t\treturn\n\t}\n\tts.Error(err.Error())\n}\n\nfunc comparableState(ch *tchannel.Channel, opts *tchannel.IntrospectionOptions) *tchannel.RuntimeState {\n\ts := ch.IntrospectState(opts)\n\ts.SubChannels = nil\n\ts.Peers = nil\n\n\t// Tests start with ChannelClient or ChannelListening, but end with ChannelClosed.\n\ts.ChannelState = \"\"\n\treturn s\n}\n\nfunc describeLeakedExchanges(rs *tchannel.RuntimeState) string {\n\tvar connections []*tchannel.ConnectionRuntimeState\n\tfor _, peer := range rs.RootPeers {\n\t\tfor _, conn := range peer.InboundConnections {\n\t\t\tconnections = append(connections, &conn)\n\t\t}\n\t\tfor _, conn := range peer.OutboundConnections {\n\t\t\tconnections = append(connections, &conn)\n\t\t}\n\t}\n\treturn describeLeakedExchangesConns(connections)\n}\n\nfunc describeLeakedExchangesConns(connections []*tchannel.ConnectionRuntimeState) string {\n\tvar exchanges []string\n\tfor _, c := range connections {\n\t\tif exch := describeLeakedExchangesSingleConn(c); exch != \"\" {\n\t\t\texchanges = append(exchanges, exch)\n\t\t}\n\t}\n\treturn strings.Join(exchanges, \"\\n\")\n}\n\nfunc describeLeakedExchangesSingleConn(cs *tchannel.ConnectionRuntimeState) string {\n\tvar exchanges []string\n\tcheckExchange := func(e tchannel.ExchangeSetRuntimeState) {\n\t\tif e.Count > 0 {\n\t\t\texchanges = append(exchanges, fmt.Sprintf(\" %v leftover %v exchanges\", e.Name, e.Count))\n\t\t\tfor _, v := range e.Exchanges {\n\t\t\t\texchanges = append(exchanges, fmt.Sprintf(\"  exchanges: %+v\", v))\n\t\t\t}\n\t\t}\n\t}\n\tcheckExchange(cs.InboundExchange)\n\tcheckExchange(cs.OutboundExchange)\n\tif len(exchanges) == 0 {\n\t\treturn \"\"\n\t}\n\n\treturn fmt.Sprintf(\"Connection %d has leftover exchanges:\\n\\t%v\", cs.ID, strings.Join(exchanges, \"\\n\\t\"))\n}\n\nfunc withServer(t testing.TB, chanOpts *ChannelOpts, f func(testing.TB, *TestServer)) {\n\tts := NewTestServer(t, chanOpts)\n\t// Note: We use defer, as we want the postFns to run even if the test\n\t// goroutine exits (e.g. user calls t.Fatalf).\n\tdefer ts.post()\n\tdefer ts.CloseAndVerify()\n\n\tf(t, ts)\n\tif ts.HasServer() {\n\t\tts.Server().Logger().Debugf(\"TEST: Test function complete\")\n\t}\n}\n"
  },
  {
    "path": "testutils/testreader/chunk.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testreader\n\nimport (\n\t\"errors\"\n\t\"io\"\n)\n\n// ErrUser is returned by ChunkReader when the user requests an error.\nvar ErrUser = errors.New(\"error set by user\")\n\n// ChunkReader returns a reader that returns chunks written to the control channel.\n// The caller should write byte chunks to return to the channel, or write nil if they\n// want the Reader to return an error. The control channel should be closed to signal EOF.\nfunc ChunkReader() (chan<- []byte, io.Reader) {\n\treader := &errorReader{\n\t\tc: make(chan []byte, 100),\n\t}\n\treturn reader.c, reader\n}\n\ntype errorReader struct {\n\tc         chan []byte\n\tremaining []byte\n}\n\nfunc (r *errorReader) Read(bs []byte) (int, error) {\n\tfor len(r.remaining) == 0 {\n\t\tvar ok bool\n\t\tr.remaining, ok = <-r.c\n\t\tif !ok {\n\t\t\treturn 0, io.EOF\n\t\t}\n\t\tif r.remaining == nil {\n\t\t\treturn 0, ErrUser\n\t\t}\n\t\tif len(r.remaining) == 0 {\n\t\t\treturn 0, nil\n\t\t}\n\t}\n\n\tn := copy(bs, r.remaining)\n\tr.remaining = r.remaining[n:]\n\treturn n, nil\n}\n"
  },
  {
    "path": "testutils/testreader/chunk_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testreader\n\nimport (\n\t\"io\"\n\t\"io/ioutil\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestChunkReader0ByteRead(t *testing.T) {\n\twriter, reader := ChunkReader()\n\n\twriter <- []byte{}\n\twriter <- []byte{'a'}\n\tclose(writer)\n\n\tbuf := make([]byte, 1)\n\tn, err := reader.Read(buf)\n\tassert.NoError(t, err, \"Read should not fail\")\n\tassert.Equal(t, 0, n, \"Read should not read any bytes\")\n\n\tn, err = reader.Read(buf)\n\tassert.NoError(t, err, \"Read should not fail\")\n\tassert.Equal(t, 1, n, \"Read should read one byte\")\n\tassert.EqualValues(t, 'a', buf[0], \"Read did not read correct byte\")\n\n\tn, err = reader.Read(buf)\n\tassert.Equal(t, io.EOF, err, \"Read should EOF\")\n\tassert.Equal(t, 0, n, \"Read should not read any bytes\")\n}\n\nfunc TestChunkReader(t *testing.T) {\n\twriter, reader := ChunkReader()\n\n\twriter <- []byte{1, 2}\n\twriter <- []byte{3}\n\twriter <- nil\n\twriter <- []byte{4}\n\twriter <- []byte{}\n\twriter <- []byte{5}\n\twriter <- []byte{}\n\twriter <- []byte{6}\n\twriter <- []byte{}\n\tclose(writer)\n\n\tbuf, err := ioutil.ReadAll(reader)\n\tassert.Equal(t, ErrUser, err, \"Expected error after initial bytes\")\n\tassert.Equal(t, []byte{1, 2, 3}, buf, \"Unexpected bytes\")\n\n\tbuf, err = ioutil.ReadAll(reader)\n\tassert.NoError(t, err, \"Reader shouldn't fail on second set of bytes\")\n\tassert.Equal(t, []byte{4, 5, 6}, buf, \"Unexpected bytes\")\n}\n"
  },
  {
    "path": "testutils/testreader/loop.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testreader\n\nimport \"io\"\n\ntype loopReader struct {\n\tbs  []byte\n\tpos int\n}\n\nfunc (r loopReader) Read(p []byte) (int, error) {\n\tfor i := range p {\n\t\tp[i] = r.bs[r.pos]\n\t\tif r.pos++; r.pos == len(r.bs) {\n\t\t\tr.pos = 0\n\t\t}\n\t}\n\treturn len(p), nil\n}\n\n// Looper returns a reader that will return the bytes in bs as if it was a circular buffer.\nfunc Looper(bs []byte) io.Reader {\n\treturn &loopReader{bs, 0}\n}\n"
  },
  {
    "path": "testutils/testreader/loop_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testreader\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLooper(t *testing.T) {\n\ttests := []struct {\n\t\tbs       []byte\n\t\texpected []byte\n\t}{\n\t\t{[]byte{0x1}, []byte{0x1, 0x1, 0x1}},\n\t\t{[]byte{0x1, 0x2}, []byte{0x1, 0x2, 0x1}},\n\t\t{[]byte{0x1, 0x2}, []byte{0x1, 0x2, 0x1, 0x2, 0x1, 0x2}},\n\t}\n\n\tfor _, tt := range tests {\n\t\tr := Looper(tt.bs)\n\t\tgot := make([]byte, len(tt.expected))\n\t\tn, err := r.Read(got)\n\t\tassert.NoError(t, err, \"Read failed\")\n\t\tassert.Equal(t, len(got), n)\n\t\tassert.Equal(t, tt.expected, got, \"Got unexpected bytes\")\n\t}\n}\n"
  },
  {
    "path": "testutils/testtracing/propagation.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testtracing\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/mocktracer\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/uber/jaeger-client-go\"\n\t\"golang.org/x/net/context\"\n)\n\nconst (\n\t// BaggageKey is used for testing baggage propagation\n\tBaggageKey = \"luggage\"\n\n\t// BaggageValue is used for testing baggage propagation\n\tBaggageValue = \"suitcase\"\n)\n\n// TracingRequest tests tracing capabilities in a given server.\ntype TracingRequest struct {\n\t// ForwardCount tells the server how many times to forward this request to itself recursively\n\tForwardCount int\n}\n\n// TracingResponse captures the trace info observed in the server and its downstream calls\ntype TracingResponse struct {\n\tTraceID        uint64\n\tSpanID         uint64\n\tParentID       uint64\n\tTracingEnabled bool\n\tChild          *TracingResponse\n\tLuggage        string\n}\n\n// ObserveSpan extracts an OpenTracing span from the context and populates the response.\nfunc (r *TracingResponse) ObserveSpan(ctx context.Context) *TracingResponse {\n\tif span := opentracing.SpanFromContext(ctx); span != nil {\n\t\tif mockSpan, ok := span.(*mocktracer.MockSpan); ok {\n\t\t\tsc := mockSpan.Context().(mocktracer.MockSpanContext)\n\t\t\tr.TraceID = uint64(sc.TraceID)\n\t\t\tr.SpanID = uint64(sc.SpanID)\n\t\t\tr.ParentID = uint64(mockSpan.ParentID)\n\t\t\tr.TracingEnabled = sc.Sampled\n\t\t} else if span := tchannel.CurrentSpan(ctx); span != nil {\n\t\t\tr.TraceID = span.TraceID()\n\t\t\tr.SpanID = span.SpanID()\n\t\t\tr.ParentID = span.ParentID()\n\t\t\tr.TracingEnabled = span.Flags()&1 == 1\n\t\t}\n\t\tr.Luggage = span.BaggageItem(BaggageKey)\n\t}\n\treturn r\n}\n\n// TraceHandler is a base class for testing tracing propagation\ntype TraceHandler struct {\n\tCh *tchannel.Channel\n}\n\n// HandleCall is used by handlers from different encodings as the main business logic.\n// It respects the ForwardCount input parameter to make downstream calls, and returns\n// a result containing the observed tracing span and the downstream results.\nfunc (h *TraceHandler) HandleCall(\n\tctx context.Context,\n\treq *TracingRequest,\n\tdownstream TracingCall,\n) (*TracingResponse, error) {\n\tvar childResp *TracingResponse\n\tif req.ForwardCount > 0 {\n\t\tdownstreamReq := &TracingRequest{ForwardCount: req.ForwardCount - 1}\n\t\tif resp, err := downstream(ctx, downstreamReq); err == nil {\n\t\t\tchildResp = resp\n\t\t} else {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tresp := &TracingResponse{Child: childResp}\n\tresp.ObserveSpan(ctx)\n\n\treturn resp, nil\n}\n\n// TracerType is a convenient enum to indicate which type of tracer is being used in the test.\n// It is a string because it's printed as part of the test description in the logs.\ntype TracerType string\n\nconst (\n\t// Noop is for the default no-op tracer from OpenTracing\n\tNoop TracerType = \"NOOP\"\n\t// Mock tracer, baggage-capable, non-Zipkin trace IDs\n\tMock TracerType = \"MOCK\"\n\t// Jaeger is Uber's tracer, baggage-capable, Zipkin-style trace IDs\n\tJaeger TracerType = \"JAEGER\"\n)\n\n// TracingCall is used in a few other structs here\ntype TracingCall func(ctx context.Context, req *TracingRequest) (*TracingResponse, error)\n\n// EncodingInfo describes the encoding used with tracing propagation test\ntype EncodingInfo struct {\n\tFormat           tchannel.Format\n\tHeadersSupported bool\n}\n\n// PropagationTestSuite is a collection of test cases for a certain encoding\ntype PropagationTestSuite struct {\n\tEncoding  EncodingInfo\n\tRegister  func(t *testing.T, ch *tchannel.Channel) TracingCall\n\tTestCases map[TracerType][]PropagationTestCase\n}\n\n// PropagationTestCase describes a single propagation test case and expected results\ntype PropagationTestCase struct {\n\tForwardCount      int\n\tTracingDisabled   bool\n\tExpectedBaggage   string\n\tExpectedSpanCount int\n}\n\ntype tracerChoice struct {\n\ttracerType       TracerType\n\ttracer           opentracing.Tracer\n\tspansRecorded    func() int\n\tresetSpans       func()\n\tisFake           bool\n\tzipkinCompatible bool\n}\n\n// Run executes the test cases in the test suite against 3 different tracer implementations\nfunc (s *PropagationTestSuite) Run(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\trun  func(t *testing.T)\n\t}{\n\t\t{\"Noop_Tracer\", s.runWithNoopTracer},\n\t\t{\"Mock_Tracer\", s.runWithMockTracer},\n\t\t{\"Jaeger_Tracer\", s.runWithJaegerTracer},\n\t}\n\tfor _, test := range tests {\n\t\tt.Logf(\"Running with %s\", test.name)\n\t\ttest.run(t)\n\t}\n}\n\nfunc (s *PropagationTestSuite) runWithNoopTracer(t *testing.T) {\n\ts.runWithTracer(t, tracerChoice{\n\t\ttracer:        nil, // will cause opentracing.GlobalTracer() to be used\n\t\ttracerType:    Noop,\n\t\tspansRecorded: func() int { return 0 },\n\t\tresetSpans:    func() {},\n\t\tisFake:        true,\n\t})\n}\n\nfunc (s *PropagationTestSuite) runWithMockTracer(t *testing.T) {\n\tmockTracer := mocktracer.New()\n\ts.runWithTracer(t, tracerChoice{\n\t\ttracerType: Mock,\n\t\ttracer:     mockTracer,\n\t\tspansRecorded: func() int {\n\t\t\treturn len(MockTracerSampledSpans(mockTracer))\n\t\t},\n\t\tresetSpans: func() {\n\t\t\tmockTracer.Reset()\n\t\t},\n\t})\n}\n\nfunc (s *PropagationTestSuite) runWithJaegerTracer(t *testing.T) {\n\tjaegerReporter := jaeger.NewInMemoryReporter()\n\tjaegerTracer, jaegerCloser := jaeger.NewTracer(testutils.DefaultServerName,\n\t\tjaeger.NewConstSampler(true),\n\t\tjaegerReporter)\n\t// To enable logging, use composite reporter:\n\t// jaeger.NewCompositeReporter(jaegerReporter, jaeger.NewLoggingReporter(jaeger.StdLogger)))\n\tdefer jaegerCloser.Close()\n\ts.runWithTracer(t, tracerChoice{\n\t\ttracerType: Jaeger,\n\t\ttracer:     jaegerTracer,\n\t\tspansRecorded: func() int {\n\t\t\treturn len(jaegerReporter.GetSpans())\n\t\t},\n\t\tresetSpans: func() {\n\t\t\tjaegerReporter.Reset()\n\t\t},\n\t\tzipkinCompatible: true,\n\t})\n}\n\nfunc (s *PropagationTestSuite) runWithTracer(t *testing.T, tracer tracerChoice) {\n\ttestCases, ok := s.TestCases[tracer.tracerType]\n\tif !ok {\n\t\tt.Logf(\"No test cases for encoding=%s and tracer=%s\", s.Encoding.Format, tracer.tracerType)\n\t\treturn\n\t}\n\topts := &testutils.ChannelOpts{\n\t\tChannelOptions: tchannel.ChannelOptions{Tracer: tracer.tracer},\n\t\tDisableRelay:   true,\n\t}\n\tch := testutils.NewServer(t, opts)\n\tdefer ch.Close()\n\tch.Peers().Add(ch.PeerInfo().HostPort)\n\tcall := s.Register(t, ch)\n\tfor _, tt := range testCases {\n\t\ts.runTestCase(t, tracer, ch, tt, call)\n\t}\n}\n\nfunc (s *PropagationTestSuite) runTestCase(\n\tt *testing.T,\n\ttracer tracerChoice,\n\tch *tchannel.Channel,\n\ttest PropagationTestCase,\n\tcall TracingCall,\n) {\n\tdescr := fmt.Sprintf(\"test %+v with tracer %+v\", test, tracer)\n\tch.Logger().Debugf(\"Starting tracing test %s\", descr)\n\n\ttracer.resetSpans()\n\n\tspan := ch.Tracer().StartSpan(\"client\")\n\tspan.SetBaggageItem(BaggageKey, BaggageValue)\n\tctx := opentracing.ContextWithSpan(context.Background(), span)\n\n\tctxBuilder := tchannel.NewContextBuilder(5 * time.Second).SetParentContext(ctx)\n\tif test.TracingDisabled {\n\t\tctxBuilder.DisableTracing()\n\t}\n\tctx, cancel := ctxBuilder.Build()\n\tdefer cancel()\n\n\treq := &TracingRequest{ForwardCount: test.ForwardCount}\n\tch.Logger().Infof(\"Sending tracing request %+v\", req)\n\tresponse, err := call(ctx, req)\n\trequire.NoError(t, err)\n\tch.Logger().Infof(\"Received tracing response %+v\", response)\n\n\t// Spans are finished in inbound.doneSending() or outbound.doneReading(),\n\t// which are called on different go-routines and may execute *after* the\n\t// response has been received by the client. Give them a chance to run.\n\tfor i := 0; i < 1000; i++ {\n\t\tif spanCount := tracer.spansRecorded(); spanCount == test.ExpectedSpanCount {\n\t\t\tbreak\n\t\t}\n\t\ttime.Sleep(testutils.Timeout(time.Millisecond))\n\t}\n\tspanCount := tracer.spansRecorded()\n\tch.Logger().Debugf(\"end span count: %d\", spanCount)\n\n\t// finish span after taking count of recorded spans, as we're only interested\n\t// in the count of spans created by RPC calls.\n\tspan.Finish()\n\n\troot := new(TracingResponse).ObserveSpan(ctx)\n\n\tif !tracer.isFake {\n\t\tassert.Equal(t, uint64(0), root.ParentID)\n\t\tassert.NotEqual(t, uint64(0), root.TraceID)\n\t}\n\n\tassert.Equal(t, test.ExpectedSpanCount, spanCount, \"Wrong span count; %s\", descr)\n\n\tfor r, cnt := response, 0; r != nil || cnt <= test.ForwardCount; r, cnt = r.Child, cnt+1 {\n\t\trequire.NotNil(t, r, \"Expecting response for forward=%d; %s\", cnt, descr)\n\t\tif !tracer.isFake {\n\t\t\tif tracer.zipkinCompatible || s.Encoding.HeadersSupported {\n\t\t\t\tassert.Equal(t, root.TraceID, r.TraceID, \"traceID should be the same; %s\", descr)\n\t\t\t}\n\t\t\tassert.Equal(t, test.ExpectedBaggage, r.Luggage, \"baggage should propagate; %s\", descr)\n\t\t}\n\t}\n\tch.Logger().Debugf(\"Finished tracing test %s\", descr)\n}\n\n// MockTracerSampledSpans is a helper function that returns only sampled spans from MockTracer\nfunc MockTracerSampledSpans(tracer *mocktracer.MockTracer) []*mocktracer.MockSpan {\n\tvar spans []*mocktracer.MockSpan\n\tfor _, span := range tracer.FinishedSpans() {\n\t\tif span.Context().(mocktracer.MockSpanContext).Sampled {\n\t\t\tspans = append(spans, span)\n\t\t}\n\t}\n\treturn spans\n}\n"
  },
  {
    "path": "testutils/testtracing/propagation_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testtracing\n\nimport (\n\tjson_encoding \"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\n\t\"golang.org/x/net/context\"\n)\n\nfunc requestFromRaw(args *raw.Args) *TracingRequest {\n\tr := new(TracingRequest)\n\tr.ForwardCount = int(args.Arg3[0])\n\treturn r\n}\n\nfunc requestToRaw(r *TracingRequest) []byte {\n\treturn []byte{byte(r.ForwardCount)}\n}\n\nfunc responseFromRaw(t *testing.T, arg3 []byte) (*TracingResponse, error) {\n\tvar r TracingResponse\n\terr := json_encoding.Unmarshal(arg3, &r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &r, nil\n}\n\nfunc responseToRaw(t *testing.T, r *TracingResponse) (*raw.Res, error) {\n\tjsonBytes, err := json_encoding.Marshal(r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &raw.Res{Arg3: jsonBytes}, nil\n}\n\n// RawHandler tests tracing over Raw encoding\ntype RawHandler struct {\n\tTraceHandler\n\tt *testing.T\n}\n\nfunc (h *RawHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\treq := requestFromRaw(args)\n\tres, err := h.HandleCall(ctx, req,\n\t\tfunc(ctx context.Context, req *TracingRequest) (*TracingResponse, error) {\n\t\t\t_, arg3, _, err := raw.Call(ctx, h.Ch, h.Ch.PeerInfo().HostPort,\n\t\t\t\th.Ch.PeerInfo().ServiceName, \"rawcall\", nil, requestToRaw(req))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn responseFromRaw(h.t, arg3)\n\t\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn responseToRaw(h.t, res)\n}\n\nfunc (h *RawHandler) OnError(ctx context.Context, err error) { h.t.Errorf(\"onError %v\", err) }\n\nfunc (h *RawHandler) firstCall(ctx context.Context, req *TracingRequest) (*TracingResponse, error) {\n\t_, arg3, _, err := raw.Call(ctx, h.Ch, h.Ch.PeerInfo().HostPort, h.Ch.PeerInfo().ServiceName,\n\t\t\"rawcall\", nil, requestToRaw(req))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn responseFromRaw(h.t, arg3)\n}\n\nfunc TestRawTracingPropagation(t *testing.T) {\n\tsuite := &PropagationTestSuite{\n\t\tEncoding: EncodingInfo{Format: tchannel.Raw, HeadersSupported: false},\n\t\tRegister: func(t *testing.T, ch *tchannel.Channel) TracingCall {\n\t\t\thandler := &RawHandler{\n\t\t\t\tTraceHandler: TraceHandler{Ch: ch},\n\t\t\t\tt:            t,\n\t\t\t}\n\t\t\tch.Register(raw.Wrap(handler), \"rawcall\")\n\t\t\treturn handler.firstCall\n\t\t},\n\t\t// Since Raw encoding does not support headers, there is no baggage propagation\n\t\tTestCases: map[TracerType][]PropagationTestCase{\n\t\t\tNoop: {\n\t\t\t\t{ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: \"\", ExpectedSpanCount: 0},\n\t\t\t\t{ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: \"\", ExpectedSpanCount: 0},\n\t\t\t},\n\t\t\tMock: {\n\t\t\t\t// Since Raw encoding does not propagate generic traces, the tracingDisable\n\t\t\t\t// only affects the first outbound span (it's not sampled), but the other\n\t\t\t\t// two outbound spans are still sampled and recorded.\n\t\t\t\t{ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: \"\", ExpectedSpanCount: 2},\n\t\t\t\t// Since Raw encoding does not propagate generic traces, we record 3 spans\n\t\t\t\t// for outbound calls, but none for inbound calls.\n\t\t\t\t{ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: \"\", ExpectedSpanCount: 3},\n\t\t\t},\n\t\t\tJaeger: {\n\t\t\t\t// Since Jaeger is Zipkin-compatible, it is able to keep track of tracingDisabled\n\t\t\t\t{ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: \"\", ExpectedSpanCount: 0},\n\t\t\t\t// Since Jaeger is Zipkin-compatible, it is able to decode the trace\n\t\t\t\t// even from the Raw encoding.\n\t\t\t\t{ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: \"\", ExpectedSpanCount: 6},\n\t\t\t},\n\t\t},\n\t}\n\tsuite.Run(t)\n}\n"
  },
  {
    "path": "testutils/testwriter/limited.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testwriter\n\nimport (\n\t\"errors\"\n\t\"io\"\n)\n\n// ErrOutOfSpace is returned by Limited reader when it is out of bytes.\nvar ErrOutOfSpace = errors.New(\"out of space\")\n\ntype writerFunc func([]byte) (int, error)\n\nfunc (f writerFunc) Write(p []byte) (n int, err error) {\n\treturn f(p)\n}\n\n// Limited returns an io.Writer that will only accept n bytes.\n// All further calls will cause an error.\nfunc Limited(n int) io.Writer {\n\treturn writerFunc(func(p []byte) (int, error) {\n\t\tif n < len(p) {\n\t\t\tretN := n\n\t\t\tn = 0\n\t\t\treturn retN, ErrOutOfSpace\n\t\t}\n\n\t\tn -= len(p)\n\t\treturn len(p), nil\n\t})\n}\n"
  },
  {
    "path": "testutils/testwriter/limited_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testwriter\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLimitedWriter(t *testing.T) {\n\ttests := []struct {\n\t\tlimit      int\n\t\twriteBytes []byte\n\t\twantErr    error\n\t\twantBytes  int\n\t}{\n\t\t{\n\t\t\tlimit:      1,\n\t\t\twriteBytes: []byte{1},\n\t\t\twantBytes:  1,\n\t\t},\n\t\t{\n\t\t\tlimit:      1,\n\t\t\twriteBytes: []byte{1, 2},\n\t\t\twantErr:    ErrOutOfSpace,\n\t\t\twantBytes:  1,\n\t\t},\n\t\t{\n\t\t\tlimit:      0,\n\t\t\twriteBytes: nil,\n\t\t\twantBytes:  0,\n\t\t},\n\t\t{\n\t\t\tlimit:      5,\n\t\t\twriteBytes: []byte{1, 2, 3, 4, 5, 6},\n\t\t\twantErr:    ErrOutOfSpace,\n\t\t\twantBytes:  5,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\twriter := Limited(tt.limit)\n\t\tn, err := writer.Write(tt.writeBytes)\n\t\tif tt.wantErr != nil {\n\t\t\tassert.Equal(t, tt.wantErr, err, \"Write %v to Limited(%v) should fail\", tt.writeBytes, tt.limit)\n\t\t} else {\n\t\t\tassert.NoError(t, err, \"Write %v to Limited(%v) should not fail\", tt.writeBytes, tt.limit)\n\t\t}\n\t\tassert.Equal(t, tt.wantBytes, n, \"Unexpected number of bytes written to Limited(%v)\", tt.limit)\n\n\t\tn, err = writer.Write([]byte{2})\n\t\tassert.Equal(t, ErrOutOfSpace, err, \"Write should be out of space\")\n\t\tassert.Equal(t, 0, n, \"Write should not write any bytes when it is out of space\")\n\t}\n}\n\nfunc TestLimitedWriter2(t *testing.T) {\n\twriter := Limited(1)\n\tn, err := writer.Write([]byte{1, 2})\n\tassert.Equal(t, ErrOutOfSpace, err, \"Write should fail\")\n\tassert.Equal(t, 1, n, \"Write should only write one byte\")\n\n\tn, err = writer.Write([]byte{2})\n\tassert.Equal(t, ErrOutOfSpace, err, \"Write should be out of space\")\n\tassert.Equal(t, 0, n, \"Write should not write any bytes when it is out of space\")\n}\n"
  },
  {
    "path": "testutils/thriftarg2test/arg2_kv.go",
    "content": "package thriftarg2test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\n// BuildKVBuffer builds an thrift Arg2 KV buffer.\nfunc BuildKVBuffer(kv map[string]string) []byte {\n\t// Scan once to know size of buffer\n\tvar bufSize int\n\tfor k, v := range kv {\n\t\t// k~2 v~2\n\t\tbufSize += 2 + len(k) + 2 + len(v)\n\t}\n\tbufSize += 2 // nh:2\n\tbuf := make([]byte, bufSize)\n\twb := typed.NewWriteBuffer(buf)\n\twb.WriteUint16(uint16(len(kv)))\n\tfor k, v := range kv {\n\t\twb.WriteLen16String(k)\n\t\twb.WriteLen16String(v)\n\t}\n\treturn buf[:wb.BytesWritten()]\n}\n\n// ReadKVBuffer converts an arg2 buffer to a string map\nfunc ReadKVBuffer(b []byte) (map[string]string, error) {\n\trbuf := typed.NewReadBuffer(b)\n\tnh := rbuf.ReadUint16()\n\tretMap := make(map[string]string, nh)\n\tfor i := uint16(0); i < nh; i++ {\n\t\tkey := rbuf.ReadLen16String()\n\t\tval := rbuf.ReadLen16String()\n\t\tretMap[key] = val\n\t}\n\tif rbuf.BytesRemaining() > 0 {\n\t\treturn nil, fmt.Errorf(\"kv buffer wasn't fully consumed (%d bytes remaining)\", rbuf.BytesRemaining())\n\t}\n\treturn retMap, nil\n}\n\n// MustReadKVBuffer calls require.NoError on the error returned by ReadKVBuffer\nfunc MustReadKVBuffer(tb testing.TB, b []byte) map[string]string {\n\tm, err := ReadKVBuffer(b)\n\trequire.NoError(tb, err)\n\treturn m\n}\n"
  },
  {
    "path": "testutils/thriftarg2test/arg2_kv_test.go",
    "content": "package thriftarg2test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\nfunc TestBuildKVBuffer(t *testing.T) {\n\tkv := map[string]string{\n\t\t\"key\":  \"valval\",\n\t\t\"key2\": \"val\",\n\t}\n\tbuf := BuildKVBuffer(kv)\n\trb := typed.NewReadBuffer(buf)\n\tassert.EqualValues(t, len(kv), rb.ReadUint16())\n\n\tgotKV := make(map[string]string)\n\tfor i := 0; i < len(kv); i++ {\n\t\tk := rb.ReadLen16String()\n\t\tv := rb.ReadLen16String()\n\t\tgotKV[k] = v\n\t}\n\tassert.Equal(t, kv, gotKV)\n}\n\nfunc TestReadKVBuffer(t *testing.T) {\n\tkvMap := map[string]string{\n\t\t\"key\":  \"valval\",\n\t\t\"key2\": \"val\",\n\t}\n\n\tvar buffer [128]byte\n\twbuf := typed.NewWriteBuffer(buffer[:])\n\twbuf.WriteUint16(uint16(len(kvMap))) // nh\n\n\t// the order doesn't matter here since we're comparing maps\n\tfor k, v := range kvMap {\n\t\twbuf.WriteLen16String(k)\n\t\twbuf.WriteLen16String(v)\n\t}\n\tassert.Equal(t, kvMap, MustReadKVBuffer(t, buffer[:wbuf.BytesWritten()]))\n}\n"
  },
  {
    "path": "testutils/ticker.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport \"time\"\n\n// FakeTicker is a ticker for unit tests that can be controlled\n// deterministically.\ntype FakeTicker struct {\n\tc chan time.Time\n}\n\n// NewFakeTicker returns a new instance of FakeTicker\nfunc NewFakeTicker() *FakeTicker {\n\treturn &FakeTicker{\n\t\tc: make(chan time.Time, 1),\n\t}\n}\n\n// Tick sends an immediate tick call to the receiver\nfunc (ft *FakeTicker) Tick() {\n\tft.c <- time.Now()\n}\n\n// TryTick attempts to send a tick, if the channel isn't blocked.\nfunc (ft *FakeTicker) TryTick() bool {\n\tselect {\n\tcase ft.c <- time.Time{}:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// New can be used in tests as a factory method for tickers, by passing it to\n// ChannelOptions.TimeTicker\nfunc (ft *FakeTicker) New(d time.Duration) *time.Ticker {\n\tt := time.NewTicker(time.Hour)\n\tt.C = ft.c\n\treturn t\n}\n"
  },
  {
    "path": "testutils/ticker_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestFakeTicker(t *testing.T) {\n\tft := NewFakeTicker()\n\n\tticker := ft.New(time.Second)\n\tselect {\n\tcase <-ticker.C:\n\t\tt.Fatalf(\"Fake ticker ticked by itself\")\n\tdefault:\n\t}\n\n\tfor i := 0; i < 10; i++ {\n\t\tif i%2 == 0 {\n\t\t\tassert.True(t, ft.TryTick(), \"TryTick should succeed with no other pending ticks\")\n\t\t\tassert.False(t, ft.TryTick(), \"TryTick should fail with pending ticks\")\n\t\t} else {\n\t\t\tft.Tick()\n\t\t}\n\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\tdefault:\n\t\t\tt.Fatalf(\"Fake ticker tick did not unblock ticker\")\n\t\t}\n\t}\n\n\tticker.Stop()\n\tselect {\n\tcase <-ticker.C:\n\t\tt.Fatalf(\"Stopped ticker ticked\")\n\tdefault:\n\t}\n}\n"
  },
  {
    "path": "testutils/timeout.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar timeoutScaleFactor = 1.0\n\nfunc init() {\n\tif v := os.Getenv(\"TEST_TIMEOUT_SCALE\"); v != \"\" {\n\t\tfv, err := strconv.ParseFloat(v, 64)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\ttimeoutScaleFactor = fv\n\t\tfmt.Fprintln(os.Stderr, \"Scaling timeouts by factor\", timeoutScaleFactor)\n\t}\n}\n\n// Timeout returns the timeout multiplied by any set multiplier.\nfunc Timeout(timeout time.Duration) time.Duration {\n\treturn time.Duration(timeoutScaleFactor * float64(timeout))\n}\n\n// getCallerName returns the test name that called this function.\n// It traverses the stack to find the function name directly after a testing.* call.\nfunc getCallerName() string {\n\tpc := make([]uintptr, 10)\n\tn := runtime.Callers(2, pc)\n\tfor i := n; i > 0; i-- {\n\t\tfname := runtime.FuncForPC(pc[i-1]).Name()\n\t\tif strings.HasPrefix(fname, \"testing.\") {\n\t\t\treturn runtime.FuncForPC(pc[i-2]).Name()\n\t\t}\n\t}\n\treturn \"unknown\"\n}\n\n// SetTimeout is used to fail tests after a timeout. It returns a function that should be\n// run once the test is complete. The standard way is to use defer, e.g.\n// defer SetTimeout(t, time.Second)()\nfunc SetTimeout(t *testing.T, timeout time.Duration) func() {\n\ttimeout = Timeout(timeout)\n\n\tcaller := getCallerName()\n\n\ttimer := time.AfterFunc(timeout, func() {\n\t\tt.Logf(\"Test %s timed out after %v\", caller, timeout)\n\t\t// Unfortunately, tests cannot be failed from new goroutines, so use a panic.\n\t\tpanic(fmt.Errorf(\"Test %s timed out after %v\", caller, timeout))\n\t})\n\n\treturn func() {\n\t\ttimer.Stop()\n\t}\n}\n"
  },
  {
    "path": "testutils/wait.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutils\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\n// WaitFor will retry f till it returns true for a maximum of timeout.\n// It returns true if f returned true, false if timeout was hit.\nfunc WaitFor(timeout time.Duration, f func() bool) bool {\n\ttimeoutEnd := time.Now().Add(Timeout(timeout))\n\n\tconst maxSleep = time.Millisecond * 50\n\tsleepFor := time.Millisecond\n\tfor {\n\t\tif f() {\n\t\t\treturn true\n\t\t}\n\n\t\tif time.Now().After(timeoutEnd) {\n\t\t\treturn false\n\t\t}\n\n\t\ttime.Sleep(sleepFor)\n\t\tif sleepFor < maxSleep {\n\t\t\tsleepFor *= 2\n\t\t}\n\t}\n}\n\n// WaitWG waits for the given WaitGroup to be complete with a timeout\n// and returns whether the WaitGroup completed within the timeout.\nfunc WaitWG(wg *sync.WaitGroup, timeout time.Duration) bool {\n\twgC := make(chan struct{})\n\n\tgo func() {\n\t\twg.Wait()\n\t\twgC <- struct{}{}\n\t}()\n\tselect {\n\tcase <-time.After(timeout):\n\t\treturn false\n\tcase <-wgC:\n\t\treturn true\n\t}\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/application_exception.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nconst (\n\tUNKNOWN_APPLICATION_EXCEPTION  = 0\n\tUNKNOWN_METHOD                 = 1\n\tINVALID_MESSAGE_TYPE_EXCEPTION = 2\n\tWRONG_METHOD_NAME              = 3\n\tBAD_SEQUENCE_ID                = 4\n\tMISSING_RESULT                 = 5\n\tINTERNAL_ERROR                 = 6\n\tPROTOCOL_ERROR                 = 7\n)\n\n// Application level Thrift exception\ntype TApplicationException interface {\n\tTException\n\tTypeId() int32\n\tRead(iprot TProtocol) (TApplicationException, error)\n\tWrite(oprot TProtocol) error\n}\n\ntype tApplicationException struct {\n\tmessage string\n\ttype_   int32\n}\n\nfunc (e tApplicationException) Error() string {\n\treturn e.message\n}\n\nfunc NewTApplicationException(type_ int32, message string) TApplicationException {\n\treturn &tApplicationException{message, type_}\n}\n\nfunc (p *tApplicationException) TypeId() int32 {\n\treturn p.type_\n}\n\nfunc (p *tApplicationException) Read(iprot TProtocol) (TApplicationException, error) {\n\t_, err := iprot.ReadStructBegin()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmessage := \"\"\n\ttype_ := int32(UNKNOWN_APPLICATION_EXCEPTION)\n\n\tfor {\n\t\t_, ttype, id, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif ttype == STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch id {\n\t\tcase 1:\n\t\t\tif ttype == STRING {\n\t\t\t\tif message, err = iprot.ReadString(); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err = SkipDefaultDepth(iprot, ttype); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\tcase 2:\n\t\t\tif ttype == I32 {\n\t\t\t\tif type_, err = iprot.ReadI32(); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err = SkipDefaultDepth(iprot, ttype); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\tif err = SkipDefaultDepth(iprot, ttype); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tif err = iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn NewTApplicationException(type_, message), iprot.ReadStructEnd()\n}\n\nfunc (p *tApplicationException) Write(oprot TProtocol) (err error) {\n\terr = oprot.WriteStructBegin(\"TApplicationException\")\n\tif len(p.Error()) > 0 {\n\t\terr = oprot.WriteFieldBegin(\"message\", STRING, 1)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = oprot.WriteString(p.Error())\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = oprot.WriteFieldEnd()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\terr = oprot.WriteFieldBegin(\"type\", I32, 2)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = oprot.WriteI32(p.type_)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = oprot.WriteFieldEnd()\n\tif err != nil {\n\t\treturn\n\t}\n\terr = oprot.WriteFieldStop()\n\tif err != nil {\n\t\treturn\n\t}\n\terr = oprot.WriteStructEnd()\n\treturn\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/application_exception_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"testing\"\n)\n\nfunc TestTApplicationException(t *testing.T) {\n\texc := NewTApplicationException(UNKNOWN_APPLICATION_EXCEPTION, \"\")\n\tif exc.Error() != \"\" {\n\t\tt.Fatalf(\"Expected empty string for exception but found '%s'\", exc.Error())\n\t}\n\tif exc.TypeId() != UNKNOWN_APPLICATION_EXCEPTION {\n\t\tt.Fatalf(\"Expected type UNKNOWN for exception but found '%s'\", exc.TypeId())\n\t}\n\texc = NewTApplicationException(WRONG_METHOD_NAME, \"junk_method\")\n\tif exc.Error() != \"junk_method\" {\n\t\tt.Fatalf(\"Expected 'junk_method' for exception but found '%s'\", exc.Error())\n\t}\n\tif exc.TypeId() != WRONG_METHOD_NAME {\n\t\tt.Fatalf(\"Expected type WRONG_METHOD_NAME for exception but found '%s'\", exc.TypeId())\n\t}\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/binary_protocol.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n)\n\ntype TBinaryProtocol struct {\n\ttrans         TRichTransport\n\torigTransport TTransport\n\treader        io.Reader\n\twriter        io.Writer\n\tstrictRead    bool\n\tstrictWrite   bool\n\tbuffer        [64]byte\n}\n\ntype TBinaryProtocolFactory struct {\n\tstrictRead  bool\n\tstrictWrite bool\n}\n\nfunc NewTBinaryProtocolTransport(t TTransport) *TBinaryProtocol {\n\treturn NewTBinaryProtocol(t, false, true)\n}\n\nfunc NewTBinaryProtocol(t TTransport, strictRead, strictWrite bool) *TBinaryProtocol {\n\tp := &TBinaryProtocol{origTransport: t, strictRead: strictRead, strictWrite: strictWrite}\n\tif et, ok := t.(TRichTransport); ok {\n\t\tp.trans = et\n\t} else {\n\t\tp.trans = NewTRichTransport(t)\n\t}\n\tp.reader = p.trans\n\tp.writer = p.trans\n\treturn p\n}\n\nfunc NewTBinaryProtocolFactoryDefault() *TBinaryProtocolFactory {\n\treturn NewTBinaryProtocolFactory(false, true)\n}\n\nfunc NewTBinaryProtocolFactory(strictRead, strictWrite bool) *TBinaryProtocolFactory {\n\treturn &TBinaryProtocolFactory{strictRead: strictRead, strictWrite: strictWrite}\n}\n\nfunc (p *TBinaryProtocolFactory) GetProtocol(t TTransport) TProtocol {\n\treturn NewTBinaryProtocol(t, p.strictRead, p.strictWrite)\n}\n\n/**\n * Writing Methods\n */\n\nfunc (p *TBinaryProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error {\n\tif p.strictWrite {\n\t\tversion := uint32(VERSION_1) | uint32(typeId)\n\t\te := p.WriteI32(int32(version))\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t\te = p.WriteString(name)\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t\te = p.WriteI32(seqId)\n\t\treturn e\n\t} else {\n\t\te := p.WriteString(name)\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t\te = p.WriteByte(int8(typeId))\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t\te = p.WriteI32(seqId)\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) WriteMessageEnd() error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) WriteStructBegin(name string) error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) WriteStructEnd() error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {\n\te := p.WriteByte(int8(typeId))\n\tif e != nil {\n\t\treturn e\n\t}\n\te = p.WriteI16(id)\n\treturn e\n}\n\nfunc (p *TBinaryProtocol) WriteFieldEnd() error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) WriteFieldStop() error {\n\te := p.WriteByte(STOP)\n\treturn e\n}\n\nfunc (p *TBinaryProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {\n\te := p.WriteByte(int8(keyType))\n\tif e != nil {\n\t\treturn e\n\t}\n\te = p.WriteByte(int8(valueType))\n\tif e != nil {\n\t\treturn e\n\t}\n\te = p.WriteI32(int32(size))\n\treturn e\n}\n\nfunc (p *TBinaryProtocol) WriteMapEnd() error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) WriteListBegin(elemType TType, size int) error {\n\te := p.WriteByte(int8(elemType))\n\tif e != nil {\n\t\treturn e\n\t}\n\te = p.WriteI32(int32(size))\n\treturn e\n}\n\nfunc (p *TBinaryProtocol) WriteListEnd() error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) WriteSetBegin(elemType TType, size int) error {\n\te := p.WriteByte(int8(elemType))\n\tif e != nil {\n\t\treturn e\n\t}\n\te = p.WriteI32(int32(size))\n\treturn e\n}\n\nfunc (p *TBinaryProtocol) WriteSetEnd() error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) WriteBool(value bool) error {\n\tif value {\n\t\treturn p.WriteByte(1)\n\t}\n\treturn p.WriteByte(0)\n}\n\nfunc (p *TBinaryProtocol) WriteByte(value int8) error {\n\te := p.trans.WriteByte(byte(value))\n\treturn NewTProtocolException(e)\n}\n\nfunc (p *TBinaryProtocol) WriteI16(value int16) error {\n\tv := p.buffer[0:2]\n\tbinary.BigEndian.PutUint16(v, uint16(value))\n\t_, e := p.writer.Write(v)\n\treturn NewTProtocolException(e)\n}\n\nfunc (p *TBinaryProtocol) WriteI32(value int32) error {\n\tv := p.buffer[0:4]\n\tbinary.BigEndian.PutUint32(v, uint32(value))\n\t_, e := p.writer.Write(v)\n\treturn NewTProtocolException(e)\n}\n\nfunc (p *TBinaryProtocol) WriteI64(value int64) error {\n\tv := p.buffer[0:8]\n\tbinary.BigEndian.PutUint64(v, uint64(value))\n\t_, err := p.writer.Write(v)\n\treturn NewTProtocolException(err)\n}\n\nfunc (p *TBinaryProtocol) WriteDouble(value float64) error {\n\treturn p.WriteI64(int64(math.Float64bits(value)))\n}\n\nfunc (p *TBinaryProtocol) WriteString(value string) error {\n\te := p.WriteI32(int32(len(value)))\n\tif e != nil {\n\t\treturn e\n\t}\n\t_, err := p.trans.WriteString(value)\n\treturn NewTProtocolException(err)\n}\n\nfunc (p *TBinaryProtocol) WriteBinary(value []byte) error {\n\te := p.WriteI32(int32(len(value)))\n\tif e != nil {\n\t\treturn e\n\t}\n\t_, err := p.writer.Write(value)\n\treturn NewTProtocolException(err)\n}\n\n/**\n * Reading methods\n */\n\nfunc (p *TBinaryProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) {\n\tsize, e := p.ReadI32()\n\tif e != nil {\n\t\treturn \"\", typeId, 0, NewTProtocolException(e)\n\t}\n\tif size < 0 {\n\t\ttypeId = TMessageType(size & 0x0ff)\n\t\tversion := int64(int64(size) & VERSION_MASK)\n\t\tif version != VERSION_1 {\n\t\t\treturn name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf(\"Bad version in ReadMessageBegin\"))\n\t\t}\n\t\tname, e = p.ReadString()\n\t\tif e != nil {\n\t\t\treturn name, typeId, seqId, NewTProtocolException(e)\n\t\t}\n\t\tseqId, e = p.ReadI32()\n\t\tif e != nil {\n\t\t\treturn name, typeId, seqId, NewTProtocolException(e)\n\t\t}\n\t\treturn name, typeId, seqId, nil\n\t}\n\tif p.strictRead {\n\t\treturn name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf(\"Missing version in ReadMessageBegin\"))\n\t}\n\tname, e2 := p.readStringBody(size)\n\tif e2 != nil {\n\t\treturn name, typeId, seqId, e2\n\t}\n\tb, e3 := p.ReadByte()\n\tif e3 != nil {\n\t\treturn name, typeId, seqId, e3\n\t}\n\ttypeId = TMessageType(b)\n\tseqId, e4 := p.ReadI32()\n\tif e4 != nil {\n\t\treturn name, typeId, seqId, e4\n\t}\n\treturn name, typeId, seqId, nil\n}\n\nfunc (p *TBinaryProtocol) ReadMessageEnd() error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) ReadStructBegin() (name string, err error) {\n\treturn\n}\n\nfunc (p *TBinaryProtocol) ReadStructEnd() error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) ReadFieldBegin() (name string, typeId TType, seqId int16, err error) {\n\tt, err := p.ReadByte()\n\ttypeId = TType(t)\n\tif err != nil {\n\t\treturn name, typeId, seqId, err\n\t}\n\tif t != STOP {\n\t\tseqId, err = p.ReadI16()\n\t}\n\treturn name, typeId, seqId, err\n}\n\nfunc (p *TBinaryProtocol) ReadFieldEnd() error {\n\treturn nil\n}\n\nvar invalidDataLength = NewTProtocolExceptionWithType(INVALID_DATA, errors.New(\"Invalid data length\"))\n\nfunc (p *TBinaryProtocol) ReadMapBegin() (kType, vType TType, size int, err error) {\n\tk, e := p.ReadByte()\n\tif e != nil {\n\t\terr = NewTProtocolException(e)\n\t\treturn\n\t}\n\tkType = TType(k)\n\tv, e := p.ReadByte()\n\tif e != nil {\n\t\terr = NewTProtocolException(e)\n\t\treturn\n\t}\n\tvType = TType(v)\n\tsize32, e := p.ReadI32()\n\tif e != nil {\n\t\terr = NewTProtocolException(e)\n\t\treturn\n\t}\n\tif size32 < 0 {\n\t\terr = invalidDataLength\n\t\treturn\n\t}\n\tsize = int(size32)\n\treturn kType, vType, size, nil\n}\n\nfunc (p *TBinaryProtocol) ReadMapEnd() error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) ReadListBegin() (elemType TType, size int, err error) {\n\tb, e := p.ReadByte()\n\tif e != nil {\n\t\terr = NewTProtocolException(e)\n\t\treturn\n\t}\n\telemType = TType(b)\n\tsize32, e := p.ReadI32()\n\tif e != nil {\n\t\terr = NewTProtocolException(e)\n\t\treturn\n\t}\n\tif size32 < 0 {\n\t\terr = invalidDataLength\n\t\treturn\n\t}\n\tsize = int(size32)\n\n\treturn\n}\n\nfunc (p *TBinaryProtocol) ReadListEnd() error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) ReadSetBegin() (elemType TType, size int, err error) {\n\tb, e := p.ReadByte()\n\tif e != nil {\n\t\terr = NewTProtocolException(e)\n\t\treturn\n\t}\n\telemType = TType(b)\n\tsize32, e := p.ReadI32()\n\tif e != nil {\n\t\terr = NewTProtocolException(e)\n\t\treturn\n\t}\n\tif size32 < 0 {\n\t\terr = invalidDataLength\n\t\treturn\n\t}\n\tsize = int(size32)\n\treturn elemType, size, nil\n}\n\nfunc (p *TBinaryProtocol) ReadSetEnd() error {\n\treturn nil\n}\n\nfunc (p *TBinaryProtocol) ReadBool() (bool, error) {\n\tb, e := p.ReadByte()\n\tv := true\n\tif b != 1 {\n\t\tv = false\n\t}\n\treturn v, e\n}\n\nfunc (p *TBinaryProtocol) ReadByte() (int8, error) {\n\tv, err := p.trans.ReadByte()\n\treturn int8(v), err\n}\n\nfunc (p *TBinaryProtocol) ReadI16() (value int16, err error) {\n\tbuf := p.buffer[0:2]\n\terr = p.readAll(buf)\n\tvalue = int16(binary.BigEndian.Uint16(buf))\n\treturn value, err\n}\n\nfunc (p *TBinaryProtocol) ReadI32() (value int32, err error) {\n\tbuf := p.buffer[0:4]\n\terr = p.readAll(buf)\n\tvalue = int32(binary.BigEndian.Uint32(buf))\n\treturn value, err\n}\n\nfunc (p *TBinaryProtocol) ReadI64() (value int64, err error) {\n\tbuf := p.buffer[0:8]\n\terr = p.readAll(buf)\n\tvalue = int64(binary.BigEndian.Uint64(buf))\n\treturn value, err\n}\n\nfunc (p *TBinaryProtocol) ReadDouble() (value float64, err error) {\n\tbuf := p.buffer[0:8]\n\terr = p.readAll(buf)\n\tvalue = math.Float64frombits(binary.BigEndian.Uint64(buf))\n\treturn value, err\n}\n\nfunc (p *TBinaryProtocol) ReadString() (value string, err error) {\n\tsize, e := p.ReadI32()\n\tif e != nil {\n\t\treturn \"\", e\n\t}\n\tif size < 0 {\n\t\terr = invalidDataLength\n\t\treturn\n\t}\n\n\treturn p.readStringBody(size)\n}\n\nfunc (p *TBinaryProtocol) ReadBinary() ([]byte, error) {\n\tsize, e := p.ReadI32()\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\tif size < 0 {\n\t\treturn nil, invalidDataLength\n\t}\n\tif uint64(size) > p.trans.RemainingBytes() {\n\t\treturn nil, invalidDataLength\n\t}\n\n\tisize := int(size)\n\tbuf := make([]byte, isize)\n\t_, err := io.ReadFull(p.trans, buf)\n\treturn buf, NewTProtocolException(err)\n}\n\nfunc (p *TBinaryProtocol) Flush() (err error) {\n\treturn NewTProtocolException(p.trans.Flush())\n}\n\nfunc (p *TBinaryProtocol) Skip(fieldType TType) (err error) {\n\treturn SkipDefaultDepth(p, fieldType)\n}\n\nfunc (p *TBinaryProtocol) Transport() TTransport {\n\treturn p.origTransport\n}\n\nfunc (p *TBinaryProtocol) readAll(buf []byte) error {\n\t_, err := io.ReadFull(p.reader, buf)\n\treturn NewTProtocolException(err)\n}\n\nconst readLimit = 32768\n\nfunc (p *TBinaryProtocol) readStringBody(size int32) (value string, err error) {\n\tif size < 0 {\n\t\treturn \"\", nil\n\t}\n\tif uint64(size) > p.trans.RemainingBytes() {\n\t\treturn \"\", invalidDataLength\n\t}\n\n\tvar (\n\t\tbuf bytes.Buffer\n\t\te   error\n\t\tb   []byte\n\t)\n\n\tswitch {\n\tcase int(size) <= len(p.buffer):\n\t\tb = p.buffer[:size] // avoids allocation for small reads\n\tcase int(size) < readLimit:\n\t\tb = make([]byte, size)\n\tdefault:\n\t\tb = make([]byte, readLimit)\n\t}\n\n\tfor size > 0 {\n\t\t_, e = io.ReadFull(p.trans, b)\n\t\tbuf.Write(b)\n\t\tif e != nil {\n\t\t\tbreak\n\t\t}\n\t\tsize -= readLimit\n\t\tif size < readLimit && size > 0 {\n\t\t\tb = b[:size]\n\t\t}\n\t}\n\treturn buf.String(), NewTProtocolException(e)\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/binary_protocol_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"testing\"\n)\n\nfunc TestReadWriteBinaryProtocol(t *testing.T) {\n\tReadWriteProtocolTest(t, NewTBinaryProtocolFactoryDefault())\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/buffered_transport.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bufio\"\n)\n\ntype TBufferedTransportFactory struct {\n\tsize int\n}\n\ntype TBufferedTransport struct {\n\tbufio.ReadWriter\n\ttp TTransport\n}\n\nfunc (p *TBufferedTransportFactory) GetTransport(trans TTransport) TTransport {\n\treturn NewTBufferedTransport(trans, p.size)\n}\n\nfunc NewTBufferedTransportFactory(bufferSize int) *TBufferedTransportFactory {\n\treturn &TBufferedTransportFactory{size: bufferSize}\n}\n\nfunc NewTBufferedTransport(trans TTransport, bufferSize int) *TBufferedTransport {\n\treturn &TBufferedTransport{\n\t\tReadWriter: bufio.ReadWriter{\n\t\t\tReader: bufio.NewReaderSize(trans, bufferSize),\n\t\t\tWriter: bufio.NewWriterSize(trans, bufferSize),\n\t\t},\n\t\ttp: trans,\n\t}\n}\n\nfunc (p *TBufferedTransport) IsOpen() bool {\n\treturn p.tp.IsOpen()\n}\n\nfunc (p *TBufferedTransport) Open() (err error) {\n\treturn p.tp.Open()\n}\n\nfunc (p *TBufferedTransport) Close() (err error) {\n\treturn p.tp.Close()\n}\n\nfunc (p *TBufferedTransport) Read(b []byte) (int, error) {\n\tn, err := p.ReadWriter.Read(b)\n\tif err != nil {\n\t\tp.ReadWriter.Reader.Reset(p.tp)\n\t}\n\treturn n, err\n}\n\nfunc (p *TBufferedTransport) Write(b []byte) (int, error) {\n\tn, err := p.ReadWriter.Write(b)\n\tif err != nil {\n\t\tp.ReadWriter.Writer.Reset(p.tp)\n\t}\n\treturn n, err\n}\n\nfunc (p *TBufferedTransport) Flush() error {\n\tif err := p.ReadWriter.Flush(); err != nil {\n\t\tp.ReadWriter.Writer.Reset(p.tp)\n\t\treturn err\n\t}\n\treturn p.tp.Flush()\n}\n\nfunc (p *TBufferedTransport) RemainingBytes() (num_bytes uint64) {\n\treturn p.tp.RemainingBytes()\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/buffered_transport_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"testing\"\n)\n\nfunc TestBufferedTransport(t *testing.T) {\n\ttrans := NewTBufferedTransport(NewTMemoryBuffer(), 10240)\n\tTransportTest(t, trans, trans)\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/compact_protocol.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n)\n\nconst (\n\tCOMPACT_PROTOCOL_ID       = 0x082\n\tCOMPACT_VERSION           = 1\n\tCOMPACT_VERSION_MASK      = 0x1f\n\tCOMPACT_TYPE_MASK         = 0x0E0\n\tCOMPACT_TYPE_BITS         = 0x07\n\tCOMPACT_TYPE_SHIFT_AMOUNT = 5\n)\n\ntype tCompactType byte\n\nconst (\n\tCOMPACT_BOOLEAN_TRUE  = 0x01\n\tCOMPACT_BOOLEAN_FALSE = 0x02\n\tCOMPACT_BYTE          = 0x03\n\tCOMPACT_I16           = 0x04\n\tCOMPACT_I32           = 0x05\n\tCOMPACT_I64           = 0x06\n\tCOMPACT_DOUBLE        = 0x07\n\tCOMPACT_BINARY        = 0x08\n\tCOMPACT_LIST          = 0x09\n\tCOMPACT_SET           = 0x0A\n\tCOMPACT_MAP           = 0x0B\n\tCOMPACT_STRUCT        = 0x0C\n)\n\nvar (\n\tttypeToCompactType map[TType]tCompactType\n)\n\nfunc init() {\n\tttypeToCompactType = map[TType]tCompactType{\n\t\tSTOP:   STOP,\n\t\tBOOL:   COMPACT_BOOLEAN_TRUE,\n\t\tBYTE:   COMPACT_BYTE,\n\t\tI16:    COMPACT_I16,\n\t\tI32:    COMPACT_I32,\n\t\tI64:    COMPACT_I64,\n\t\tDOUBLE: COMPACT_DOUBLE,\n\t\tSTRING: COMPACT_BINARY,\n\t\tLIST:   COMPACT_LIST,\n\t\tSET:    COMPACT_SET,\n\t\tMAP:    COMPACT_MAP,\n\t\tSTRUCT: COMPACT_STRUCT,\n\t}\n}\n\ntype TCompactProtocolFactory struct{}\n\nfunc NewTCompactProtocolFactory() *TCompactProtocolFactory {\n\treturn &TCompactProtocolFactory{}\n}\n\nfunc (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol {\n\treturn NewTCompactProtocol(trans)\n}\n\ntype TCompactProtocol struct {\n\ttrans         TRichTransport\n\torigTransport TTransport\n\n\t// Used to keep track of the last field for the current and previous structs,\n\t// so we can do the delta stuff.\n\tlastField   []int\n\tlastFieldId int\n\n\t// If we encounter a boolean field begin, save the TField here so it can\n\t// have the value incorporated.\n\tbooleanFieldName    string\n\tbooleanFieldId      int16\n\tbooleanFieldPending bool\n\n\t// If we read a field header, and it's a boolean field, save the boolean\n\t// value here so that readBool can use it.\n\tboolValue          bool\n\tboolValueIsNotNull bool\n\tbuffer             [64]byte\n}\n\n// Create a TCompactProtocol given a TTransport\nfunc NewTCompactProtocol(trans TTransport) *TCompactProtocol {\n\tp := &TCompactProtocol{origTransport: trans, lastField: []int{}}\n\tif et, ok := trans.(TRichTransport); ok {\n\t\tp.trans = et\n\t} else {\n\t\tp.trans = NewTRichTransport(trans)\n\t}\n\n\treturn p\n\n}\n\n//\n// Public Writing methods.\n//\n\n// Write a message header to the wire. Compact Protocol messages contain the\n// protocol version so we can migrate forwards in the future if need be.\nfunc (p *TCompactProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error {\n\terr := p.writeByteDirect(COMPACT_PROTOCOL_ID)\n\tif err != nil {\n\t\treturn NewTProtocolException(err)\n\t}\n\terr = p.writeByteDirect((COMPACT_VERSION & COMPACT_VERSION_MASK) | ((byte(typeId) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK))\n\tif err != nil {\n\t\treturn NewTProtocolException(err)\n\t}\n\t_, err = p.writeVarint32(seqid)\n\tif err != nil {\n\t\treturn NewTProtocolException(err)\n\t}\n\te := p.WriteString(name)\n\treturn e\n\n}\n\nfunc (p *TCompactProtocol) WriteMessageEnd() error { return nil }\n\n// Write a struct begin. This doesn't actually put anything on the wire. We\n// use it as an opportunity to put special placeholder markers on the field\n// stack so we can get the field id deltas correct.\nfunc (p *TCompactProtocol) WriteStructBegin(name string) error {\n\tp.lastField = append(p.lastField, p.lastFieldId)\n\tp.lastFieldId = 0\n\treturn nil\n}\n\n// Write a struct end. This doesn't actually put anything on the wire. We use\n// this as an opportunity to pop the last field from the current struct off\n// of the field stack.\nfunc (p *TCompactProtocol) WriteStructEnd() error {\n\tp.lastFieldId = p.lastField[len(p.lastField)-1]\n\tp.lastField = p.lastField[:len(p.lastField)-1]\n\treturn nil\n}\n\nfunc (p *TCompactProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {\n\tif typeId == BOOL {\n\t\t// we want to possibly include the value, so we'll wait.\n\t\tp.booleanFieldName, p.booleanFieldId, p.booleanFieldPending = name, id, true\n\t\treturn nil\n\t}\n\t_, err := p.writeFieldBeginInternal(name, typeId, id, 0xFF)\n\treturn NewTProtocolException(err)\n}\n\n// The workhorse of writeFieldBegin. It has the option of doing a\n// 'type override' of the type header. This is used specifically in the\n// boolean field case.\nfunc (p *TCompactProtocol) writeFieldBeginInternal(name string, typeId TType, id int16, typeOverride byte) (int, error) {\n\t// short lastField = lastField_.pop();\n\n\t// if there's a type override, use that.\n\tvar typeToWrite byte\n\tif typeOverride == 0xFF {\n\t\ttypeToWrite = byte(p.getCompactType(typeId))\n\t} else {\n\t\ttypeToWrite = typeOverride\n\t}\n\t// check if we can use delta encoding for the field id\n\tfieldId := int(id)\n\twritten := 0\n\tif fieldId > p.lastFieldId && fieldId-p.lastFieldId <= 15 {\n\t\t// write them together\n\t\terr := p.writeByteDirect(byte((fieldId-p.lastFieldId)<<4) | typeToWrite)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t} else {\n\t\t// write them separate\n\t\terr := p.writeByteDirect(typeToWrite)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\terr = p.WriteI16(id)\n\t\twritten = 1 + 2\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\n\tp.lastFieldId = fieldId\n\t// p.lastField.Push(field.id);\n\treturn written, nil\n}\n\nfunc (p *TCompactProtocol) WriteFieldEnd() error { return nil }\n\nfunc (p *TCompactProtocol) WriteFieldStop() error {\n\terr := p.writeByteDirect(STOP)\n\treturn NewTProtocolException(err)\n}\n\nfunc (p *TCompactProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {\n\tif size == 0 {\n\t\terr := p.writeByteDirect(0)\n\t\treturn NewTProtocolException(err)\n\t}\n\t_, err := p.writeVarint32(int32(size))\n\tif err != nil {\n\t\treturn NewTProtocolException(err)\n\t}\n\terr = p.writeByteDirect(byte(p.getCompactType(keyType))<<4 | byte(p.getCompactType(valueType)))\n\treturn NewTProtocolException(err)\n}\n\nfunc (p *TCompactProtocol) WriteMapEnd() error { return nil }\n\n// Write a list header.\nfunc (p *TCompactProtocol) WriteListBegin(elemType TType, size int) error {\n\t_, err := p.writeCollectionBegin(elemType, size)\n\treturn NewTProtocolException(err)\n}\n\nfunc (p *TCompactProtocol) WriteListEnd() error { return nil }\n\n// Write a set header.\nfunc (p *TCompactProtocol) WriteSetBegin(elemType TType, size int) error {\n\t_, err := p.writeCollectionBegin(elemType, size)\n\treturn NewTProtocolException(err)\n}\n\nfunc (p *TCompactProtocol) WriteSetEnd() error { return nil }\n\nfunc (p *TCompactProtocol) WriteBool(value bool) error {\n\tv := byte(COMPACT_BOOLEAN_FALSE)\n\tif value {\n\t\tv = byte(COMPACT_BOOLEAN_TRUE)\n\t}\n\tif p.booleanFieldPending {\n\t\t// we haven't written the field header yet\n\t\t_, err := p.writeFieldBeginInternal(p.booleanFieldName, BOOL, p.booleanFieldId, v)\n\t\tp.booleanFieldPending = false\n\t\treturn NewTProtocolException(err)\n\t}\n\t// we're not part of a field, so just write the value.\n\terr := p.writeByteDirect(v)\n\treturn NewTProtocolException(err)\n}\n\n// Write a byte. Nothing to see here!\nfunc (p *TCompactProtocol) WriteByte(value int8) error {\n\terr := p.writeByteDirect(byte(value))\n\treturn NewTProtocolException(err)\n}\n\n// Write an I16 as a zigzag varint.\nfunc (p *TCompactProtocol) WriteI16(value int16) error {\n\t_, err := p.writeVarint32(p.int32ToZigzag(int32(value)))\n\treturn NewTProtocolException(err)\n}\n\n// Write an i32 as a zigzag varint.\nfunc (p *TCompactProtocol) WriteI32(value int32) error {\n\t_, err := p.writeVarint32(p.int32ToZigzag(value))\n\treturn NewTProtocolException(err)\n}\n\n// Write an i64 as a zigzag varint.\nfunc (p *TCompactProtocol) WriteI64(value int64) error {\n\t_, err := p.writeVarint64(p.int64ToZigzag(value))\n\treturn NewTProtocolException(err)\n}\n\n// Write a double to the wire as 8 bytes.\nfunc (p *TCompactProtocol) WriteDouble(value float64) error {\n\tbuf := p.buffer[0:8]\n\tbinary.LittleEndian.PutUint64(buf, math.Float64bits(value))\n\t_, err := p.trans.Write(buf)\n\treturn NewTProtocolException(err)\n}\n\n// Write a string to the wire with a varint size preceding.\nfunc (p *TCompactProtocol) WriteString(value string) error {\n\t_, e := p.writeVarint32(int32(len(value)))\n\tif e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\tif len(value) > 0 {\n\t}\n\t_, e = p.trans.WriteString(value)\n\treturn e\n}\n\n// Write a byte array, using a varint for the size.\nfunc (p *TCompactProtocol) WriteBinary(bin []byte) error {\n\t_, e := p.writeVarint32(int32(len(bin)))\n\tif e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\tif len(bin) > 0 {\n\t\t_, e = p.trans.Write(bin)\n\t\treturn NewTProtocolException(e)\n\t}\n\treturn nil\n}\n\n//\n// Reading methods.\n//\n\n// Read a message header.\nfunc (p *TCompactProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) {\n\n\tprotocolId, err := p.readByteDirect()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif protocolId != COMPACT_PROTOCOL_ID {\n\t\te := fmt.Errorf(\"Expected protocol id %02x but got %02x\", COMPACT_PROTOCOL_ID, protocolId)\n\t\treturn \"\", typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, e)\n\t}\n\n\tversionAndType, err := p.readByteDirect()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tversion := versionAndType & COMPACT_VERSION_MASK\n\ttypeId = TMessageType((versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_BITS)\n\tif version != COMPACT_VERSION {\n\t\te := fmt.Errorf(\"Expected version %02x but got %02x\", COMPACT_VERSION, version)\n\t\terr = NewTProtocolExceptionWithType(BAD_VERSION, e)\n\t\treturn\n\t}\n\tseqId, e := p.readVarint32()\n\tif e != nil {\n\t\terr = NewTProtocolException(e)\n\t\treturn\n\t}\n\tname, err = p.ReadString()\n\treturn\n}\n\nfunc (p *TCompactProtocol) ReadMessageEnd() error { return nil }\n\n// Read a struct begin. There's nothing on the wire for this, but it is our\n// opportunity to push a new struct begin marker onto the field stack.\nfunc (p *TCompactProtocol) ReadStructBegin() (name string, err error) {\n\tp.lastField = append(p.lastField, p.lastFieldId)\n\tp.lastFieldId = 0\n\treturn\n}\n\n// Doesn't actually consume any wire data, just removes the last field for\n// this struct from the field stack.\nfunc (p *TCompactProtocol) ReadStructEnd() error {\n\t// consume the last field we read off the wire.\n\tp.lastFieldId = p.lastField[len(p.lastField)-1]\n\tp.lastField = p.lastField[:len(p.lastField)-1]\n\treturn nil\n}\n\n// Read a field header off the wire.\nfunc (p *TCompactProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err error) {\n\tt, err := p.readByteDirect()\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// if it's a stop, then we can return immediately, as the struct is over.\n\tif (t & 0x0f) == STOP {\n\t\treturn \"\", STOP, 0, nil\n\t}\n\n\t// mask off the 4 MSB of the type header. it could contain a field id delta.\n\tmodifier := int16((t & 0xf0) >> 4)\n\tif modifier == 0 {\n\t\t// not a delta. look ahead for the zigzag varint field id.\n\t\tid, err = p.ReadI16()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t} else {\n\t\t// has a delta. add the delta to the last read field id.\n\t\tid = int16(p.lastFieldId) + modifier\n\t}\n\ttypeId, e := p.getTType(tCompactType(t & 0x0f))\n\tif e != nil {\n\t\terr = NewTProtocolException(e)\n\t\treturn\n\t}\n\n\t// if this happens to be a boolean field, the value is encoded in the type\n\tif p.isBoolType(t) {\n\t\t// save the boolean value in a special instance variable.\n\t\tp.boolValue = (byte(t)&0x0f == COMPACT_BOOLEAN_TRUE)\n\t\tp.boolValueIsNotNull = true\n\t}\n\n\t// push the new field onto the field stack so we can keep the deltas going.\n\tp.lastFieldId = int(id)\n\treturn\n}\n\nfunc (p *TCompactProtocol) ReadFieldEnd() error { return nil }\n\n// Read a map header off the wire. If the size is zero, skip reading the key\n// and value type. This means that 0-length maps will yield TMaps without the\n// \"correct\" types.\nfunc (p *TCompactProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) {\n\tsize32, e := p.readVarint32()\n\tif e != nil {\n\t\terr = NewTProtocolException(e)\n\t\treturn\n\t}\n\tif size32 < 0 {\n\t\terr = invalidDataLength\n\t\treturn\n\t}\n\tsize = int(size32)\n\n\tkeyAndValueType := byte(STOP)\n\tif size != 0 {\n\t\tkeyAndValueType, err = p.readByteDirect()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tkeyType, _ = p.getTType(tCompactType(keyAndValueType >> 4))\n\tvalueType, _ = p.getTType(tCompactType(keyAndValueType & 0xf))\n\treturn\n}\n\nfunc (p *TCompactProtocol) ReadMapEnd() error { return nil }\n\n// Read a list header off the wire. If the list size is 0-14, the size will\n// be packed into the element type header. If it's a longer list, the 4 MSB\n// of the element type header will be 0xF, and a varint will follow with the\n// true size.\nfunc (p *TCompactProtocol) ReadListBegin() (elemType TType, size int, err error) {\n\tsize_and_type, err := p.readByteDirect()\n\tif err != nil {\n\t\treturn\n\t}\n\tsize = int((size_and_type >> 4) & 0x0f)\n\tif size == 15 {\n\t\tsize2, e := p.readVarint32()\n\t\tif e != nil {\n\t\t\terr = NewTProtocolException(e)\n\t\t\treturn\n\t\t}\n\t\tif size2 < 0 {\n\t\t\terr = invalidDataLength\n\t\t\treturn\n\t\t}\n\t\tsize = int(size2)\n\t}\n\telemType, e := p.getTType(tCompactType(size_and_type))\n\tif e != nil {\n\t\terr = NewTProtocolException(e)\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (p *TCompactProtocol) ReadListEnd() error { return nil }\n\n// Read a set header off the wire. If the set size is 0-14, the size will\n// be packed into the element type header. If it's a longer set, the 4 MSB\n// of the element type header will be 0xF, and a varint will follow with the\n// true size.\nfunc (p *TCompactProtocol) ReadSetBegin() (elemType TType, size int, err error) {\n\treturn p.ReadListBegin()\n}\n\nfunc (p *TCompactProtocol) ReadSetEnd() error { return nil }\n\n// Read a boolean off the wire. If this is a boolean field, the value should\n// already have been read during readFieldBegin, so we'll just consume the\n// pre-stored value. Otherwise, read a byte.\nfunc (p *TCompactProtocol) ReadBool() (value bool, err error) {\n\tif p.boolValueIsNotNull {\n\t\tp.boolValueIsNotNull = false\n\t\treturn p.boolValue, nil\n\t}\n\tv, err := p.readByteDirect()\n\treturn v == COMPACT_BOOLEAN_TRUE, err\n}\n\n// Read a single byte off the wire. Nothing interesting here.\nfunc (p *TCompactProtocol) ReadByte() (int8, error) {\n\tv, err := p.readByteDirect()\n\tif err != nil {\n\t\treturn 0, NewTProtocolException(err)\n\t}\n\treturn int8(v), err\n}\n\n// Read an i16 from the wire as a zigzag varint.\nfunc (p *TCompactProtocol) ReadI16() (value int16, err error) {\n\tv, err := p.ReadI32()\n\treturn int16(v), err\n}\n\n// Read an i32 from the wire as a zigzag varint.\nfunc (p *TCompactProtocol) ReadI32() (value int32, err error) {\n\tv, e := p.readVarint32()\n\tif e != nil {\n\t\treturn 0, NewTProtocolException(e)\n\t}\n\tvalue = p.zigzagToInt32(v)\n\treturn value, nil\n}\n\n// Read an i64 from the wire as a zigzag varint.\nfunc (p *TCompactProtocol) ReadI64() (value int64, err error) {\n\tv, e := p.readVarint64()\n\tif e != nil {\n\t\treturn 0, NewTProtocolException(e)\n\t}\n\tvalue = p.zigzagToInt64(v)\n\treturn value, nil\n}\n\n// No magic here - just read a double off the wire.\nfunc (p *TCompactProtocol) ReadDouble() (value float64, err error) {\n\tlongBits := p.buffer[0:8]\n\t_, e := io.ReadFull(p.trans, longBits)\n\tif e != nil {\n\t\treturn 0.0, NewTProtocolException(e)\n\t}\n\treturn math.Float64frombits(p.bytesToUint64(longBits)), nil\n}\n\n// Reads a []byte (via readBinary), and then UTF-8 decodes it.\nfunc (p *TCompactProtocol) ReadString() (value string, err error) {\n\tlength, e := p.readVarint32()\n\tif e != nil {\n\t\treturn \"\", NewTProtocolException(e)\n\t}\n\tif length < 0 {\n\t\treturn \"\", invalidDataLength\n\t}\n\tif uint64(length) > p.trans.RemainingBytes() {\n\t\treturn \"\", invalidDataLength\n\t}\n\n\tif length == 0 {\n\t\treturn \"\", nil\n\t}\n\tvar buf []byte\n\tif length <= int32(len(p.buffer)) {\n\t\tbuf = p.buffer[0:length]\n\t} else {\n\t\tbuf = make([]byte, length)\n\t}\n\t_, e = io.ReadFull(p.trans, buf)\n\treturn string(buf), NewTProtocolException(e)\n}\n\n// Read a []byte from the wire.\nfunc (p *TCompactProtocol) ReadBinary() (value []byte, err error) {\n\tlength, e := p.readVarint32()\n\tif e != nil {\n\t\treturn nil, NewTProtocolException(e)\n\t}\n\tif length == 0 {\n\t\treturn []byte{}, nil\n\t}\n\tif length < 0 {\n\t\treturn nil, invalidDataLength\n\t}\n\tif uint64(length) > p.trans.RemainingBytes() {\n\t\treturn nil, invalidDataLength\n\t}\n\n\tbuf := make([]byte, length)\n\t_, e = io.ReadFull(p.trans, buf)\n\treturn buf, NewTProtocolException(e)\n}\n\nfunc (p *TCompactProtocol) Flush() (err error) {\n\treturn NewTProtocolException(p.trans.Flush())\n}\n\nfunc (p *TCompactProtocol) Skip(fieldType TType) (err error) {\n\treturn SkipDefaultDepth(p, fieldType)\n}\n\nfunc (p *TCompactProtocol) Transport() TTransport {\n\treturn p.origTransport\n}\n\n//\n// Internal writing methods\n//\n\n// Abstract method for writing the start of lists and sets. List and sets on\n// the wire differ only by the type indicator.\nfunc (p *TCompactProtocol) writeCollectionBegin(elemType TType, size int) (int, error) {\n\tif size <= 14 {\n\t\treturn 1, p.writeByteDirect(byte(int32(size<<4) | int32(p.getCompactType(elemType))))\n\t}\n\terr := p.writeByteDirect(0xf0 | byte(p.getCompactType(elemType)))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tm, err := p.writeVarint32(int32(size))\n\treturn 1 + m, err\n}\n\n// Write an i32 as a varint. Results in 1-5 bytes on the wire.\n// TODO(pomack): make a permanent buffer like writeVarint64?\nfunc (p *TCompactProtocol) writeVarint32(n int32) (int, error) {\n\ti32buf := p.buffer[0:5]\n\tidx := 0\n\tfor {\n\t\tif (n & ^0x7F) == 0 {\n\t\t\ti32buf[idx] = byte(n)\n\t\t\tidx++\n\t\t\t// p.writeByteDirect(byte(n));\n\t\t\tbreak\n\t\t\t// return;\n\t\t} else {\n\t\t\ti32buf[idx] = byte((n & 0x7F) | 0x80)\n\t\t\tidx++\n\t\t\t// p.writeByteDirect(byte(((n & 0x7F) | 0x80)));\n\t\t\tu := uint32(n)\n\t\t\tn = int32(u >> 7)\n\t\t}\n\t}\n\treturn p.trans.Write(i32buf[0:idx])\n}\n\n// Write an i64 as a varint. Results in 1-10 bytes on the wire.\nfunc (p *TCompactProtocol) writeVarint64(n int64) (int, error) {\n\tvarint64out := p.buffer[0:10]\n\tidx := 0\n\tfor {\n\t\tif (n & ^0x7F) == 0 {\n\t\t\tvarint64out[idx] = byte(n)\n\t\t\tidx++\n\t\t\tbreak\n\t\t} else {\n\t\t\tvarint64out[idx] = byte((n & 0x7F) | 0x80)\n\t\t\tidx++\n\t\t\tu := uint64(n)\n\t\t\tn = int64(u >> 7)\n\t\t}\n\t}\n\treturn p.trans.Write(varint64out[0:idx])\n}\n\n// Convert l into a zigzag long. This allows negative numbers to be\n// represented compactly as a varint.\nfunc (p *TCompactProtocol) int64ToZigzag(l int64) int64 {\n\treturn (l << 1) ^ (l >> 63)\n}\n\n// Convert l into a zigzag long. This allows negative numbers to be\n// represented compactly as a varint.\nfunc (p *TCompactProtocol) int32ToZigzag(n int32) int32 {\n\treturn (n << 1) ^ (n >> 31)\n}\n\nfunc (p *TCompactProtocol) fixedUint64ToBytes(n uint64, buf []byte) {\n\tbinary.LittleEndian.PutUint64(buf, n)\n}\n\nfunc (p *TCompactProtocol) fixedInt64ToBytes(n int64, buf []byte) {\n\tbinary.LittleEndian.PutUint64(buf, uint64(n))\n}\n\n// Writes a byte without any possibility of all that field header nonsense.\n// Used internally by other writing methods that know they need to write a byte.\nfunc (p *TCompactProtocol) writeByteDirect(b byte) error {\n\treturn p.trans.WriteByte(b)\n}\n\n// Writes a byte without any possibility of all that field header nonsense.\nfunc (p *TCompactProtocol) writeIntAsByteDirect(n int) (int, error) {\n\treturn 1, p.writeByteDirect(byte(n))\n}\n\n//\n// Internal reading methods\n//\n\n// Read an i32 from the wire as a varint. The MSB of each byte is set\n// if there is another byte to follow. This can read up to 5 bytes.\nfunc (p *TCompactProtocol) readVarint32() (int32, error) {\n\t// if the wire contains the right stuff, this will just truncate the i64 we\n\t// read and get us the right sign.\n\tv, err := p.readVarint64()\n\treturn int32(v), err\n}\n\n// Read an i64 from the wire as a proper varint. The MSB of each byte is set\n// if there is another byte to follow. This can read up to 10 bytes.\nfunc (p *TCompactProtocol) readVarint64() (int64, error) {\n\tshift := uint(0)\n\tresult := int64(0)\n\tfor {\n\t\tb, err := p.readByteDirect()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tresult |= int64(b&0x7f) << shift\n\t\tif (b & 0x80) != 0x80 {\n\t\t\tbreak\n\t\t}\n\t\tshift += 7\n\t}\n\treturn result, nil\n}\n\n// Read a byte, unlike ReadByte that reads Thrift-byte that is i8.\nfunc (p *TCompactProtocol) readByteDirect() (byte, error) {\n\treturn p.trans.ReadByte()\n}\n\n//\n// encoding helpers\n//\n\n// Convert from zigzag int to int.\nfunc (p *TCompactProtocol) zigzagToInt32(n int32) int32 {\n\tu := uint32(n)\n\treturn int32(u>>1) ^ -(n & 1)\n}\n\n// Convert from zigzag long to long.\nfunc (p *TCompactProtocol) zigzagToInt64(n int64) int64 {\n\tu := uint64(n)\n\treturn int64(u>>1) ^ -(n & 1)\n}\n\n// Note that it's important that the mask bytes are long literals,\n// otherwise they'll default to ints, and when you shift an int left 56 bits,\n// you just get a messed up int.\nfunc (p *TCompactProtocol) bytesToInt64(b []byte) int64 {\n\treturn int64(binary.LittleEndian.Uint64(b))\n}\n\n// Note that it's important that the mask bytes are long literals,\n// otherwise they'll default to ints, and when you shift an int left 56 bits,\n// you just get a messed up int.\nfunc (p *TCompactProtocol) bytesToUint64(b []byte) uint64 {\n\treturn binary.LittleEndian.Uint64(b)\n}\n\n//\n// type testing and converting\n//\n\nfunc (p *TCompactProtocol) isBoolType(b byte) bool {\n\treturn (b&0x0f) == COMPACT_BOOLEAN_TRUE || (b&0x0f) == COMPACT_BOOLEAN_FALSE\n}\n\n// Given a tCompactType constant, convert it to its corresponding\n// TType value.\nfunc (p *TCompactProtocol) getTType(t tCompactType) (TType, error) {\n\tswitch byte(t) & 0x0f {\n\tcase STOP:\n\t\treturn STOP, nil\n\tcase COMPACT_BOOLEAN_FALSE, COMPACT_BOOLEAN_TRUE:\n\t\treturn BOOL, nil\n\tcase COMPACT_BYTE:\n\t\treturn BYTE, nil\n\tcase COMPACT_I16:\n\t\treturn I16, nil\n\tcase COMPACT_I32:\n\t\treturn I32, nil\n\tcase COMPACT_I64:\n\t\treturn I64, nil\n\tcase COMPACT_DOUBLE:\n\t\treturn DOUBLE, nil\n\tcase COMPACT_BINARY:\n\t\treturn STRING, nil\n\tcase COMPACT_LIST:\n\t\treturn LIST, nil\n\tcase COMPACT_SET:\n\t\treturn SET, nil\n\tcase COMPACT_MAP:\n\t\treturn MAP, nil\n\tcase COMPACT_STRUCT:\n\t\treturn STRUCT, nil\n\t}\n\treturn STOP, TException(fmt.Errorf(\"don't know what type: %s\", t&0x0f))\n}\n\n// Given a TType value, find the appropriate TCompactProtocol.Types constant.\nfunc (p *TCompactProtocol) getCompactType(t TType) tCompactType {\n\treturn ttypeToCompactType[t]\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/compact_protocol_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestReadWriteCompactProtocol(t *testing.T) {\n\tReadWriteProtocolTest(t, NewTCompactProtocolFactory())\n\t   transports := []TTransport{\n\t     NewTMemoryBuffer(),\n\t     NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 16384))),\n\t     NewTFramedTransport(NewTMemoryBuffer()),\n\t   }\n\t   for _, trans := range transports {\n\t     p := NewTCompactProtocol(trans);\n\t     ReadWriteBool(t, p, trans);\n\t     p = NewTCompactProtocol(trans);\n\t     ReadWriteByte(t, p, trans);\n\t     p = NewTCompactProtocol(trans);\n\t     ReadWriteI16(t, p, trans);\n\t     p = NewTCompactProtocol(trans);\n\t     ReadWriteI32(t, p, trans);\n\t     p = NewTCompactProtocol(trans);\n\t     ReadWriteI64(t, p, trans);\n\t     p = NewTCompactProtocol(trans);\n\t     ReadWriteDouble(t, p, trans);\n\t     p = NewTCompactProtocol(trans);\n\t     ReadWriteString(t, p, trans);\n\t     p = NewTCompactProtocol(trans);\n\t     ReadWriteBinary(t, p, trans);\n\t     trans.Close();\n\t   }\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/debug_protocol.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"log\"\n)\n\ntype TDebugProtocol struct {\n\tDelegate  TProtocol\n\tLogPrefix string\n}\n\ntype TDebugProtocolFactory struct {\n\tUnderlying TProtocolFactory\n\tLogPrefix  string\n}\n\nfunc NewTDebugProtocolFactory(underlying TProtocolFactory, logPrefix string) *TDebugProtocolFactory {\n\treturn &TDebugProtocolFactory{\n\t\tUnderlying: underlying,\n\t\tLogPrefix:  logPrefix,\n\t}\n}\n\nfunc (t *TDebugProtocolFactory) GetProtocol(trans TTransport) TProtocol {\n\treturn &TDebugProtocol{\n\t\tDelegate:  t.Underlying.GetProtocol(trans),\n\t\tLogPrefix: t.LogPrefix,\n\t}\n}\n\nfunc (tdp *TDebugProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error {\n\terr := tdp.Delegate.WriteMessageBegin(name, typeId, seqid)\n\tlog.Printf(\"%sWriteMessageBegin(name=%#v, typeId=%#v, seqid=%#v) => %#v\", tdp.LogPrefix, name, typeId, seqid, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteMessageEnd() error {\n\terr := tdp.Delegate.WriteMessageEnd()\n\tlog.Printf(\"%sWriteMessageEnd() => %#v\", tdp.LogPrefix, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteStructBegin(name string) error {\n\terr := tdp.Delegate.WriteStructBegin(name)\n\tlog.Printf(\"%sWriteStructBegin(name=%#v) => %#v\", tdp.LogPrefix, name, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteStructEnd() error {\n\terr := tdp.Delegate.WriteStructEnd()\n\tlog.Printf(\"%sWriteStructEnd() => %#v\", tdp.LogPrefix, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {\n\terr := tdp.Delegate.WriteFieldBegin(name, typeId, id)\n\tlog.Printf(\"%sWriteFieldBegin(name=%#v, typeId=%#v, id%#v) => %#v\", tdp.LogPrefix, name, typeId, id, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteFieldEnd() error {\n\terr := tdp.Delegate.WriteFieldEnd()\n\tlog.Printf(\"%sWriteFieldEnd() => %#v\", tdp.LogPrefix, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteFieldStop() error {\n\terr := tdp.Delegate.WriteFieldStop()\n\tlog.Printf(\"%sWriteFieldStop() => %#v\", tdp.LogPrefix, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {\n\terr := tdp.Delegate.WriteMapBegin(keyType, valueType, size)\n\tlog.Printf(\"%sWriteMapBegin(keyType=%#v, valueType=%#v, size=%#v) => %#v\", tdp.LogPrefix, keyType, valueType, size, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteMapEnd() error {\n\terr := tdp.Delegate.WriteMapEnd()\n\tlog.Printf(\"%sWriteMapEnd() => %#v\", tdp.LogPrefix, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteListBegin(elemType TType, size int) error {\n\terr := tdp.Delegate.WriteListBegin(elemType, size)\n\tlog.Printf(\"%sWriteListBegin(elemType=%#v, size=%#v) => %#v\", tdp.LogPrefix, elemType, size, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteListEnd() error {\n\terr := tdp.Delegate.WriteListEnd()\n\tlog.Printf(\"%sWriteListEnd() => %#v\", tdp.LogPrefix, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteSetBegin(elemType TType, size int) error {\n\terr := tdp.Delegate.WriteSetBegin(elemType, size)\n\tlog.Printf(\"%sWriteSetBegin(elemType=%#v, size=%#v) => %#v\", tdp.LogPrefix, elemType, size, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteSetEnd() error {\n\terr := tdp.Delegate.WriteSetEnd()\n\tlog.Printf(\"%sWriteSetEnd() => %#v\", tdp.LogPrefix, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteBool(value bool) error {\n\terr := tdp.Delegate.WriteBool(value)\n\tlog.Printf(\"%sWriteBool(value=%#v) => %#v\", tdp.LogPrefix, value, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteByte(value int8) error {\n\terr := tdp.Delegate.WriteByte(value)\n\tlog.Printf(\"%sWriteByte(value=%#v) => %#v\", tdp.LogPrefix, value, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteI16(value int16) error {\n\terr := tdp.Delegate.WriteI16(value)\n\tlog.Printf(\"%sWriteI16(value=%#v) => %#v\", tdp.LogPrefix, value, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteI32(value int32) error {\n\terr := tdp.Delegate.WriteI32(value)\n\tlog.Printf(\"%sWriteI32(value=%#v) => %#v\", tdp.LogPrefix, value, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteI64(value int64) error {\n\terr := tdp.Delegate.WriteI64(value)\n\tlog.Printf(\"%sWriteI64(value=%#v) => %#v\", tdp.LogPrefix, value, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteDouble(value float64) error {\n\terr := tdp.Delegate.WriteDouble(value)\n\tlog.Printf(\"%sWriteDouble(value=%#v) => %#v\", tdp.LogPrefix, value, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteString(value string) error {\n\terr := tdp.Delegate.WriteString(value)\n\tlog.Printf(\"%sWriteString(value=%#v) => %#v\", tdp.LogPrefix, value, err)\n\treturn err\n}\nfunc (tdp *TDebugProtocol) WriteBinary(value []byte) error {\n\terr := tdp.Delegate.WriteBinary(value)\n\tlog.Printf(\"%sWriteBinary(value=%#v) => %#v\", tdp.LogPrefix, value, err)\n\treturn err\n}\n\nfunc (tdp *TDebugProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) {\n\tname, typeId, seqid, err = tdp.Delegate.ReadMessageBegin()\n\tlog.Printf(\"%sReadMessageBegin() (name=%#v, typeId=%#v, seqid=%#v, err=%#v)\", tdp.LogPrefix, name, typeId, seqid, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadMessageEnd() (err error) {\n\terr = tdp.Delegate.ReadMessageEnd()\n\tlog.Printf(\"%sReadMessageEnd() err=%#v\", tdp.LogPrefix, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadStructBegin() (name string, err error) {\n\tname, err = tdp.Delegate.ReadStructBegin()\n\tlog.Printf(\"%sReadStructBegin() (name%#v, err=%#v)\", tdp.LogPrefix, name, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadStructEnd() (err error) {\n\terr = tdp.Delegate.ReadStructEnd()\n\tlog.Printf(\"%sReadStructEnd() err=%#v\", tdp.LogPrefix, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err error) {\n\tname, typeId, id, err = tdp.Delegate.ReadFieldBegin()\n\tlog.Printf(\"%sReadFieldBegin() (name=%#v, typeId=%#v, id=%#v, err=%#v)\", tdp.LogPrefix, name, typeId, id, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadFieldEnd() (err error) {\n\terr = tdp.Delegate.ReadFieldEnd()\n\tlog.Printf(\"%sReadFieldEnd() err=%#v\", tdp.LogPrefix, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) {\n\tkeyType, valueType, size, err = tdp.Delegate.ReadMapBegin()\n\tlog.Printf(\"%sReadMapBegin() (keyType=%#v, valueType=%#v, size=%#v, err=%#v)\", tdp.LogPrefix, keyType, valueType, size, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadMapEnd() (err error) {\n\terr = tdp.Delegate.ReadMapEnd()\n\tlog.Printf(\"%sReadMapEnd() err=%#v\", tdp.LogPrefix, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadListBegin() (elemType TType, size int, err error) {\n\telemType, size, err = tdp.Delegate.ReadListBegin()\n\tlog.Printf(\"%sReadListBegin() (elemType=%#v, size=%#v, err=%#v)\", tdp.LogPrefix, elemType, size, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadListEnd() (err error) {\n\terr = tdp.Delegate.ReadListEnd()\n\tlog.Printf(\"%sReadListEnd() err=%#v\", tdp.LogPrefix, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadSetBegin() (elemType TType, size int, err error) {\n\telemType, size, err = tdp.Delegate.ReadSetBegin()\n\tlog.Printf(\"%sReadSetBegin() (elemType=%#v, size=%#v, err=%#v)\", tdp.LogPrefix, elemType, size, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadSetEnd() (err error) {\n\terr = tdp.Delegate.ReadSetEnd()\n\tlog.Printf(\"%sReadSetEnd() err=%#v\", tdp.LogPrefix, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadBool() (value bool, err error) {\n\tvalue, err = tdp.Delegate.ReadBool()\n\tlog.Printf(\"%sReadBool() (value=%#v, err=%#v)\", tdp.LogPrefix, value, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadByte() (value int8, err error) {\n\tvalue, err = tdp.Delegate.ReadByte()\n\tlog.Printf(\"%sReadByte() (value=%#v, err=%#v)\", tdp.LogPrefix, value, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadI16() (value int16, err error) {\n\tvalue, err = tdp.Delegate.ReadI16()\n\tlog.Printf(\"%sReadI16() (value=%#v, err=%#v)\", tdp.LogPrefix, value, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadI32() (value int32, err error) {\n\tvalue, err = tdp.Delegate.ReadI32()\n\tlog.Printf(\"%sReadI32() (value=%#v, err=%#v)\", tdp.LogPrefix, value, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadI64() (value int64, err error) {\n\tvalue, err = tdp.Delegate.ReadI64()\n\tlog.Printf(\"%sReadI64() (value=%#v, err=%#v)\", tdp.LogPrefix, value, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadDouble() (value float64, err error) {\n\tvalue, err = tdp.Delegate.ReadDouble()\n\tlog.Printf(\"%sReadDouble() (value=%#v, err=%#v)\", tdp.LogPrefix, value, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadString() (value string, err error) {\n\tvalue, err = tdp.Delegate.ReadString()\n\tlog.Printf(\"%sReadString() (value=%#v, err=%#v)\", tdp.LogPrefix, value, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) ReadBinary() (value []byte, err error) {\n\tvalue, err = tdp.Delegate.ReadBinary()\n\tlog.Printf(\"%sReadBinary() (value=%#v, err=%#v)\", tdp.LogPrefix, value, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) Skip(fieldType TType) (err error) {\n\terr = tdp.Delegate.Skip(fieldType)\n\tlog.Printf(\"%sSkip(fieldType=%#v) (err=%#v)\", tdp.LogPrefix, fieldType, err)\n\treturn\n}\nfunc (tdp *TDebugProtocol) Flush() (err error) {\n\terr = tdp.Delegate.Flush()\n\tlog.Printf(\"%sFlush() (err=%#v)\", tdp.LogPrefix, err)\n\treturn\n}\n\nfunc (tdp *TDebugProtocol) Transport() TTransport {\n\treturn tdp.Delegate.Transport()\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/deserializer.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\ntype TDeserializer struct {\n\tTransport TTransport\n\tProtocol  TProtocol\n}\n\nfunc NewTDeserializer() *TDeserializer {\n\tvar transport TTransport\n\ttransport = NewTMemoryBufferLen(1024)\n\n\tprotocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport)\n\n\treturn &TDeserializer{\n\t\ttransport,\n\t\tprotocol}\n}\n\nfunc (t *TDeserializer) ReadString(msg TStruct, s string) (err error) {\n\terr = nil\n\tif _, err = t.Transport.Write([]byte(s)); err != nil {\n\t\treturn\n\t}\n\tif err = msg.Read(t.Protocol); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (t *TDeserializer) Read(msg TStruct, b []byte) (err error) {\n\terr = nil\n\tif _, err = t.Transport.Write(b); err != nil {\n\t\treturn\n\t}\n\tif err = msg.Read(t.Protocol); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/exception.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"errors\"\n)\n\n// Generic Thrift exception\ntype TException interface {\n\terror\n}\n\n// Prepends additional information to an error without losing the Thrift exception interface\nfunc PrependError(prepend string, err error) error {\n\tif t, ok := err.(TTransportException); ok {\n\t\treturn NewTTransportException(t.TypeId(), prepend+t.Error())\n\t}\n\tif t, ok := err.(TProtocolException); ok {\n\t\treturn NewTProtocolExceptionWithType(t.TypeId(), errors.New(prepend+err.Error()))\n\t}\n\tif t, ok := err.(TApplicationException); ok {\n\t\treturn NewTApplicationException(t.TypeId(), prepend+t.Error())\n\t}\n\n\treturn errors.New(prepend + err.Error())\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/exception_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"errors\"\n\t\"testing\"\n)\n\nfunc TestPrependError(t *testing.T) {\n\terr := NewTApplicationException(INTERNAL_ERROR, \"original error\")\n\terr2, ok := PrependError(\"Prepend: \", err).(TApplicationException)\n\tif !ok {\n\t\tt.Fatal(\"Couldn't cast error TApplicationException\")\n\t}\n\tif err2.Error() != \"Prepend: original error\" {\n\t\tt.Fatal(\"Unexpected error string\")\n\t}\n\tif err2.TypeId() != INTERNAL_ERROR {\n\t\tt.Fatal(\"Unexpected type error\")\n\t}\n\n\terr3 := NewTProtocolExceptionWithType(INVALID_DATA, errors.New(\"original error\"))\n\terr4, ok := PrependError(\"Prepend: \", err3).(TProtocolException)\n\tif !ok {\n\t\tt.Fatal(\"Couldn't cast error TProtocolException\")\n\t}\n\tif err4.Error() != \"Prepend: original error\" {\n\t\tt.Fatal(\"Unexpected error string\")\n\t}\n\tif err4.TypeId() != INVALID_DATA {\n\t\tt.Fatal(\"Unexpected type error\")\n\t}\n\n\terr5 := NewTTransportException(TIMED_OUT, \"original error\")\n\terr6, ok := PrependError(\"Prepend: \", err5).(TTransportException)\n\tif !ok {\n\t\tt.Fatal(\"Couldn't cast error TTransportException\")\n\t}\n\tif err6.Error() != \"Prepend: original error\" {\n\t\tt.Fatal(\"Unexpected error string\")\n\t}\n\tif err6.TypeId() != TIMED_OUT {\n\t\tt.Fatal(\"Unexpected type error\")\n\t}\n\n\terr7 := errors.New(\"original error\")\n\terr8 := PrependError(\"Prepend: \", err7)\n\tif err8.Error() != \"Prepend: original error\" {\n\t\tt.Fatal(\"Unexpected error string\")\n\t}\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/field.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\n// Helper class that encapsulates field metadata.\ntype field struct {\n\tname   string\n\ttypeId TType\n\tid     int\n}\n\nfunc newField(n string, t TType, i int) *field {\n\treturn &field{name: n, typeId: t, id: i}\n}\n\nfunc (p *field) Name() string {\n\tif p == nil {\n\t\treturn \"\"\n\t}\n\treturn p.name\n}\n\nfunc (p *field) TypeId() TType {\n\tif p == nil {\n\t\treturn TType(VOID)\n\t}\n\treturn p.typeId\n}\n\nfunc (p *field) Id() int {\n\tif p == nil {\n\t\treturn -1\n\t}\n\treturn p.id\n}\n\nfunc (p *field) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn \"<TField name:'\" + p.name + \"' type:\" + string(p.typeId) + \" field-id:\" + string(p.id) + \">\"\n}\n\nvar ANONYMOUS_FIELD *field\n\ntype fieldSlice []field\n\nfunc (p fieldSlice) Len() int {\n\treturn len(p)\n}\n\nfunc (p fieldSlice) Less(i, j int) bool {\n\treturn p[i].Id() < p[j].Id()\n}\n\nfunc (p fieldSlice) Swap(i, j int) {\n\tp[i], p[j] = p[j], p[i]\n}\n\nfunc init() {\n\tANONYMOUS_FIELD = newField(\"\", STOP, 0)\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/framed_transport.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n)\n\nconst DEFAULT_MAX_LENGTH = 16384000\n\ntype TFramedTransport struct {\n\ttransport TTransport\n\tbuf       bytes.Buffer\n\treader    *bufio.Reader\n\tframeSize uint32 //Current remaining size of the frame. if ==0 read next frame header\n\tbuffer    [4]byte\n\tmaxLength uint32\n}\n\ntype tFramedTransportFactory struct {\n\tfactory   TTransportFactory\n\tmaxLength uint32\n}\n\nfunc NewTFramedTransportFactory(factory TTransportFactory) TTransportFactory {\n\treturn &tFramedTransportFactory{factory: factory, maxLength: DEFAULT_MAX_LENGTH}\n}\n\nfunc NewTFramedTransportFactoryMaxLength(factory TTransportFactory, maxLength uint32) TTransportFactory {\n        return &tFramedTransportFactory{factory: factory, maxLength: maxLength}\n}\n\nfunc (p *tFramedTransportFactory) GetTransport(base TTransport) TTransport {\n\treturn NewTFramedTransportMaxLength(p.factory.GetTransport(base), p.maxLength)\n}\n\nfunc NewTFramedTransport(transport TTransport) *TFramedTransport {\n\treturn &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: DEFAULT_MAX_LENGTH}\n}\n\nfunc NewTFramedTransportMaxLength(transport TTransport, maxLength uint32) *TFramedTransport {\n\treturn &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: maxLength}\n}\n\nfunc (p *TFramedTransport) Open() error {\n\treturn p.transport.Open()\n}\n\nfunc (p *TFramedTransport) IsOpen() bool {\n\treturn p.transport.IsOpen()\n}\n\nfunc (p *TFramedTransport) Close() error {\n\treturn p.transport.Close()\n}\n\nfunc (p *TFramedTransport) Read(buf []byte) (l int, err error) {\n\tif p.frameSize == 0 {\n\t\tp.frameSize, err = p.readFrameHeader()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif p.frameSize < uint32(len(buf)) {\n\t\tframeSize := p.frameSize\n\t\ttmp := make([]byte, p.frameSize)\n\t\tl, err = p.Read(tmp)\n\t\tcopy(buf, tmp)\n\t\tif err == nil {\n\t\t\terr = NewTTransportExceptionFromError(fmt.Errorf(\"Not enough frame size %d to read %d bytes\", frameSize, len(buf)))\n\t\t\treturn\n\t\t}\n\t}\n\tgot, err := p.reader.Read(buf)\n\tp.frameSize = p.frameSize - uint32(got)\n\t//sanity check\n\tif p.frameSize < 0 {\n\t\treturn 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, \"Negative frame size\")\n\t}\n\treturn got, NewTTransportExceptionFromError(err)\n}\n\nfunc (p *TFramedTransport) ReadByte() (c byte, err error) {\n\tif p.frameSize == 0 {\n\t\tp.frameSize, err = p.readFrameHeader()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif p.frameSize < 1 {\n\t\treturn 0, NewTTransportExceptionFromError(fmt.Errorf(\"Not enough frame size %d to read %d bytes\", p.frameSize, 1))\n\t}\n\tc, err = p.reader.ReadByte()\n\tif err == nil {\n\t\tp.frameSize--\n\t}\n\treturn\n}\n\nfunc (p *TFramedTransport) Write(buf []byte) (int, error) {\n\tn, err := p.buf.Write(buf)\n\treturn n, NewTTransportExceptionFromError(err)\n}\n\nfunc (p *TFramedTransport) WriteByte(c byte) error {\n\treturn p.buf.WriteByte(c)\n}\n\nfunc (p *TFramedTransport) WriteString(s string) (n int, err error) {\n\treturn p.buf.WriteString(s)\n}\n\nfunc (p *TFramedTransport) Flush() error {\n\tsize := p.buf.Len()\n\tbuf := p.buffer[:4]\n\tbinary.BigEndian.PutUint32(buf, uint32(size))\n\t_, err := p.transport.Write(buf)\n\tif err != nil {\n\t\treturn NewTTransportExceptionFromError(err)\n\t}\n\tif size > 0 {\n\t\tif n, err := p.buf.WriteTo(p.transport); err != nil {\n\t\t\tprint(\"Error while flushing write buffer of size \", size, \" to transport, only wrote \", n, \" bytes: \", err.Error(), \"\\n\")\n\t\t\treturn NewTTransportExceptionFromError(err)\n\t\t}\n\t}\n\terr = p.transport.Flush()\n\treturn NewTTransportExceptionFromError(err)\n}\n\nfunc (p *TFramedTransport) readFrameHeader() (uint32, error) {\n\tbuf := p.buffer[:4]\n\tif _, err := io.ReadFull(p.reader, buf); err != nil {\n\t\treturn 0, err\n\t}\n\tsize := binary.BigEndian.Uint32(buf)\n\tif size < 0 || size > p.maxLength {\n\t\treturn 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, fmt.Sprintf(\"Incorrect frame size (%d)\", size))\n\t}\n\treturn size, nil\n}\n\nfunc (p *TFramedTransport) RemainingBytes() (num_bytes uint64) {\n\treturn uint64(p.frameSize)\n}\n\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/framed_transport_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"testing\"\n)\n\nfunc TestFramedTransport(t *testing.T) {\n\ttrans := NewTFramedTransport(NewTMemoryBuffer())\n\tTransportTest(t, trans, trans)\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/http_client.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n)\n\n// Default to using the shared http client. Library users are\n// free to change this global client or specify one through\n// THttpClientOptions.\nvar DefaultHttpClient *http.Client = http.DefaultClient\n\ntype THttpClient struct {\n\tclient             *http.Client\n\tresponse           *http.Response\n\turl                *url.URL\n\trequestBuffer      *bytes.Buffer\n\theader             http.Header\n\tnsecConnectTimeout int64\n\tnsecReadTimeout    int64\n}\n\ntype THttpClientTransportFactory struct {\n\toptions THttpClientOptions\n\turl     string\n\tisPost  bool\n}\n\nfunc (p *THttpClientTransportFactory) GetTransport(trans TTransport) TTransport {\n\tif trans != nil {\n\t\tt, ok := trans.(*THttpClient)\n\t\tif ok && t.url != nil {\n\t\t\tif t.requestBuffer != nil {\n\t\t\t\tt2, _ := NewTHttpPostClientWithOptions(t.url.String(), p.options)\n\t\t\t\treturn t2\n\t\t\t}\n\t\t\tt2, _ := NewTHttpClientWithOptions(t.url.String(), p.options)\n\t\t\treturn t2\n\t\t}\n\t}\n\tif p.isPost {\n\t\ts, _ := NewTHttpPostClientWithOptions(p.url, p.options)\n\t\treturn s\n\t}\n\ts, _ := NewTHttpClientWithOptions(p.url, p.options)\n\treturn s\n}\n\ntype THttpClientOptions struct {\n\t// If nil, DefaultHttpClient is used\n\tClient *http.Client\n}\n\nfunc NewTHttpClientTransportFactory(url string) *THttpClientTransportFactory {\n\treturn NewTHttpClientTransportFactoryWithOptions(url, THttpClientOptions{})\n}\n\nfunc NewTHttpClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory {\n\treturn &THttpClientTransportFactory{url: url, isPost: false, options: options}\n}\n\nfunc NewTHttpPostClientTransportFactory(url string) *THttpClientTransportFactory {\n\treturn NewTHttpPostClientTransportFactoryWithOptions(url, THttpClientOptions{})\n}\n\nfunc NewTHttpPostClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory {\n\treturn &THttpClientTransportFactory{url: url, isPost: true, options: options}\n}\n\nfunc NewTHttpClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) {\n\tparsedURL, err := url.Parse(urlstr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresponse, err := http.Get(urlstr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclient := options.Client\n\tif client == nil {\n\t\tclient = DefaultHttpClient\n\t}\n\thttpHeader := map[string][]string{\"Content-Type\": []string{\"application/x-thrift\"}}\n\treturn &THttpClient{client: client, response: response, url: parsedURL, header: httpHeader}, nil\n}\n\nfunc NewTHttpClient(urlstr string) (TTransport, error) {\n\treturn NewTHttpClientWithOptions(urlstr, THttpClientOptions{})\n}\n\nfunc NewTHttpPostClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) {\n\tparsedURL, err := url.Parse(urlstr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbuf := make([]byte, 0, 1024)\n\tclient := options.Client\n\tif client == nil {\n\t\tclient = DefaultHttpClient\n\t}\n\thttpHeader := map[string][]string{\"Content-Type\": []string{\"application/x-thrift\"}}\n\treturn &THttpClient{client: client, url: parsedURL, requestBuffer: bytes.NewBuffer(buf), header: httpHeader}, nil\n}\n\nfunc NewTHttpPostClient(urlstr string) (TTransport, error) {\n\treturn NewTHttpPostClientWithOptions(urlstr, THttpClientOptions{})\n}\n\n// Set the HTTP Header for this specific Thrift Transport\n// It is important that you first assert the TTransport as a THttpClient type\n// like so:\n//\n// httpTrans := trans.(THttpClient)\n// httpTrans.SetHeader(\"User-Agent\",\"Thrift Client 1.0\")\nfunc (p *THttpClient) SetHeader(key string, value string) {\n\tp.header.Add(key, value)\n}\n\n// Get the HTTP Header represented by the supplied Header Key for this specific Thrift Transport\n// It is important that you first assert the TTransport as a THttpClient type\n// like so:\n//\n// httpTrans := trans.(THttpClient)\n// hdrValue := httpTrans.GetHeader(\"User-Agent\")\nfunc (p *THttpClient) GetHeader(key string) string {\n\treturn p.header.Get(key)\n}\n\n// Deletes the HTTP Header given a Header Key for this specific Thrift Transport\n// It is important that you first assert the TTransport as a THttpClient type\n// like so:\n//\n// httpTrans := trans.(THttpClient)\n// httpTrans.DelHeader(\"User-Agent\")\nfunc (p *THttpClient) DelHeader(key string) {\n\tp.header.Del(key)\n}\n\nfunc (p *THttpClient) Open() error {\n\t// do nothing\n\treturn nil\n}\n\nfunc (p *THttpClient) IsOpen() bool {\n\treturn p.response != nil || p.requestBuffer != nil\n}\n\nfunc (p *THttpClient) closeResponse() error {\n\tvar err error\n\tif p.response != nil && p.response.Body != nil {\n\t\t// The docs specify that if keepalive is enabled and the response body is not\n\t\t// read to completion the connection will never be returned to the pool and\n\t\t// reused. Errors are being ignored here because if the connection is invalid\n\t\t// and this fails for some reason, the Close() method will do any remaining\n\t\t// cleanup.\n\t\tio.Copy(ioutil.Discard, p.response.Body)\n\n\t\terr = p.response.Body.Close()\n\t}\n\n\tp.response = nil\n\treturn err\n}\n\nfunc (p *THttpClient) Close() error {\n\tif p.requestBuffer != nil {\n\t\tp.requestBuffer.Reset()\n\t\tp.requestBuffer = nil\n\t}\n\treturn p.closeResponse()\n}\n\nfunc (p *THttpClient) Read(buf []byte) (int, error) {\n\tif p.response == nil {\n\t\treturn 0, NewTTransportException(NOT_OPEN, \"Response buffer is empty, no request.\")\n\t}\n\tn, err := p.response.Body.Read(buf)\n\tif n > 0 && (err == nil || err == io.EOF) {\n\t\treturn n, nil\n\t}\n\treturn n, NewTTransportExceptionFromError(err)\n}\n\nfunc (p *THttpClient) ReadByte() (c byte, err error) {\n\treturn readByte(p.response.Body)\n}\n\nfunc (p *THttpClient) Write(buf []byte) (int, error) {\n\tn, err := p.requestBuffer.Write(buf)\n\treturn n, err\n}\n\nfunc (p *THttpClient) WriteByte(c byte) error {\n\treturn p.requestBuffer.WriteByte(c)\n}\n\nfunc (p *THttpClient) WriteString(s string) (n int, err error) {\n\treturn p.requestBuffer.WriteString(s)\n}\n\nfunc (p *THttpClient) Flush() error {\n\t// Close any previous response body to avoid leaking connections.\n\tp.closeResponse()\n\n\treq, err := http.NewRequest(\"POST\", p.url.String(), p.requestBuffer)\n\tif err != nil {\n\t\treturn NewTTransportExceptionFromError(err)\n\t}\n\treq.Header = p.header\n\tresponse, err := p.client.Do(req)\n\tif err != nil {\n\t\treturn NewTTransportExceptionFromError(err)\n\t}\n\tif response.StatusCode != http.StatusOK {\n\t\t// Close the response to avoid leaking file descriptors. closeResponse does\n\t\t// more than just call Close(), so temporarily assign it and reuse the logic.\n\t\tp.response = response\n\t\tp.closeResponse()\n\n\t\t// TODO(pomack) log bad response\n\t\treturn NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, \"HTTP Response code: \"+strconv.Itoa(response.StatusCode))\n\t}\n\tp.response = response\n\treturn nil\n}\n\nfunc (p *THttpClient) RemainingBytes() (num_bytes uint64) {\n\tlen := p.response.ContentLength\n\tif len >= 0 {\n\t\treturn uint64(len)\n\t}\n\n\tconst maxSize = ^uint64(0)\n\treturn maxSize // the thruth is, we just don't know unless framed is used\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/http_client_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n)\n\nfunc TestHttpClient(t *testing.T) {\n\tl, addr := HttpClientSetupForTest(t)\n\tif l != nil {\n\t\tdefer l.Close()\n\t}\n\ttrans, err := NewTHttpPostClient(\"http://\" + addr.String())\n\tif err != nil {\n\t\tl.Close()\n\t\tt.Fatalf(\"Unable to connect to %s: %s\", addr.String(), err)\n\t}\n\tTransportTest(t, trans, trans)\n}\n\nfunc TestHttpClientHeaders(t *testing.T) {\n\tl, addr := HttpClientSetupForTest(t)\n\tif l != nil {\n\t\tdefer l.Close()\n\t}\n\ttrans, err := NewTHttpPostClient(\"http://\" + addr.String())\n\tif err != nil {\n\t\tl.Close()\n\t\tt.Fatalf(\"Unable to connect to %s: %s\", addr.String(), err)\n\t}\n\tTransportHeaderTest(t, trans, trans)\n}\n\nfunc TestHttpCustomClient(t *testing.T) {\n\tl, addr := HttpClientSetupForTest(t)\n\tif l != nil {\n\t\tdefer l.Close()\n\t}\n\n\thttpTransport := &customHttpTransport{}\n\n\ttrans, err := NewTHttpPostClientWithOptions(\"http://\"+addr.String(), THttpClientOptions{\n\t\tClient: &http.Client{\n\t\t\tTransport: httpTransport,\n\t\t},\n\t})\n\tif err != nil {\n\t\tl.Close()\n\t\tt.Fatalf(\"Unable to connect to %s: %s\", addr.String(), err)\n\t}\n\tTransportHeaderTest(t, trans, trans)\n\n\tif !httpTransport.hit {\n\t\tt.Fatalf(\"Custom client was not used\")\n\t}\n}\n\nfunc TestHttpCustomClientPackageScope(t *testing.T) {\n\tl, addr := HttpClientSetupForTest(t)\n\tif l != nil {\n\t\tdefer l.Close()\n\t}\n\thttpTransport := &customHttpTransport{}\n\tDefaultHttpClient = &http.Client{\n\t\tTransport: httpTransport,\n\t}\n\n\ttrans, err := NewTHttpPostClient(\"http://\" + addr.String())\n\tif err != nil {\n\t\tl.Close()\n\t\tt.Fatalf(\"Unable to connect to %s: %s\", addr.String(), err)\n\t}\n\tTransportHeaderTest(t, trans, trans)\n\n\tif !httpTransport.hit {\n\t\tt.Fatalf(\"Custom client was not used\")\n\t}\n}\n\ntype customHttpTransport struct {\n\thit bool\n}\n\nfunc (c *customHttpTransport) RoundTrip(req *http.Request) (*http.Response, error) {\n\tc.hit = true\n\treturn http.DefaultTransport.RoundTrip(req)\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/http_transport.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport \"net/http\"\n\n// NewThriftHandlerFunc is a function that create a ready to use Apache Thrift Handler function\nfunc NewThriftHandlerFunc(processor TProcessor,\n\tinPfactory, outPfactory TProtocolFactory) func(w http.ResponseWriter, r *http.Request) {\n\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Header().Add(\"Content-Type\", \"application/x-thrift\")\n\n\t\ttransport := NewStreamTransport(r.Body, w)\n\t\tprocessor.Process(inPfactory.GetProtocol(transport), outPfactory.GetProtocol(transport))\n\t}\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/iostream_transport.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bufio\"\n\t\"io\"\n)\n\n// StreamTransport is a Transport made of an io.Reader and/or an io.Writer\ntype StreamTransport struct {\n\tio.Reader\n\tio.Writer\n\tisReadWriter bool\n\tclosed       bool\n}\n\ntype StreamTransportFactory struct {\n\tReader       io.Reader\n\tWriter       io.Writer\n\tisReadWriter bool\n}\n\nfunc (p *StreamTransportFactory) GetTransport(trans TTransport) TTransport {\n\tif trans != nil {\n\t\tt, ok := trans.(*StreamTransport)\n\t\tif ok {\n\t\t\tif t.isReadWriter {\n\t\t\t\treturn NewStreamTransportRW(t.Reader.(io.ReadWriter))\n\t\t\t}\n\t\t\tif t.Reader != nil && t.Writer != nil {\n\t\t\t\treturn NewStreamTransport(t.Reader, t.Writer)\n\t\t\t}\n\t\t\tif t.Reader != nil && t.Writer == nil {\n\t\t\t\treturn NewStreamTransportR(t.Reader)\n\t\t\t}\n\t\t\tif t.Reader == nil && t.Writer != nil {\n\t\t\t\treturn NewStreamTransportW(t.Writer)\n\t\t\t}\n\t\t\treturn &StreamTransport{}\n\t\t}\n\t}\n\tif p.isReadWriter {\n\t\treturn NewStreamTransportRW(p.Reader.(io.ReadWriter))\n\t}\n\tif p.Reader != nil && p.Writer != nil {\n\t\treturn NewStreamTransport(p.Reader, p.Writer)\n\t}\n\tif p.Reader != nil && p.Writer == nil {\n\t\treturn NewStreamTransportR(p.Reader)\n\t}\n\tif p.Reader == nil && p.Writer != nil {\n\t\treturn NewStreamTransportW(p.Writer)\n\t}\n\treturn &StreamTransport{}\n}\n\nfunc NewStreamTransportFactory(reader io.Reader, writer io.Writer, isReadWriter bool) *StreamTransportFactory {\n\treturn &StreamTransportFactory{Reader: reader, Writer: writer, isReadWriter: isReadWriter}\n}\n\nfunc NewStreamTransport(r io.Reader, w io.Writer) *StreamTransport {\n\treturn &StreamTransport{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)}\n}\n\nfunc NewStreamTransportR(r io.Reader) *StreamTransport {\n\treturn &StreamTransport{Reader: bufio.NewReader(r)}\n}\n\nfunc NewStreamTransportW(w io.Writer) *StreamTransport {\n\treturn &StreamTransport{Writer: bufio.NewWriter(w)}\n}\n\nfunc NewStreamTransportRW(rw io.ReadWriter) *StreamTransport {\n\tbufrw := bufio.NewReadWriter(bufio.NewReader(rw), bufio.NewWriter(rw))\n\treturn &StreamTransport{Reader: bufrw, Writer: bufrw, isReadWriter: true}\n}\n\nfunc (p *StreamTransport) IsOpen() bool {\n\treturn !p.closed\n}\n\n// implicitly opened on creation, can't be reopened once closed\nfunc (p *StreamTransport) Open() error {\n\tif !p.closed {\n\t\treturn NewTTransportException(ALREADY_OPEN, \"StreamTransport already open.\")\n\t} else {\n\t\treturn NewTTransportException(NOT_OPEN, \"cannot reopen StreamTransport.\")\n\t}\n}\n\n// Closes both the input and output streams.\nfunc (p *StreamTransport) Close() error {\n\tif p.closed {\n\t\treturn NewTTransportException(NOT_OPEN, \"StreamTransport already closed.\")\n\t}\n\tp.closed = true\n\tclosedReader := false\n\tif p.Reader != nil {\n\t\tc, ok := p.Reader.(io.Closer)\n\t\tif ok {\n\t\t\te := c.Close()\n\t\t\tclosedReader = true\n\t\t\tif e != nil {\n\t\t\t\treturn e\n\t\t\t}\n\t\t}\n\t\tp.Reader = nil\n\t}\n\tif p.Writer != nil && (!closedReader || !p.isReadWriter) {\n\t\tc, ok := p.Writer.(io.Closer)\n\t\tif ok {\n\t\t\te := c.Close()\n\t\t\tif e != nil {\n\t\t\t\treturn e\n\t\t\t}\n\t\t}\n\t\tp.Writer = nil\n\t}\n\treturn nil\n}\n\n// Flushes the underlying output stream if not null.\nfunc (p *StreamTransport) Flush() error {\n\tif p.Writer == nil {\n\t\treturn NewTTransportException(NOT_OPEN, \"Cannot flush null outputStream\")\n\t}\n\tf, ok := p.Writer.(Flusher)\n\tif ok {\n\t\terr := f.Flush()\n\t\tif err != nil {\n\t\t\treturn NewTTransportExceptionFromError(err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p *StreamTransport) Read(c []byte) (n int, err error) {\n\tn, err = p.Reader.Read(c)\n\tif err != nil {\n\t\terr = NewTTransportExceptionFromError(err)\n\t}\n\treturn\n}\n\nfunc (p *StreamTransport) ReadByte() (c byte, err error) {\n\tf, ok := p.Reader.(io.ByteReader)\n\tif ok {\n\t\tc, err = f.ReadByte()\n\t} else {\n\t\tc, err = readByte(p.Reader)\n\t}\n\tif err != nil {\n\t\terr = NewTTransportExceptionFromError(err)\n\t}\n\treturn\n}\n\nfunc (p *StreamTransport) Write(c []byte) (n int, err error) {\n\tn, err = p.Writer.Write(c)\n\tif err != nil {\n\t\terr = NewTTransportExceptionFromError(err)\n\t}\n\treturn\n}\n\nfunc (p *StreamTransport) WriteByte(c byte) (err error) {\n\tf, ok := p.Writer.(io.ByteWriter)\n\tif ok {\n\t\terr = f.WriteByte(c)\n\t} else {\n\t\terr = writeByte(p.Writer, c)\n\t}\n\tif err != nil {\n\t\terr = NewTTransportExceptionFromError(err)\n\t}\n\treturn\n}\n\nfunc (p *StreamTransport) WriteString(s string) (n int, err error) {\n\tf, ok := p.Writer.(stringWriter)\n\tif ok {\n\t\tn, err = f.WriteString(s)\n\t} else {\n\t\tn, err = p.Writer.Write([]byte(s))\n\t}\n\tif err != nil {\n\t\terr = NewTTransportExceptionFromError(err)\n\t}\n\treturn\n}\n\nfunc (p *StreamTransport) RemainingBytes() (num_bytes uint64) {\n\tconst maxSize = ^uint64(0)\n\treturn maxSize  // the thruth is, we just don't know unless framed is used\n}\n\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/iostream_transport_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestStreamTransport(t *testing.T) {\n\ttrans := NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 1024)))\n\tTransportTest(t, trans, trans)\n}\n\nfunc TestStreamTransportOpenClose(t *testing.T) {\n\ttrans := NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 1024)))\n\tif !trans.IsOpen() {\n\t\tt.Fatal(\"StreamTransport should be already open\")\n\t}\n\tif trans.Open() == nil {\n\t\tt.Fatal(\"StreamTransport should return error when open twice\")\n\t}\n\tif trans.Close() != nil {\n\t\tt.Fatal(\"StreamTransport should not return error when closing open transport\")\n\t}\n\tif trans.IsOpen() {\n\t\tt.Fatal(\"StreamTransport should not be open after close\")\n\t}\n\tif trans.Close() == nil {\n\t\tt.Fatal(\"StreamTransport should return error when closing a non open transport\")\n\t}\n\tif trans.Open() == nil {\n\t\tt.Fatal(\"StreamTransport should not be able to reopen\")\n\t}\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/json_protocol.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n)\n\nconst (\n\tTHRIFT_JSON_PROTOCOL_VERSION = 1\n)\n\n// for references to _ParseContext see tsimplejson_protocol.go\n\n// JSON protocol implementation for thrift.\n//\n// This protocol produces/consumes a simple output format\n// suitable for parsing by scripting languages.  It should not be\n// confused with the full-featured TJSONProtocol.\n//\ntype TJSONProtocol struct {\n\t*TSimpleJSONProtocol\n}\n\n// Constructor\nfunc NewTJSONProtocol(t TTransport) *TJSONProtocol {\n\tv := &TJSONProtocol{TSimpleJSONProtocol: NewTSimpleJSONProtocol(t)}\n\tv.parseContextStack = append(v.parseContextStack, int(_CONTEXT_IN_TOPLEVEL))\n\tv.dumpContext = append(v.dumpContext, int(_CONTEXT_IN_TOPLEVEL))\n\treturn v\n}\n\n// Factory\ntype TJSONProtocolFactory struct{}\n\nfunc (p *TJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol {\n\treturn NewTJSONProtocol(trans)\n}\n\nfunc NewTJSONProtocolFactory() *TJSONProtocolFactory {\n\treturn &TJSONProtocolFactory{}\n}\n\nfunc (p *TJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error {\n\tp.resetContextStack() // THRIFT-3735\n\tif e := p.OutputListBegin(); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteI32(THRIFT_JSON_PROTOCOL_VERSION); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteString(name); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteByte(int8(typeId)); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteI32(seqId); e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TJSONProtocol) WriteMessageEnd() error {\n\treturn p.OutputListEnd()\n}\n\nfunc (p *TJSONProtocol) WriteStructBegin(name string) error {\n\tif e := p.OutputObjectBegin(); e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TJSONProtocol) WriteStructEnd() error {\n\treturn p.OutputObjectEnd()\n}\n\nfunc (p *TJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {\n\tif e := p.WriteI16(id); e != nil {\n\t\treturn e\n\t}\n\tif e := p.OutputObjectBegin(); e != nil {\n\t\treturn e\n\t}\n\ts, e1 := p.TypeIdToString(typeId)\n\tif e1 != nil {\n\t\treturn e1\n\t}\n\tif e := p.WriteString(s); e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TJSONProtocol) WriteFieldEnd() error {\n\treturn p.OutputObjectEnd()\n}\n\nfunc (p *TJSONProtocol) WriteFieldStop() error { return nil }\n\nfunc (p *TJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {\n\tif e := p.OutputListBegin(); e != nil {\n\t\treturn e\n\t}\n\ts, e1 := p.TypeIdToString(keyType)\n\tif e1 != nil {\n\t\treturn e1\n\t}\n\tif e := p.WriteString(s); e != nil {\n\t\treturn e\n\t}\n\ts, e1 = p.TypeIdToString(valueType)\n\tif e1 != nil {\n\t\treturn e1\n\t}\n\tif e := p.WriteString(s); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteI64(int64(size)); e != nil {\n\t\treturn e\n\t}\n\treturn p.OutputObjectBegin()\n}\n\nfunc (p *TJSONProtocol) WriteMapEnd() error {\n\tif e := p.OutputObjectEnd(); e != nil {\n\t\treturn e\n\t}\n\treturn p.OutputListEnd()\n}\n\nfunc (p *TJSONProtocol) WriteListBegin(elemType TType, size int) error {\n\treturn p.OutputElemListBegin(elemType, size)\n}\n\nfunc (p *TJSONProtocol) WriteListEnd() error {\n\treturn p.OutputListEnd()\n}\n\nfunc (p *TJSONProtocol) WriteSetBegin(elemType TType, size int) error {\n\treturn p.OutputElemListBegin(elemType, size)\n}\n\nfunc (p *TJSONProtocol) WriteSetEnd() error {\n\treturn p.OutputListEnd()\n}\n\nfunc (p *TJSONProtocol) WriteBool(b bool) error {\n\tif b {\n\t\treturn p.WriteI32(1)\n\t}\n\treturn p.WriteI32(0)\n}\n\nfunc (p *TJSONProtocol) WriteByte(b int8) error {\n\treturn p.WriteI32(int32(b))\n}\n\nfunc (p *TJSONProtocol) WriteI16(v int16) error {\n\treturn p.WriteI32(int32(v))\n}\n\nfunc (p *TJSONProtocol) WriteI32(v int32) error {\n\treturn p.OutputI64(int64(v))\n}\n\nfunc (p *TJSONProtocol) WriteI64(v int64) error {\n\treturn p.OutputI64(int64(v))\n}\n\nfunc (p *TJSONProtocol) WriteDouble(v float64) error {\n\treturn p.OutputF64(v)\n}\n\nfunc (p *TJSONProtocol) WriteString(v string) error {\n\treturn p.OutputString(v)\n}\n\nfunc (p *TJSONProtocol) WriteBinary(v []byte) error {\n\t// JSON library only takes in a string,\n\t// not an arbitrary byte array, to ensure bytes are transmitted\n\t// efficiently we must convert this into a valid JSON string\n\t// therefore we use base64 encoding to avoid excessive escaping/quoting\n\tif e := p.OutputPreValue(); e != nil {\n\t\treturn e\n\t}\n\tif _, e := p.write(JSON_QUOTE_BYTES); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\twriter := base64.NewEncoder(base64.StdEncoding, p.writer)\n\tif _, e := writer.Write(v); e != nil {\n\t\tp.writer.Reset(p.trans) // THRIFT-3735\n\t\treturn NewTProtocolException(e)\n\t}\n\tif e := writer.Close(); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\tif _, e := p.write(JSON_QUOTE_BYTES); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\treturn p.OutputPostValue()\n}\n\n// Reading methods.\nfunc (p *TJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) {\n\tp.resetContextStack() // THRIFT-3735\n\tif isNull, err := p.ParseListBegin(); isNull || err != nil {\n\t\treturn name, typeId, seqId, err\n\t}\n\tversion, err := p.ReadI32()\n\tif err != nil {\n\t\treturn name, typeId, seqId, err\n\t}\n\tif version != THRIFT_JSON_PROTOCOL_VERSION {\n\t\te := fmt.Errorf(\"Unknown Protocol version %d, expected version %d\", version, THRIFT_JSON_PROTOCOL_VERSION)\n\t\treturn name, typeId, seqId, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\n\t}\n\tif name, err = p.ReadString(); err != nil {\n\t\treturn name, typeId, seqId, err\n\t}\n\tbTypeId, err := p.ReadByte()\n\ttypeId = TMessageType(bTypeId)\n\tif err != nil {\n\t\treturn name, typeId, seqId, err\n\t}\n\tif seqId, err = p.ReadI32(); err != nil {\n\t\treturn name, typeId, seqId, err\n\t}\n\treturn name, typeId, seqId, nil\n}\n\nfunc (p *TJSONProtocol) ReadMessageEnd() error {\n\terr := p.ParseListEnd()\n\treturn err\n}\n\nfunc (p *TJSONProtocol) ReadStructBegin() (name string, err error) {\n\t_, err = p.ParseObjectStart()\n\treturn \"\", err\n}\n\nfunc (p *TJSONProtocol) ReadStructEnd() error {\n\treturn p.ParseObjectEnd()\n}\n\nfunc (p *TJSONProtocol) ReadFieldBegin() (string, TType, int16, error) {\n\tb, _ := p.reader.Peek(1)\n\tif len(b) < 1 || b[0] == JSON_RBRACE[0] || b[0] == JSON_RBRACKET[0] {\n\t\treturn \"\", STOP, -1, nil\n\t}\n\tfieldId, err := p.ReadI16()\n\tif err != nil {\n\t\treturn \"\", STOP, fieldId, err\n\t}\n\tif _, err = p.ParseObjectStart(); err != nil {\n\t\treturn \"\", STOP, fieldId, err\n\t}\n\tsType, err := p.ReadString()\n\tif err != nil {\n\t\treturn \"\", STOP, fieldId, err\n\t}\n\tfType, err := p.StringToTypeId(sType)\n\treturn \"\", fType, fieldId, err\n}\n\nfunc (p *TJSONProtocol) ReadFieldEnd() error {\n\treturn p.ParseObjectEnd()\n}\n\nfunc (p *TJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e error) {\n\tif isNull, e := p.ParseListBegin(); isNull || e != nil {\n\t\treturn VOID, VOID, 0, e\n\t}\n\n\t// read keyType\n\tsKeyType, e := p.ReadString()\n\tif e != nil {\n\t\treturn keyType, valueType, size, e\n\t}\n\tkeyType, e = p.StringToTypeId(sKeyType)\n\tif e != nil {\n\t\treturn keyType, valueType, size, e\n\t}\n\n\t// read valueType\n\tsValueType, e := p.ReadString()\n\tif e != nil {\n\t\treturn keyType, valueType, size, e\n\t}\n\tvalueType, e = p.StringToTypeId(sValueType)\n\tif e != nil {\n\t\treturn keyType, valueType, size, e\n\t}\n\n\t// read size\n\tiSize, e := p.ReadI64()\n\tif e != nil {\n\t\treturn keyType, valueType, size, e\n\t}\n\tsize = int(iSize)\n\n\t_, e = p.ParseObjectStart()\n\treturn keyType, valueType, size, e\n}\n\nfunc (p *TJSONProtocol) ReadMapEnd() error {\n\te := p.ParseObjectEnd()\n\tif e != nil {\n\t\treturn e\n\t}\n\treturn p.ParseListEnd()\n}\n\nfunc (p *TJSONProtocol) ReadListBegin() (elemType TType, size int, e error) {\n\treturn p.ParseElemListBegin()\n}\n\nfunc (p *TJSONProtocol) ReadListEnd() error {\n\treturn p.ParseListEnd()\n}\n\nfunc (p *TJSONProtocol) ReadSetBegin() (elemType TType, size int, e error) {\n\treturn p.ParseElemListBegin()\n}\n\nfunc (p *TJSONProtocol) ReadSetEnd() error {\n\treturn p.ParseListEnd()\n}\n\nfunc (p *TJSONProtocol) ReadBool() (bool, error) {\n\tvalue, err := p.ReadI32()\n\treturn (value != 0), err\n}\n\nfunc (p *TJSONProtocol) ReadByte() (int8, error) {\n\tv, err := p.ReadI64()\n\treturn int8(v), err\n}\n\nfunc (p *TJSONProtocol) ReadI16() (int16, error) {\n\tv, err := p.ReadI64()\n\treturn int16(v), err\n}\n\nfunc (p *TJSONProtocol) ReadI32() (int32, error) {\n\tv, err := p.ReadI64()\n\treturn int32(v), err\n}\n\nfunc (p *TJSONProtocol) ReadI64() (int64, error) {\n\tv, _, err := p.ParseI64()\n\treturn v, err\n}\n\nfunc (p *TJSONProtocol) ReadDouble() (float64, error) {\n\tv, _, err := p.ParseF64()\n\treturn v, err\n}\n\nfunc (p *TJSONProtocol) ReadString() (string, error) {\n\tvar v string\n\tif err := p.ParsePreValue(); err != nil {\n\t\treturn v, err\n\t}\n\tf, _ := p.reader.Peek(1)\n\tif len(f) > 0 && f[0] == JSON_QUOTE {\n\t\tp.reader.ReadByte()\n\t\tvalue, err := p.ParseStringBody()\n\t\tv = value\n\t\tif err != nil {\n\t\t\treturn v, err\n\t\t}\n\t} else if len(f) > 0 && f[0] == JSON_NULL[0] {\n\t\tb := make([]byte, len(JSON_NULL))\n\t\t_, err := p.reader.Read(b)\n\t\tif err != nil {\n\t\t\treturn v, NewTProtocolException(err)\n\t\t}\n\t\tif string(b) != string(JSON_NULL) {\n\t\t\te := fmt.Errorf(\"Expected a JSON string, found unquoted data started with %s\", string(b))\n\t\t\treturn v, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t}\n\t} else {\n\t\te := fmt.Errorf(\"Expected a JSON string, found unquoted data started with %s\", string(f))\n\t\treturn v, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t}\n\treturn v, p.ParsePostValue()\n}\n\nfunc (p *TJSONProtocol) ReadBinary() ([]byte, error) {\n\tvar v []byte\n\tif err := p.ParsePreValue(); err != nil {\n\t\treturn nil, err\n\t}\n\tf, _ := p.reader.Peek(1)\n\tif len(f) > 0 && f[0] == JSON_QUOTE {\n\t\tp.reader.ReadByte()\n\t\tvalue, err := p.ParseBase64EncodedBody()\n\t\tv = value\n\t\tif err != nil {\n\t\t\treturn v, err\n\t\t}\n\t} else if len(f) > 0 && f[0] == JSON_NULL[0] {\n\t\tb := make([]byte, len(JSON_NULL))\n\t\t_, err := p.reader.Read(b)\n\t\tif err != nil {\n\t\t\treturn v, NewTProtocolException(err)\n\t\t}\n\t\tif string(b) != string(JSON_NULL) {\n\t\t\te := fmt.Errorf(\"Expected a JSON string, found unquoted data started with %s\", string(b))\n\t\t\treturn v, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t}\n\t} else {\n\t\te := fmt.Errorf(\"Expected a JSON string, found unquoted data started with %s\", string(f))\n\t\treturn v, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t}\n\n\treturn v, p.ParsePostValue()\n}\n\nfunc (p *TJSONProtocol) Flush() (err error) {\n\terr = p.writer.Flush()\n\tif err == nil {\n\t\terr = p.trans.Flush()\n\t}\n\treturn NewTProtocolException(err)\n}\n\nfunc (p *TJSONProtocol) Skip(fieldType TType) (err error) {\n\treturn SkipDefaultDepth(p, fieldType)\n}\n\nfunc (p *TJSONProtocol) Transport() TTransport {\n\treturn p.trans\n}\n\nfunc (p *TJSONProtocol) OutputElemListBegin(elemType TType, size int) error {\n\tif e := p.OutputListBegin(); e != nil {\n\t\treturn e\n\t}\n\ts, e1 := p.TypeIdToString(elemType)\n\tif e1 != nil {\n\t\treturn e1\n\t}\n\tif e := p.WriteString(s); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteI64(int64(size)); e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) {\n\tif isNull, e := p.ParseListBegin(); isNull || e != nil {\n\t\treturn VOID, 0, e\n\t}\n\tsElemType, err := p.ReadString()\n\tif err != nil {\n\t\treturn VOID, size, err\n\t}\n\telemType, err = p.StringToTypeId(sElemType)\n\tif err != nil {\n\t\treturn elemType, size, err\n\t}\n\tnSize, err2 := p.ReadI64()\n\tsize = int(nSize)\n\treturn elemType, size, err2\n}\n\nfunc (p *TJSONProtocol) readElemListBegin() (elemType TType, size int, e error) {\n\tif isNull, e := p.ParseListBegin(); isNull || e != nil {\n\t\treturn VOID, 0, e\n\t}\n\tsElemType, err := p.ReadString()\n\tif err != nil {\n\t\treturn VOID, size, err\n\t}\n\telemType, err = p.StringToTypeId(sElemType)\n\tif err != nil {\n\t\treturn elemType, size, err\n\t}\n\tnSize, err2 := p.ReadI64()\n\tsize = int(nSize)\n\treturn elemType, size, err2\n}\n\nfunc (p *TJSONProtocol) writeElemListBegin(elemType TType, size int) error {\n\tif e := p.OutputListBegin(); e != nil {\n\t\treturn e\n\t}\n\ts, e1 := p.TypeIdToString(elemType)\n\tif e1 != nil {\n\t\treturn e1\n\t}\n\tif e := p.OutputString(s); e != nil {\n\t\treturn e\n\t}\n\tif e := p.OutputI64(int64(size)); e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TJSONProtocol) TypeIdToString(fieldType TType) (string, error) {\n\tswitch byte(fieldType) {\n\tcase BOOL:\n\t\treturn \"tf\", nil\n\tcase BYTE:\n\t\treturn \"i8\", nil\n\tcase I16:\n\t\treturn \"i16\", nil\n\tcase I32:\n\t\treturn \"i32\", nil\n\tcase I64:\n\t\treturn \"i64\", nil\n\tcase DOUBLE:\n\t\treturn \"dbl\", nil\n\tcase STRING:\n\t\treturn \"str\", nil\n\tcase STRUCT:\n\t\treturn \"rec\", nil\n\tcase MAP:\n\t\treturn \"map\", nil\n\tcase SET:\n\t\treturn \"set\", nil\n\tcase LIST:\n\t\treturn \"lst\", nil\n\t}\n\n\te := fmt.Errorf(\"Unknown fieldType: %d\", int(fieldType))\n\treturn \"\", NewTProtocolExceptionWithType(INVALID_DATA, e)\n}\n\nfunc (p *TJSONProtocol) StringToTypeId(fieldType string) (TType, error) {\n\tswitch fieldType {\n\tcase \"tf\":\n\t\treturn TType(BOOL), nil\n\tcase \"i8\":\n\t\treturn TType(BYTE), nil\n\tcase \"i16\":\n\t\treturn TType(I16), nil\n\tcase \"i32\":\n\t\treturn TType(I32), nil\n\tcase \"i64\":\n\t\treturn TType(I64), nil\n\tcase \"dbl\":\n\t\treturn TType(DOUBLE), nil\n\tcase \"str\":\n\t\treturn TType(STRING), nil\n\tcase \"rec\":\n\t\treturn TType(STRUCT), nil\n\tcase \"map\":\n\t\treturn TType(MAP), nil\n\tcase \"set\":\n\t\treturn TType(SET), nil\n\tcase \"lst\":\n\t\treturn TType(LIST), nil\n\t}\n\n\te := fmt.Errorf(\"Unknown type identifier: %s\", fieldType)\n\treturn TType(STOP), NewTProtocolExceptionWithType(INVALID_DATA, e)\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/json_protocol_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"testing\"\n)\n\nfunc TestWriteJSONProtocolBool(t *testing.T) {\n\tthetype := \"boolean\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\tfor _, value := range BOOL_VALUES {\n\t\tif e := p.WriteBool(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\texpected := \"\"\n\t\tif value {\n\t\t\texpected = \"1\"\n\t\t} else {\n\t\t\texpected = \"0\"\n\t\t}\n\t\tif s != expected {\n\t\t\tt.Fatalf(\"Bad value for %s %v: %s expected\", thetype, value, s)\n\t\t}\n\t\tv := -1\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || (v != 0) != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadJSONProtocolBool(t *testing.T) {\n\tthetype := \"boolean\"\n\tfor _, value := range BOOL_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTJSONProtocol(trans)\n\t\tif value {\n\t\t\ttrans.Write([]byte{'1'}) // not JSON_TRUE\n\t\t} else {\n\t\t\ttrans.Write([]byte{'0'}) // not JSON_FALSE\n\t\t}\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadBool()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tvv := -1\n\t\tif err := json.Unmarshal([]byte(s), &vv); err != nil || (vv != 0) != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, vv)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestWriteJSONProtocolByte(t *testing.T) {\n\tthetype := \"byte\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\tfor _, value := range BYTE_VALUES {\n\t\tif e := p.WriteByte(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif s != fmt.Sprint(value) {\n\t\t\tt.Fatalf(\"Bad value for %s %v: %s\", thetype, value, s)\n\t\t}\n\t\tv := int8(0)\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadJSONProtocolByte(t *testing.T) {\n\tthetype := \"byte\"\n\tfor _, value := range BYTE_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTJSONProtocol(trans)\n\t\ttrans.WriteString(strconv.Itoa(int(value)))\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadByte()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestWriteJSONProtocolI16(t *testing.T) {\n\tthetype := \"int16\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\tfor _, value := range INT16_VALUES {\n\t\tif e := p.WriteI16(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif s != fmt.Sprint(value) {\n\t\t\tt.Fatalf(\"Bad value for %s %v: %s\", thetype, value, s)\n\t\t}\n\t\tv := int16(0)\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadJSONProtocolI16(t *testing.T) {\n\tthetype := \"int16\"\n\tfor _, value := range INT16_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTJSONProtocol(trans)\n\t\ttrans.WriteString(strconv.Itoa(int(value)))\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadI16()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestWriteJSONProtocolI32(t *testing.T) {\n\tthetype := \"int32\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\tfor _, value := range INT32_VALUES {\n\t\tif e := p.WriteI32(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif s != fmt.Sprint(value) {\n\t\t\tt.Fatalf(\"Bad value for %s %v: %s\", thetype, value, s)\n\t\t}\n\t\tv := int32(0)\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadJSONProtocolI32(t *testing.T) {\n\tthetype := \"int32\"\n\tfor _, value := range INT32_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTJSONProtocol(trans)\n\t\ttrans.WriteString(strconv.Itoa(int(value)))\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadI32()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestWriteJSONProtocolI64(t *testing.T) {\n\tthetype := \"int64\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\tfor _, value := range INT64_VALUES {\n\t\tif e := p.WriteI64(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif s != fmt.Sprint(value) {\n\t\t\tt.Fatalf(\"Bad value for %s %v: %s\", thetype, value, s)\n\t\t}\n\t\tv := int64(0)\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadJSONProtocolI64(t *testing.T) {\n\tthetype := \"int64\"\n\tfor _, value := range INT64_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTJSONProtocol(trans)\n\t\ttrans.WriteString(strconv.FormatInt(value, 10))\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadI64()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestWriteJSONProtocolDouble(t *testing.T) {\n\tthetype := \"double\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\tfor _, value := range DOUBLE_VALUES {\n\t\tif e := p.WriteDouble(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif math.IsInf(value, 1) {\n\t\t\tif s != jsonQuote(JSON_INFINITY) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, expected: %v\", thetype, value, s, jsonQuote(JSON_INFINITY))\n\t\t\t}\n\t\t} else if math.IsInf(value, -1) {\n\t\t\tif s != jsonQuote(JSON_NEGATIVE_INFINITY) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, expected: %v\", thetype, value, s, jsonQuote(JSON_NEGATIVE_INFINITY))\n\t\t\t}\n\t\t} else if math.IsNaN(value) {\n\t\t\tif s != jsonQuote(JSON_NAN) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, expected: %v\", thetype, value, s, jsonQuote(JSON_NAN))\n\t\t\t}\n\t\t} else {\n\t\t\tif s != fmt.Sprint(value) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v: %s\", thetype, value, s)\n\t\t\t}\n\t\t\tv := float64(0)\n\t\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t\t}\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadJSONProtocolDouble(t *testing.T) {\n\tthetype := \"double\"\n\tfor _, value := range DOUBLE_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTJSONProtocol(trans)\n\t\tn := NewNumericFromDouble(value)\n\t\ttrans.WriteString(n.String())\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadDouble()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif math.IsInf(value, 1) {\n\t\t\tif !math.IsInf(v, 1) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t\t}\n\t\t} else if math.IsInf(value, -1) {\n\t\t\tif !math.IsInf(v, -1) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t\t}\n\t\t} else if math.IsNaN(value) {\n\t\t\tif !math.IsNaN(v) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t\t}\n\t\t} else {\n\t\t\tif v != value {\n\t\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t\t}\n\t\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t\t}\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestWriteJSONProtocolString(t *testing.T) {\n\tthetype := \"string\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\tfor _, value := range STRING_VALUES {\n\t\tif e := p.WriteString(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif s[0] != '\"' || s[len(s)-1] != '\"' {\n\t\t\tt.Fatalf(\"Bad value for %s '%v', wrote '%v', expected: %v\", thetype, value, s, fmt.Sprint(\"\\\"\", value, \"\\\"\"))\n\t\t}\n\t\tv := new(string)\n\t\tif err := json.Unmarshal([]byte(s), v); err != nil || *v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, *v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadJSONProtocolString(t *testing.T) {\n\tthetype := \"string\"\n\tfor _, value := range STRING_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTJSONProtocol(trans)\n\t\ttrans.WriteString(jsonQuote(value))\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadString()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tv1 := new(string)\n\t\tif err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, *v1)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestWriteJSONProtocolBinary(t *testing.T) {\n\tthetype := \"binary\"\n\tvalue := protocol_bdata\n\tb64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata)))\n\tbase64.StdEncoding.Encode(b64value, value)\n\tb64String := string(b64value)\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\tif e := p.WriteBinary(value); e != nil {\n\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t}\n\tif e := p.Flush(); e != nil {\n\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t}\n\ts := trans.String()\n\texpectedString := fmt.Sprint(\"\\\"\", b64String, \"\\\"\")\n\tif s != expectedString {\n\t\tt.Fatalf(\"Bad value for %s %v\\n  wrote:  \\\"%v\\\"\\nexpected: \\\"%v\\\"\", thetype, value, s, expectedString)\n\t}\n\tv1, err := p.ReadBinary()\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to read binary: %s\", err.Error())\n\t}\n\tif len(v1) != len(value) {\n\t\tt.Fatalf(\"Invalid value for binary\\nexpected: \\\"%v\\\"\\n   read: \\\"%v\\\"\", value, v1)\n\t}\n\tfor k, v := range value {\n\t\tif v1[k] != v {\n\t\t\tt.Fatalf(\"Invalid value for binary at %v\\nexpected: \\\"%v\\\"\\n   read: \\\"%v\\\"\", k, v, v1[k])\n\t\t}\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadJSONProtocolBinary(t *testing.T) {\n\tthetype := \"binary\"\n\tvalue := protocol_bdata\n\tb64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata)))\n\tbase64.StdEncoding.Encode(b64value, value)\n\tb64String := string(b64value)\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\ttrans.WriteString(jsonQuote(b64String))\n\ttrans.Flush()\n\ts := trans.String()\n\tv, e := p.ReadBinary()\n\tif e != nil {\n\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t}\n\tif len(v) != len(value) {\n\t\tt.Fatalf(\"Bad value for %s value length %v, wrote: %v, received length: %v\", thetype, len(value), s, len(v))\n\t}\n\tfor i := 0; i < len(v); i++ {\n\t\tif v[i] != value[i] {\n\t\t\tt.Fatalf(\"Bad value for %s at index %d value %v, wrote: %v, received: %v\", thetype, i, value[i], s, v[i])\n\t\t}\n\t}\n\tv1 := new(string)\n\tif err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String {\n\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, *v1)\n\t}\n\ttrans.Reset()\n\ttrans.Close()\n}\n\nfunc TestWriteJSONProtocolList(t *testing.T) {\n\tthetype := \"list\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\tp.WriteListBegin(TType(DOUBLE), len(DOUBLE_VALUES))\n\tfor _, value := range DOUBLE_VALUES {\n\t\tif e := p.WriteDouble(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t}\n\tp.WriteListEnd()\n\tif e := p.Flush(); e != nil {\n\t\tt.Fatalf(\"Unable to write %s due to error flushing: %s\", thetype, e.Error())\n\t}\n\tstr := trans.String()\n\tstr1 := new([]interface{})\n\terr := json.Unmarshal([]byte(str), str1)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to decode %s, wrote: %s\", thetype, str)\n\t}\n\tl := *str1\n\tif len(l) < 2 {\n\t\tt.Fatalf(\"List must be at least of length two to include metadata\")\n\t}\n\tif l[0] != \"dbl\" {\n\t\tt.Fatal(\"Invalid type for list, expected: \", STRING, \", but was: \", l[0])\n\t}\n\tif int(l[1].(float64)) != len(DOUBLE_VALUES) {\n\t\tt.Fatal(\"Invalid length for list, expected: \", len(DOUBLE_VALUES), \", but was: \", l[1])\n\t}\n\tfor k, value := range DOUBLE_VALUES {\n\t\ts := l[k+2]\n\t\tif math.IsInf(value, 1) {\n\t\t\tif s.(string) != JSON_INFINITY {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_INFINITY), str)\n\t\t\t}\n\t\t} else if math.IsInf(value, 0) {\n\t\t\tif s.(string) != JSON_NEGATIVE_INFINITY {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str)\n\t\t\t}\n\t\t} else if math.IsNaN(value) {\n\t\t\tif s.(string) != JSON_NAN {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v  %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_NAN), str)\n\t\t\t}\n\t\t} else {\n\t\t\tif s.(float64) != value {\n\t\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s'\", thetype, value, s)\n\t\t\t}\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestWriteJSONProtocolSet(t *testing.T) {\n\tthetype := \"set\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\tp.WriteSetBegin(TType(DOUBLE), len(DOUBLE_VALUES))\n\tfor _, value := range DOUBLE_VALUES {\n\t\tif e := p.WriteDouble(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t}\n\tp.WriteSetEnd()\n\tif e := p.Flush(); e != nil {\n\t\tt.Fatalf(\"Unable to write %s due to error flushing: %s\", thetype, e.Error())\n\t}\n\tstr := trans.String()\n\tstr1 := new([]interface{})\n\terr := json.Unmarshal([]byte(str), str1)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to decode %s, wrote: %s\", thetype, str)\n\t}\n\tl := *str1\n\tif len(l) < 2 {\n\t\tt.Fatalf(\"Set must be at least of length two to include metadata\")\n\t}\n\tif l[0] != \"dbl\" {\n\t\tt.Fatal(\"Invalid type for set, expected: \", DOUBLE, \", but was: \", l[0])\n\t}\n\tif int(l[1].(float64)) != len(DOUBLE_VALUES) {\n\t\tt.Fatal(\"Invalid length for set, expected: \", len(DOUBLE_VALUES), \", but was: \", l[1])\n\t}\n\tfor k, value := range DOUBLE_VALUES {\n\t\ts := l[k+2]\n\t\tif math.IsInf(value, 1) {\n\t\t\tif s.(string) != JSON_INFINITY {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_INFINITY), str)\n\t\t\t}\n\t\t} else if math.IsInf(value, 0) {\n\t\t\tif s.(string) != JSON_NEGATIVE_INFINITY {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str)\n\t\t\t}\n\t\t} else if math.IsNaN(value) {\n\t\t\tif s.(string) != JSON_NAN {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v  %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_NAN), str)\n\t\t\t}\n\t\t} else {\n\t\t\tif s.(float64) != value {\n\t\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s'\", thetype, value, s)\n\t\t\t}\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestWriteJSONProtocolMap(t *testing.T) {\n\tthetype := \"map\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTJSONProtocol(trans)\n\tp.WriteMapBegin(TType(I32), TType(DOUBLE), len(DOUBLE_VALUES))\n\tfor k, value := range DOUBLE_VALUES {\n\t\tif e := p.WriteI32(int32(k)); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s key int32 value %v due to error: %s\", thetype, k, e.Error())\n\t\t}\n\t\tif e := p.WriteDouble(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value float64 value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t}\n\tp.WriteMapEnd()\n\tif e := p.Flush(); e != nil {\n\t\tt.Fatalf(\"Unable to write %s due to error flushing: %s\", thetype, e.Error())\n\t}\n\tstr := trans.String()\n\tif str[0] != '[' || str[len(str)-1] != ']' {\n\t\tt.Fatalf(\"Bad value for %s, wrote: %q, in go: %q\", thetype, str, DOUBLE_VALUES)\n\t}\n\texpectedKeyType, expectedValueType, expectedSize, err := p.ReadMapBegin()\n\tif err != nil {\n\t\tt.Fatalf(\"Error while reading map begin: %s\", err.Error())\n\t}\n\tif expectedKeyType != I32 {\n\t\tt.Fatal(\"Expected map key type \", I32, \", but was \", expectedKeyType)\n\t}\n\tif expectedValueType != DOUBLE {\n\t\tt.Fatal(\"Expected map value type \", DOUBLE, \", but was \", expectedValueType)\n\t}\n\tif expectedSize != len(DOUBLE_VALUES) {\n\t\tt.Fatal(\"Expected map size of \", len(DOUBLE_VALUES), \", but was \", expectedSize)\n\t}\n\tfor k, value := range DOUBLE_VALUES {\n\t\tik, err := p.ReadI32()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Bad key for %s index %v, wrote: %v, expected: %v, error: %s\", thetype, k, ik, string(k), err.Error())\n\t\t}\n\t\tif int(ik) != k {\n\t\t\tt.Fatalf(\"Bad key for %s index %v, wrote: %v, expected: %v\", thetype, k, ik, k)\n\t\t}\n\t\tdv, err := p.ReadDouble()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Bad value for %s index %v, wrote: %v, expected: %v, error: %s\", thetype, k, dv, value, err.Error())\n\t\t}\n\t\ts := strconv.FormatFloat(dv, 'g', 10, 64)\n\t\tif math.IsInf(value, 1) {\n\t\t\tif !math.IsInf(dv, 1) {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %v, expected: %v\", thetype, k, value, s, jsonQuote(JSON_INFINITY))\n\t\t\t}\n\t\t} else if math.IsInf(value, 0) {\n\t\t\tif !math.IsInf(dv, 0) {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %v, expected: %v\", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY))\n\t\t\t}\n\t\t} else if math.IsNaN(value) {\n\t\t\tif !math.IsNaN(dv) {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v  %v, wrote: %v, expected: %v\", thetype, k, value, s, jsonQuote(JSON_NAN))\n\t\t\t}\n\t\t} else {\n\t\t\texpected := strconv.FormatFloat(value, 'g', 10, 64)\n\t\t\tif s != expected {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %v, expected %v\", thetype, k, value, s, expected)\n\t\t\t}\n\t\t\tv := float64(0)\n\t\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t\t}\n\t\t}\n\t}\n\terr = p.ReadMapEnd()\n\tif err != nil {\n\t\tt.Fatalf(\"Error while reading map end: %s\", err.Error())\n\t}\n\ttrans.Close()\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/lowlevel_benchmarks_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nvar binaryProtoF = NewTBinaryProtocolFactoryDefault()\nvar compactProtoF = NewTCompactProtocolFactory()\n\nvar buf = bytes.NewBuffer(make([]byte, 0, 1024))\n\nvar tfv = []TTransportFactory{\n\tNewTMemoryBufferTransportFactory(1024),\n\tNewStreamTransportFactory(buf, buf, true),\n\tNewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024)),\n}\n\nfunc BenchmarkBinaryBool_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBool(b, p, trans)\n\t}\n}\n\nfunc BenchmarkBinaryByte_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteByte(b, p, trans)\n\t}\n}\n\nfunc BenchmarkBinaryI16_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI16(b, p, trans)\n\t}\n}\n\nfunc BenchmarkBinaryI32_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI32(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryI64_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI64(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryDouble_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteDouble(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryString_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteString(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryBinary_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBinary(b, p, trans)\n\t}\n}\n\nfunc BenchmarkBinaryBool_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBool(b, p, trans)\n\t}\n}\n\nfunc BenchmarkBinaryByte_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteByte(b, p, trans)\n\t}\n}\n\nfunc BenchmarkBinaryI16_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI16(b, p, trans)\n\t}\n}\n\nfunc BenchmarkBinaryI32_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI32(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryI64_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI64(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryDouble_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteDouble(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryString_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteString(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryBinary_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBinary(b, p, trans)\n\t}\n}\n\nfunc BenchmarkBinaryBool_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBool(b, p, trans)\n\t}\n}\n\nfunc BenchmarkBinaryByte_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteByte(b, p, trans)\n\t}\n}\n\nfunc BenchmarkBinaryI16_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI16(b, p, trans)\n\t}\n}\n\nfunc BenchmarkBinaryI32_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI32(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryI64_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI64(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryDouble_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteDouble(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryString_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteString(b, p, trans)\n\t}\n}\nfunc BenchmarkBinaryBinary_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := binaryProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBinary(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactBool_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBool(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactByte_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteByte(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactI16_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI16(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactI32_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI32(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactI64_0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI64(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactDouble0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteDouble(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactString0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteString(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactBinary0(b *testing.B) {\n\ttrans := tfv[0].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBinary(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactBool_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBool(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactByte_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteByte(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactI16_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI16(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactI32_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI32(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactI64_1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI64(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactDouble1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteDouble(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactString1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteString(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactBinary1(b *testing.B) {\n\ttrans := tfv[1].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBinary(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactBool_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBool(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactByte_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteByte(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactI16_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI16(b, p, trans)\n\t}\n}\n\nfunc BenchmarkCompactI32_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI32(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactI64_2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteI64(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactDouble2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteDouble(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactString2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteString(b, p, trans)\n\t}\n}\nfunc BenchmarkCompactBinary2(b *testing.B) {\n\ttrans := tfv[2].GetTransport(nil)\n\tp := compactProtoF.GetProtocol(trans)\n\tfor i := 0; i < b.N; i++ {\n\t\tReadWriteBinary(b, p, trans)\n\t}\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/memory_buffer.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bytes\"\n)\n\n// Memory buffer-based implementation of the TTransport interface.\ntype TMemoryBuffer struct {\n\t*bytes.Buffer\n\tsize int\n}\n\ntype TMemoryBufferTransportFactory struct {\n\tsize int\n}\n\nfunc (p *TMemoryBufferTransportFactory) GetTransport(trans TTransport) TTransport {\n\tif trans != nil {\n\t\tt, ok := trans.(*TMemoryBuffer)\n\t\tif ok && t.size > 0 {\n\t\t\treturn NewTMemoryBufferLen(t.size)\n\t\t}\n\t}\n\treturn NewTMemoryBufferLen(p.size)\n}\n\nfunc NewTMemoryBufferTransportFactory(size int) *TMemoryBufferTransportFactory {\n\treturn &TMemoryBufferTransportFactory{size: size}\n}\n\nfunc NewTMemoryBuffer() *TMemoryBuffer {\n\treturn &TMemoryBuffer{Buffer: &bytes.Buffer{}, size: 0}\n}\n\nfunc NewTMemoryBufferLen(size int) *TMemoryBuffer {\n\tbuf := make([]byte, 0, size)\n\treturn &TMemoryBuffer{Buffer: bytes.NewBuffer(buf), size: size}\n}\n\nfunc (p *TMemoryBuffer) IsOpen() bool {\n\treturn true\n}\n\nfunc (p *TMemoryBuffer) Open() error {\n\treturn nil\n}\n\nfunc (p *TMemoryBuffer) Close() error {\n\tp.Buffer.Reset()\n\treturn nil\n}\n\n// Flushing a memory buffer is a no-op\nfunc (p *TMemoryBuffer) Flush() error {\n\treturn nil\n}\n\nfunc (p *TMemoryBuffer) RemainingBytes() (num_bytes uint64) {\n\treturn uint64(p.Buffer.Len())\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/memory_buffer_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"testing\"\n)\n\nfunc TestMemoryBuffer(t *testing.T) {\n\ttrans := NewTMemoryBufferLen(1024)\n\tTransportTest(t, trans, trans)\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/messagetype.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\n// Message type constants in the Thrift protocol.\ntype TMessageType int32\n\nconst (\n\tINVALID_TMESSAGE_TYPE TMessageType = 0\n\tCALL                  TMessageType = 1\n\tREPLY                 TMessageType = 2\n\tEXCEPTION             TMessageType = 3\n\tONEWAY                TMessageType = 4\n)\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n/*\nTMultiplexedProtocol is a protocol-independent concrete decorator\nthat allows a Thrift client to communicate with a multiplexing Thrift server,\nby prepending the service name to the function name during function calls.\n\nNOTE: THIS IS NOT USED BY SERVERS.  On the server, use TMultiplexedProcessor to handle request\nfrom a multiplexing client.\n\nThis example uses a single socket transport to invoke two services:\n\nsocket := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT)\ntransport := thrift.NewTFramedTransport(socket)\nprotocol := thrift.NewTBinaryProtocolTransport(transport)\n\nmp := thrift.NewTMultiplexedProtocol(protocol, \"Calculator\")\nservice := Calculator.NewCalculatorClient(mp)\n\nmp2 := thrift.NewTMultiplexedProtocol(protocol, \"WeatherReport\")\nservice2 := WeatherReport.NewWeatherReportClient(mp2)\n\nerr := transport.Open()\nif err != nil {\n\tt.Fatal(\"Unable to open client socket\", err)\n}\n\nfmt.Println(service.Add(2,2))\nfmt.Println(service2.GetTemperature())\n*/\n\ntype TMultiplexedProtocol struct {\n\tTProtocol\n\tserviceName string\n}\n\nconst MULTIPLEXED_SEPARATOR = \":\"\n\nfunc NewTMultiplexedProtocol(protocol TProtocol, serviceName string) *TMultiplexedProtocol {\n\treturn &TMultiplexedProtocol{\n\t\tTProtocol:   protocol,\n\t\tserviceName: serviceName,\n\t}\n}\n\nfunc (t *TMultiplexedProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error {\n\tif typeId == CALL || typeId == ONEWAY {\n\t\treturn t.TProtocol.WriteMessageBegin(t.serviceName+MULTIPLEXED_SEPARATOR+name, typeId, seqid)\n\t} else {\n\t\treturn t.TProtocol.WriteMessageBegin(name, typeId, seqid)\n\t}\n}\n\n/*\nTMultiplexedProcessor is a TProcessor allowing\na single TServer to provide multiple services.\n\nTo do so, you instantiate the processor and then register additional\nprocessors with it, as shown in the following example:\n\nvar processor = thrift.NewTMultiplexedProcessor()\n\nfirstProcessor :=\nprocessor.RegisterProcessor(\"FirstService\", firstProcessor)\n\nprocessor.registerProcessor(\n  \"Calculator\",\n  Calculator.NewCalculatorProcessor(&CalculatorHandler{}),\n)\n\nprocessor.registerProcessor(\n  \"WeatherReport\",\n  WeatherReport.NewWeatherReportProcessor(&WeatherReportHandler{}),\n)\n\nserverTransport, err := thrift.NewTServerSocketTimeout(addr, TIMEOUT)\nif err != nil {\n  t.Fatal(\"Unable to create server socket\", err)\n}\nserver := thrift.NewTSimpleServer2(processor, serverTransport)\nserver.Serve();\n*/\n\ntype TMultiplexedProcessor struct {\n\tserviceProcessorMap map[string]TProcessor\n\tDefaultProcessor    TProcessor\n}\n\nfunc NewTMultiplexedProcessor() *TMultiplexedProcessor {\n\treturn &TMultiplexedProcessor{\n\t\tserviceProcessorMap: make(map[string]TProcessor),\n\t}\n}\n\nfunc (t *TMultiplexedProcessor) RegisterDefault(processor TProcessor) {\n\tt.DefaultProcessor = processor\n}\n\nfunc (t *TMultiplexedProcessor) RegisterProcessor(name string, processor TProcessor) {\n\tif t.serviceProcessorMap == nil {\n\t\tt.serviceProcessorMap = make(map[string]TProcessor)\n\t}\n\tt.serviceProcessorMap[name] = processor\n}\n\nfunc (t *TMultiplexedProcessor) Process(in, out TProtocol) (bool, TException) {\n\tname, typeId, seqid, err := in.ReadMessageBegin()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif typeId != CALL && typeId != ONEWAY {\n\t\treturn false, fmt.Errorf(\"Unexpected message type %v\", typeId)\n\t}\n\t//extract the service name\n\tv := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2)\n\tif len(v) != 2 {\n\t\tif t.DefaultProcessor != nil {\n\t\t\tsmb := NewStoredMessageProtocol(in, name, typeId, seqid)\n\t\t\treturn t.DefaultProcessor.Process(smb, out)\n\t\t}\n\t\treturn false, fmt.Errorf(\"Service name not found in message name: %s.  Did you forget to use a TMultiplexProtocol in your client?\", name)\n\t}\n\tactualProcessor, ok := t.serviceProcessorMap[v[0]]\n\tif !ok {\n\t\treturn false, fmt.Errorf(\"Service name not found: %s.  Did you forget to call registerProcessor()?\", v[0])\n\t}\n\tsmb := NewStoredMessageProtocol(in, v[1], typeId, seqid)\n\treturn actualProcessor.Process(smb, out)\n}\n\n//Protocol that use stored message for ReadMessageBegin\ntype storedMessageProtocol struct {\n\tTProtocol\n\tname   string\n\ttypeId TMessageType\n\tseqid  int32\n}\n\nfunc NewStoredMessageProtocol(protocol TProtocol, name string, typeId TMessageType, seqid int32) *storedMessageProtocol {\n\treturn &storedMessageProtocol{protocol, name, typeId, seqid}\n}\n\nfunc (s *storedMessageProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) {\n\treturn s.name, s.typeId, s.seqid, nil\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/numeric.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"math\"\n\t\"strconv\"\n)\n\ntype Numeric interface {\n\tInt64() int64\n\tInt32() int32\n\tInt16() int16\n\tByte() byte\n\tInt() int\n\tFloat64() float64\n\tFloat32() float32\n\tString() string\n\tisNull() bool\n}\n\ntype numeric struct {\n\tiValue int64\n\tdValue float64\n\tsValue string\n\tisNil  bool\n}\n\nvar (\n\tINFINITY          Numeric\n\tNEGATIVE_INFINITY Numeric\n\tNAN               Numeric\n\tZERO              Numeric\n\tNUMERIC_NULL      Numeric\n)\n\nfunc NewNumericFromDouble(dValue float64) Numeric {\n\tif math.IsInf(dValue, 1) {\n\t\treturn INFINITY\n\t}\n\tif math.IsInf(dValue, -1) {\n\t\treturn NEGATIVE_INFINITY\n\t}\n\tif math.IsNaN(dValue) {\n\t\treturn NAN\n\t}\n\tiValue := int64(dValue)\n\tsValue := strconv.FormatFloat(dValue, 'g', 10, 64)\n\tisNil := false\n\treturn &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil}\n}\n\nfunc NewNumericFromI64(iValue int64) Numeric {\n\tdValue := float64(iValue)\n\tsValue := string(iValue)\n\tisNil := false\n\treturn &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil}\n}\n\nfunc NewNumericFromI32(iValue int32) Numeric {\n\tdValue := float64(iValue)\n\tsValue := string(iValue)\n\tisNil := false\n\treturn &numeric{iValue: int64(iValue), dValue: dValue, sValue: sValue, isNil: isNil}\n}\n\nfunc NewNumericFromString(sValue string) Numeric {\n\tif sValue == INFINITY.String() {\n\t\treturn INFINITY\n\t}\n\tif sValue == NEGATIVE_INFINITY.String() {\n\t\treturn NEGATIVE_INFINITY\n\t}\n\tif sValue == NAN.String() {\n\t\treturn NAN\n\t}\n\tiValue, _ := strconv.ParseInt(sValue, 10, 64)\n\tdValue, _ := strconv.ParseFloat(sValue, 64)\n\tisNil := len(sValue) == 0\n\treturn &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil}\n}\n\nfunc NewNumericFromJSONString(sValue string, isNull bool) Numeric {\n\tif isNull {\n\t\treturn NewNullNumeric()\n\t}\n\tif sValue == JSON_INFINITY {\n\t\treturn INFINITY\n\t}\n\tif sValue == JSON_NEGATIVE_INFINITY {\n\t\treturn NEGATIVE_INFINITY\n\t}\n\tif sValue == JSON_NAN {\n\t\treturn NAN\n\t}\n\tiValue, _ := strconv.ParseInt(sValue, 10, 64)\n\tdValue, _ := strconv.ParseFloat(sValue, 64)\n\treturn &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNull}\n}\n\nfunc NewNullNumeric() Numeric {\n\treturn &numeric{iValue: 0, dValue: 0.0, sValue: \"\", isNil: true}\n}\n\nfunc (p *numeric) Int64() int64 {\n\treturn p.iValue\n}\n\nfunc (p *numeric) Int32() int32 {\n\treturn int32(p.iValue)\n}\n\nfunc (p *numeric) Int16() int16 {\n\treturn int16(p.iValue)\n}\n\nfunc (p *numeric) Byte() byte {\n\treturn byte(p.iValue)\n}\n\nfunc (p *numeric) Int() int {\n\treturn int(p.iValue)\n}\n\nfunc (p *numeric) Float64() float64 {\n\treturn p.dValue\n}\n\nfunc (p *numeric) Float32() float32 {\n\treturn float32(p.dValue)\n}\n\nfunc (p *numeric) String() string {\n\treturn p.sValue\n}\n\nfunc (p *numeric) isNull() bool {\n\treturn p.isNil\n}\n\nfunc init() {\n\tINFINITY = &numeric{iValue: 0, dValue: math.Inf(1), sValue: \"Infinity\", isNil: false}\n\tNEGATIVE_INFINITY = &numeric{iValue: 0, dValue: math.Inf(-1), sValue: \"-Infinity\", isNil: false}\n\tNAN = &numeric{iValue: 0, dValue: math.NaN(), sValue: \"NaN\", isNil: false}\n\tZERO = &numeric{iValue: 0, dValue: 0, sValue: \"0\", isNil: false}\n\tNUMERIC_NULL = &numeric{iValue: 0, dValue: 0, sValue: \"0\", isNil: true}\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/pointerize.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\n///////////////////////////////////////////////////////////////////////////////\n// This file is home to helpers that convert from various base types to\n// respective pointer types. This is necessary because Go does not permit\n// references to constants, nor can a pointer type to base type be allocated\n// and initialized in a single expression.\n//\n// E.g., this is not allowed:\n//\n//    var ip *int = &5\n//\n// But this *is* allowed:\n//\n//    func IntPtr(i int) *int { return &i }\n//    var ip *int = IntPtr(5)\n//\n// Since pointers to base types are commonplace as [optional] fields in\n// exported thrift structs, we factor such helpers here.\n///////////////////////////////////////////////////////////////////////////////\n\nfunc Float32Ptr(v float32) *float32 { return &v }\nfunc Float64Ptr(v float64) *float64 { return &v }\nfunc IntPtr(v int) *int             { return &v }\nfunc Int32Ptr(v int32) *int32       { return &v }\nfunc Int64Ptr(v int64) *int64       { return &v }\nfunc StringPtr(v string) *string    { return &v }\nfunc Uint32Ptr(v uint32) *uint32    { return &v }\nfunc Uint64Ptr(v uint64) *uint64    { return &v }\nfunc BoolPtr(v bool) *bool          { return &v }\nfunc ByteSlicePtr(v []byte) *[]byte { return &v }\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/processor.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\n// A processor is a generic object which operates upon an input stream and\n// writes to some output stream.\ntype TProcessor interface {\n\tProcess(in, out TProtocol) (bool, TException)\n}\n\ntype TProcessorFunction interface {\n\tProcess(seqId int32, in, out TProtocol) (bool, TException)\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/processor_factory.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\n// The default processor factory just returns a singleton\n// instance.\ntype TProcessorFactory interface {\n\tGetProcessor(trans TTransport) TProcessor\n}\n\ntype tProcessorFactory struct {\n\tprocessor TProcessor\n}\n\nfunc NewTProcessorFactory(p TProcessor) TProcessorFactory {\n\treturn &tProcessorFactory{processor: p}\n}\n\nfunc (p *tProcessorFactory) GetProcessor(trans TTransport) TProcessor {\n\treturn p.processor\n}\n\n/**\n * The default processor factory just returns a singleton\n * instance.\n */\ntype TProcessorFunctionFactory interface {\n\tGetProcessorFunction(trans TTransport) TProcessorFunction\n}\n\ntype tProcessorFunctionFactory struct {\n\tprocessor TProcessorFunction\n}\n\nfunc NewTProcessorFunctionFactory(p TProcessorFunction) TProcessorFunctionFactory {\n\treturn &tProcessorFunctionFactory{processor: p}\n}\n\nfunc (p *tProcessorFunctionFactory) GetProcessorFunction(trans TTransport) TProcessorFunction {\n\treturn p.processor\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/protocol.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nconst (\n\tVERSION_MASK = 0xffff0000\n\tVERSION_1    = 0x80010000\n)\n\ntype TProtocol interface {\n\tWriteMessageBegin(name string, typeId TMessageType, seqid int32) error\n\tWriteMessageEnd() error\n\tWriteStructBegin(name string) error\n\tWriteStructEnd() error\n\tWriteFieldBegin(name string, typeId TType, id int16) error\n\tWriteFieldEnd() error\n\tWriteFieldStop() error\n\tWriteMapBegin(keyType TType, valueType TType, size int) error\n\tWriteMapEnd() error\n\tWriteListBegin(elemType TType, size int) error\n\tWriteListEnd() error\n\tWriteSetBegin(elemType TType, size int) error\n\tWriteSetEnd() error\n\tWriteBool(value bool) error\n\tWriteByte(value int8) error\n\tWriteI16(value int16) error\n\tWriteI32(value int32) error\n\tWriteI64(value int64) error\n\tWriteDouble(value float64) error\n\tWriteString(value string) error\n\tWriteBinary(value []byte) error\n\n\tReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error)\n\tReadMessageEnd() error\n\tReadStructBegin() (name string, err error)\n\tReadStructEnd() error\n\tReadFieldBegin() (name string, typeId TType, id int16, err error)\n\tReadFieldEnd() error\n\tReadMapBegin() (keyType TType, valueType TType, size int, err error)\n\tReadMapEnd() error\n\tReadListBegin() (elemType TType, size int, err error)\n\tReadListEnd() error\n\tReadSetBegin() (elemType TType, size int, err error)\n\tReadSetEnd() error\n\tReadBool() (value bool, err error)\n\tReadByte() (value int8, err error)\n\tReadI16() (value int16, err error)\n\tReadI32() (value int32, err error)\n\tReadI64() (value int64, err error)\n\tReadDouble() (value float64, err error)\n\tReadString() (value string, err error)\n\tReadBinary() (value []byte, err error)\n\n\tSkip(fieldType TType) (err error)\n\tFlush() (err error)\n\n\tTransport() TTransport\n}\n\n// The maximum recursive depth the skip() function will traverse\nconst DEFAULT_RECURSION_DEPTH = 64\n\n// Skips over the next data element from the provided input TProtocol object.\nfunc SkipDefaultDepth(prot TProtocol, typeId TType) (err error) {\n\treturn Skip(prot, typeId, DEFAULT_RECURSION_DEPTH)\n}\n\n// Skips over the next data element from the provided input TProtocol object.\nfunc Skip(self TProtocol, fieldType TType, maxDepth int) (err error) {\n\n    if maxDepth <= 0 {\n\t\treturn NewTProtocolExceptionWithType( DEPTH_LIMIT, errors.New(\"Depth limit exceeded\"))\n\t}\n\n\tswitch fieldType {\n\tcase STOP:\n\t\treturn\n\tcase BOOL:\n\t\t_, err = self.ReadBool()\n\t\treturn\n\tcase BYTE:\n\t\t_, err = self.ReadByte()\n\t\treturn\n\tcase I16:\n\t\t_, err = self.ReadI16()\n\t\treturn\n\tcase I32:\n\t\t_, err = self.ReadI32()\n\t\treturn\n\tcase I64:\n\t\t_, err = self.ReadI64()\n\t\treturn\n\tcase DOUBLE:\n\t\t_, err = self.ReadDouble()\n\t\treturn\n\tcase STRING:\n\t\t_, err = self.ReadString()\n\t\treturn\n\tcase STRUCT:\n\t\tif _, err = self.ReadStructBegin(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor {\n\t\t\t_, typeId, _, _ := self.ReadFieldBegin()\n\t\t\tif typeId == STOP {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\terr := Skip(self, typeId, maxDepth-1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tself.ReadFieldEnd()\n\t\t}\n\t\treturn self.ReadStructEnd()\n\tcase MAP:\n\t\tkeyType, valueType, size, err := self.ReadMapBegin()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor i := 0; i < size; i++ {\n\t\t\terr := Skip(self, keyType, maxDepth-1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\terr = Skip(self, valueType, maxDepth-1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn self.ReadMapEnd()\n\tcase SET:\n\t\telemType, size, err := self.ReadSetBegin()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor i := 0; i < size; i++ {\n\t\t\terr := Skip(self, elemType, maxDepth-1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn self.ReadSetEnd()\n\tcase LIST:\n\t\telemType, size, err := self.ReadListBegin()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor i := 0; i < size; i++ {\n\t\t\terr := Skip(self, elemType, maxDepth-1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn self.ReadListEnd()\n\tdefault:\n\t\treturn NewTProtocolExceptionWithType(INVALID_DATA, fmt.Errorf(\"Unknown data type %d\", fieldType))\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/protocol_exception.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"encoding/base64\"\n)\n\n// Thrift Protocol exception\ntype TProtocolException interface {\n\tTException\n\tTypeId() int\n}\n\nconst (\n\tUNKNOWN_PROTOCOL_EXCEPTION = 0\n\tINVALID_DATA               = 1\n\tNEGATIVE_SIZE              = 2\n\tSIZE_LIMIT                 = 3\n\tBAD_VERSION                = 4\n\tNOT_IMPLEMENTED            = 5\n\tDEPTH_LIMIT                = 6\n)\n\ntype tProtocolException struct {\n\ttypeId  int\n\tmessage string\n}\n\nfunc (p *tProtocolException) TypeId() int {\n\treturn p.typeId\n}\n\nfunc (p *tProtocolException) String() string {\n\treturn p.message\n}\n\nfunc (p *tProtocolException) Error() string {\n\treturn p.message\n}\n\nfunc NewTProtocolException(err error) TProtocolException {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif e,ok := err.(TProtocolException); ok {\n\t\treturn e\n\t}\n\tif _, ok := err.(base64.CorruptInputError); ok {\n\t\treturn &tProtocolException{INVALID_DATA, err.Error()}\n\t}\n\treturn &tProtocolException{UNKNOWN_PROTOCOL_EXCEPTION, err.Error()}\n}\n\nfunc NewTProtocolExceptionWithType(errType int, err error) TProtocolException {\n\tif err == nil {\n\t\treturn nil\n\t}\n\treturn &tProtocolException{errType, err.Error()}\n}\n\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/protocol_factory.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\n// Factory interface for constructing protocol instances.\ntype TProtocolFactory interface {\n\tGetProtocol(trans TTransport) TProtocol\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/protocol_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bytes\"\n\t\"io/ioutil\"\n\t\"math\"\n\t\"net\"\n\t\"net/http\"\n\t\"testing\"\n)\n\nconst PROTOCOL_BINARY_DATA_SIZE = 155\n\nvar (\n\tdata           string // test data for writing\n\tprotocol_bdata []byte // test data for writing; same as data\n\tBOOL_VALUES    []bool\n\tBYTE_VALUES    []int8\n\tINT16_VALUES   []int16\n\tINT32_VALUES   []int32\n\tINT64_VALUES   []int64\n\tDOUBLE_VALUES  []float64\n\tSTRING_VALUES  []string\n)\n\nfunc init() {\n\tprotocol_bdata = make([]byte, PROTOCOL_BINARY_DATA_SIZE)\n\tfor i := 0; i < PROTOCOL_BINARY_DATA_SIZE; i++ {\n\t\tprotocol_bdata[i] = byte((i + 'a') % 255)\n\t}\n\tdata = string(protocol_bdata)\n\tBOOL_VALUES = []bool{false, true, false, false, true}\n\tBYTE_VALUES = []int8{117, 0, 1, 32, 127, -128, -1}\n\tINT16_VALUES = []int16{459, 0, 1, -1, -128, 127, 32767, -32768}\n\tINT32_VALUES = []int32{459, 0, 1, -1, -128, 127, 32767, 2147483647, -2147483535}\n\tINT64_VALUES = []int64{459, 0, 1, -1, -128, 127, 32767, 2147483647, -2147483535, 34359738481, -35184372088719, -9223372036854775808, 9223372036854775807}\n\tDOUBLE_VALUES = []float64{459.3, 0.0, -1.0, 1.0, 0.5, 0.3333, 3.14159, 1.537e-38, 1.673e25, 6.02214179e23, -6.02214179e23, INFINITY.Float64(), NEGATIVE_INFINITY.Float64(), NAN.Float64()}\n\tSTRING_VALUES = []string{\"\", \"a\", \"st[uf]f\", \"st,u:ff with spaces\", \"stuff\\twith\\nescape\\\\characters'...\\\"lots{of}fun</xml>\"}\n}\n\ntype HTTPEchoServer struct{}\ntype HTTPHeaderEchoServer struct{}\n\nfunc (p *HTTPEchoServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n\tbuf, err := ioutil.ReadAll(req.Body)\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tw.Write(buf)\n\t} else {\n\t\tw.WriteHeader(http.StatusOK)\n\t\tw.Write(buf)\n\t}\n}\n\nfunc (p *HTTPHeaderEchoServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n\tbuf, err := ioutil.ReadAll(req.Body)\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tw.Write(buf)\n\t} else {\n\t\tw.WriteHeader(http.StatusOK)\n\t\tw.Write(buf)\n\t}\n}\n\nfunc HttpClientSetupForTest(t *testing.T) (net.Listener, net.Addr) {\n\taddr, err := FindAvailableTCPServerPort(40000)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to find available tcp port addr: %s\", err)\n\t\treturn nil, addr\n\t}\n\tl, err := net.Listen(addr.Network(), addr.String())\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to setup tcp listener on %s: %s\", addr.String(), err)\n\t\treturn l, addr\n\t}\n\tgo http.Serve(l, &HTTPEchoServer{})\n\treturn l, addr\n}\n\nfunc HttpClientSetupForHeaderTest(t *testing.T) (net.Listener, net.Addr) {\n\taddr, err := FindAvailableTCPServerPort(40000)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to find available tcp port addr: %s\", err)\n\t\treturn nil, addr\n\t}\n\tl, err := net.Listen(addr.Network(), addr.String())\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to setup tcp listener on %s: %s\", addr.String(), err)\n\t\treturn l, addr\n\t}\n\tgo http.Serve(l, &HTTPHeaderEchoServer{})\n\treturn l, addr\n}\n\nfunc ReadWriteProtocolTest(t *testing.T, protocolFactory TProtocolFactory) {\n\tbuf := bytes.NewBuffer(make([]byte, 0, 1024))\n\tl, addr := HttpClientSetupForTest(t)\n\tdefer l.Close()\n\ttransports := []TTransportFactory{\n\t\tNewTMemoryBufferTransportFactory(1024),\n\t\tNewStreamTransportFactory(buf, buf, true),\n\t\tNewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024)),\n\t\tNewTHttpPostClientTransportFactory(\"http://\" + addr.String()),\n\t}\n\tfor _, tf := range transports {\n\t\ttrans := tf.GetTransport(nil)\n\t\tp := protocolFactory.GetProtocol(trans)\n\t\tReadWriteBool(t, p, trans)\n\t\ttrans.Close()\n\t}\n\tfor _, tf := range transports {\n\t\ttrans := tf.GetTransport(nil)\n\t\tp := protocolFactory.GetProtocol(trans)\n\t\tReadWriteByte(t, p, trans)\n\t\ttrans.Close()\n\t}\n\tfor _, tf := range transports {\n\t\ttrans := tf.GetTransport(nil)\n\t\tp := protocolFactory.GetProtocol(trans)\n\t\tReadWriteI16(t, p, trans)\n\t\ttrans.Close()\n\t}\n\tfor _, tf := range transports {\n\t\ttrans := tf.GetTransport(nil)\n\t\tp := protocolFactory.GetProtocol(trans)\n\t\tReadWriteI32(t, p, trans)\n\t\ttrans.Close()\n\t}\n\tfor _, tf := range transports {\n\t\ttrans := tf.GetTransport(nil)\n\t\tp := protocolFactory.GetProtocol(trans)\n\t\tReadWriteI64(t, p, trans)\n\t\ttrans.Close()\n\t}\n\tfor _, tf := range transports {\n\t\ttrans := tf.GetTransport(nil)\n\t\tp := protocolFactory.GetProtocol(trans)\n\t\tReadWriteDouble(t, p, trans)\n\t\ttrans.Close()\n\t}\n\tfor _, tf := range transports {\n\t\ttrans := tf.GetTransport(nil)\n\t\tp := protocolFactory.GetProtocol(trans)\n\t\tReadWriteString(t, p, trans)\n\t\ttrans.Close()\n\t}\n\tfor _, tf := range transports {\n\t\ttrans := tf.GetTransport(nil)\n\t\tp := protocolFactory.GetProtocol(trans)\n\t\tReadWriteBinary(t, p, trans)\n\t\ttrans.Close()\n\t}\n\tfor _, tf := range transports {\n\t\ttrans := tf.GetTransport(nil)\n\t\tp := protocolFactory.GetProtocol(trans)\n\t\tReadWriteI64(t, p, trans)\n\t\tReadWriteDouble(t, p, trans)\n\t\tReadWriteBinary(t, p, trans)\n\t\tReadWriteByte(t, p, trans)\n\t\ttrans.Close()\n\t}\n}\n\nfunc ReadWriteBool(t testing.TB, p TProtocol, trans TTransport) {\n\tthetype := TType(BOOL)\n\tthelen := len(BOOL_VALUES)\n\terr := p.WriteListBegin(thetype, thelen)\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error writing list begin: %q\", \"ReadWriteBool\", p, trans, err, thetype)\n\t}\n\tfor k, v := range BOOL_VALUES {\n\t\terr = p.WriteBool(v)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%s: %T %T %q Error writing bool in list at index %d: %q\", \"ReadWriteBool\", p, trans, err, k, v)\n\t\t}\n\t}\n\tp.WriteListEnd()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error writing list end: %q\", \"ReadWriteBool\", p, trans, err, BOOL_VALUES)\n\t}\n\tp.Flush()\n\tthetype2, thelen2, err := p.ReadListBegin()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error reading list: %q\", \"ReadWriteBool\", p, trans, err, BOOL_VALUES)\n\t}\n\t_, ok := p.(*TSimpleJSONProtocol)\n\tif !ok {\n\t\tif thetype != thetype2 {\n\t\t\tt.Errorf(\"%s: %T %T type %s != type %s\", \"ReadWriteBool\", p, trans, thetype, thetype2)\n\t\t}\n\t\tif thelen != thelen2 {\n\t\t\tt.Errorf(\"%s: %T %T len %s != len %s\", \"ReadWriteBool\", p, trans, thelen, thelen2)\n\t\t}\n\t}\n\tfor k, v := range BOOL_VALUES {\n\t\tvalue, err := p.ReadBool()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%s: %T %T %q Error reading bool at index %d: %q\", \"ReadWriteBool\", p, trans, err, k, v)\n\t\t}\n\t\tif v != value {\n\t\t\tt.Errorf(\"%s: index %d %q %q %q != %q\", \"ReadWriteBool\", k, p, trans, v, value)\n\t\t}\n\t}\n\terr = p.ReadListEnd()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T Unable to read list end: %q\", \"ReadWriteBool\", p, trans, err)\n\t}\n}\n\nfunc ReadWriteByte(t testing.TB, p TProtocol, trans TTransport) {\n\tthetype := TType(BYTE)\n\tthelen := len(BYTE_VALUES)\n\terr := p.WriteListBegin(thetype, thelen)\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error writing list begin: %q\", \"ReadWriteByte\", p, trans, err, thetype)\n\t}\n\tfor k, v := range BYTE_VALUES {\n\t\terr = p.WriteByte(v)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%s: %T %T %q Error writing byte in list at index %d: %q\", \"ReadWriteByte\", p, trans, err, k, v)\n\t\t}\n\t}\n\terr = p.WriteListEnd()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error writing list end: %q\", \"ReadWriteByte\", p, trans, err, BYTE_VALUES)\n\t}\n\terr = p.Flush()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error flushing list of bytes: %q\", \"ReadWriteByte\", p, trans, err, BYTE_VALUES)\n\t}\n\tthetype2, thelen2, err := p.ReadListBegin()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error reading list: %q\", \"ReadWriteByte\", p, trans, err, BYTE_VALUES)\n\t}\n\t_, ok := p.(*TSimpleJSONProtocol)\n\tif !ok {\n\t\tif thetype != thetype2 {\n\t\t\tt.Errorf(\"%s: %T %T type %s != type %s\", \"ReadWriteByte\", p, trans, thetype, thetype2)\n\t\t}\n\t\tif thelen != thelen2 {\n\t\t\tt.Errorf(\"%s: %T %T len %s != len %s\", \"ReadWriteByte\", p, trans, thelen, thelen2)\n\t\t}\n\t}\n\tfor k, v := range BYTE_VALUES {\n\t\tvalue, err := p.ReadByte()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%s: %T %T %q Error reading byte at index %d: %q\", \"ReadWriteByte\", p, trans, err, k, v)\n\t\t}\n\t\tif v != value {\n\t\t\tt.Errorf(\"%s: %T %T %d != %d\", \"ReadWriteByte\", p, trans, v, value)\n\t\t}\n\t}\n\terr = p.ReadListEnd()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T Unable to read list end: %q\", \"ReadWriteByte\", p, trans, err)\n\t}\n}\n\nfunc ReadWriteI16(t testing.TB, p TProtocol, trans TTransport) {\n\tthetype := TType(I16)\n\tthelen := len(INT16_VALUES)\n\tp.WriteListBegin(thetype, thelen)\n\tfor _, v := range INT16_VALUES {\n\t\tp.WriteI16(v)\n\t}\n\tp.WriteListEnd()\n\tp.Flush()\n\tthetype2, thelen2, err := p.ReadListBegin()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error reading list: %q\", \"ReadWriteI16\", p, trans, err, INT16_VALUES)\n\t}\n\t_, ok := p.(*TSimpleJSONProtocol)\n\tif !ok {\n\t\tif thetype != thetype2 {\n\t\t\tt.Errorf(\"%s: %T %T type %s != type %s\", \"ReadWriteI16\", p, trans, thetype, thetype2)\n\t\t}\n\t\tif thelen != thelen2 {\n\t\t\tt.Errorf(\"%s: %T %T len %s != len %s\", \"ReadWriteI16\", p, trans, thelen, thelen2)\n\t\t}\n\t}\n\tfor k, v := range INT16_VALUES {\n\t\tvalue, err := p.ReadI16()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%s: %T %T %q Error reading int16 at index %d: %q\", \"ReadWriteI16\", p, trans, err, k, v)\n\t\t}\n\t\tif v != value {\n\t\t\tt.Errorf(\"%s: %T %T %d != %d\", \"ReadWriteI16\", p, trans, v, value)\n\t\t}\n\t}\n\terr = p.ReadListEnd()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T Unable to read list end: %q\", \"ReadWriteI16\", p, trans, err)\n\t}\n}\n\nfunc ReadWriteI32(t testing.TB, p TProtocol, trans TTransport) {\n\tthetype := TType(I32)\n\tthelen := len(INT32_VALUES)\n\tp.WriteListBegin(thetype, thelen)\n\tfor _, v := range INT32_VALUES {\n\t\tp.WriteI32(v)\n\t}\n\tp.WriteListEnd()\n\tp.Flush()\n\tthetype2, thelen2, err := p.ReadListBegin()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error reading list: %q\", \"ReadWriteI32\", p, trans, err, INT32_VALUES)\n\t}\n\t_, ok := p.(*TSimpleJSONProtocol)\n\tif !ok {\n\t\tif thetype != thetype2 {\n\t\t\tt.Errorf(\"%s: %T %T type %s != type %s\", \"ReadWriteI32\", p, trans, thetype, thetype2)\n\t\t}\n\t\tif thelen != thelen2 {\n\t\t\tt.Errorf(\"%s: %T %T len %s != len %s\", \"ReadWriteI32\", p, trans, thelen, thelen2)\n\t\t}\n\t}\n\tfor k, v := range INT32_VALUES {\n\t\tvalue, err := p.ReadI32()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%s: %T %T %q Error reading int32 at index %d: %q\", \"ReadWriteI32\", p, trans, err, k, v)\n\t\t}\n\t\tif v != value {\n\t\t\tt.Errorf(\"%s: %T %T %d != %d\", \"ReadWriteI32\", p, trans, v, value)\n\t\t}\n\t}\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T Unable to read list end: %q\", \"ReadWriteI32\", p, trans, err)\n\t}\n}\n\nfunc ReadWriteI64(t testing.TB, p TProtocol, trans TTransport) {\n\tthetype := TType(I64)\n\tthelen := len(INT64_VALUES)\n\tp.WriteListBegin(thetype, thelen)\n\tfor _, v := range INT64_VALUES {\n\t\tp.WriteI64(v)\n\t}\n\tp.WriteListEnd()\n\tp.Flush()\n\tthetype2, thelen2, err := p.ReadListBegin()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error reading list: %q\", \"ReadWriteI64\", p, trans, err, INT64_VALUES)\n\t}\n\t_, ok := p.(*TSimpleJSONProtocol)\n\tif !ok {\n\t\tif thetype != thetype2 {\n\t\t\tt.Errorf(\"%s: %T %T type %s != type %s\", \"ReadWriteI64\", p, trans, thetype, thetype2)\n\t\t}\n\t\tif thelen != thelen2 {\n\t\t\tt.Errorf(\"%s: %T %T len %s != len %s\", \"ReadWriteI64\", p, trans, thelen, thelen2)\n\t\t}\n\t}\n\tfor k, v := range INT64_VALUES {\n\t\tvalue, err := p.ReadI64()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%s: %T %T %q Error reading int64 at index %d: %q\", \"ReadWriteI64\", p, trans, err, k, v)\n\t\t}\n\t\tif v != value {\n\t\t\tt.Errorf(\"%s: %T %T %q != %q\", \"ReadWriteI64\", p, trans, v, value)\n\t\t}\n\t}\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T Unable to read list end: %q\", \"ReadWriteI64\", p, trans, err)\n\t}\n}\n\nfunc ReadWriteDouble(t testing.TB, p TProtocol, trans TTransport) {\n\tthetype := TType(DOUBLE)\n\tthelen := len(DOUBLE_VALUES)\n\tp.WriteListBegin(thetype, thelen)\n\tfor _, v := range DOUBLE_VALUES {\n\t\tp.WriteDouble(v)\n\t}\n\tp.WriteListEnd()\n\tp.Flush()\n\tthetype2, thelen2, err := p.ReadListBegin()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error reading list: %q\", \"ReadWriteDouble\", p, trans, err, DOUBLE_VALUES)\n\t}\n\tif thetype != thetype2 {\n\t\tt.Errorf(\"%s: %T %T type %s != type %s\", \"ReadWriteDouble\", p, trans, thetype, thetype2)\n\t}\n\tif thelen != thelen2 {\n\t\tt.Errorf(\"%s: %T %T len %s != len %s\", \"ReadWriteDouble\", p, trans, thelen, thelen2)\n\t}\n\tfor k, v := range DOUBLE_VALUES {\n\t\tvalue, err := p.ReadDouble()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%s: %T %T %q Error reading double at index %d: %q\", \"ReadWriteDouble\", p, trans, err, k, v)\n\t\t}\n\t\tif math.IsNaN(v) {\n\t\t\tif !math.IsNaN(value) {\n\t\t\t\tt.Errorf(\"%s: %T %T math.IsNaN(%q) != math.IsNaN(%q)\", \"ReadWriteDouble\", p, trans, v, value)\n\t\t\t}\n\t\t} else if v != value {\n\t\t\tt.Errorf(\"%s: %T %T %v != %q\", \"ReadWriteDouble\", p, trans, v, value)\n\t\t}\n\t}\n\terr = p.ReadListEnd()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T Unable to read list end: %q\", \"ReadWriteDouble\", p, trans, err)\n\t}\n}\n\nfunc ReadWriteString(t testing.TB, p TProtocol, trans TTransport) {\n\tthetype := TType(STRING)\n\tthelen := len(STRING_VALUES)\n\tp.WriteListBegin(thetype, thelen)\n\tfor _, v := range STRING_VALUES {\n\t\tp.WriteString(v)\n\t}\n\tp.WriteListEnd()\n\tp.Flush()\n\tthetype2, thelen2, err := p.ReadListBegin()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T %q Error reading list: %q\", \"ReadWriteString\", p, trans, err, STRING_VALUES)\n\t}\n\t_, ok := p.(*TSimpleJSONProtocol)\n\tif !ok {\n\t\tif thetype != thetype2 {\n\t\t\tt.Errorf(\"%s: %T %T type %s != type %s\", \"ReadWriteString\", p, trans, thetype, thetype2)\n\t\t}\n\t\tif thelen != thelen2 {\n\t\t\tt.Errorf(\"%s: %T %T len %s != len %s\", \"ReadWriteString\", p, trans, thelen, thelen2)\n\t\t}\n\t}\n\tfor k, v := range STRING_VALUES {\n\t\tvalue, err := p.ReadString()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%s: %T %T %q Error reading string at index %d: %q\", \"ReadWriteString\", p, trans, err, k, v)\n\t\t}\n\t\tif v != value {\n\t\t\tt.Errorf(\"%s: %T %T %d != %d\", \"ReadWriteString\", p, trans, v, value)\n\t\t}\n\t}\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T Unable to read list end: %q\", \"ReadWriteString\", p, trans, err)\n\t}\n}\n\nfunc ReadWriteBinary(t testing.TB, p TProtocol, trans TTransport) {\n\tv := protocol_bdata\n\tp.WriteBinary(v)\n\tp.Flush()\n\tvalue, err := p.ReadBinary()\n\tif err != nil {\n\t\tt.Errorf(\"%s: %T %T Unable to read binary: %s\", \"ReadWriteBinary\", p, trans, err.Error())\n\t}\n\tif len(v) != len(value) {\n\t\tt.Errorf(\"%s: %T %T len(v) != len(value)... %d != %d\", \"ReadWriteBinary\", p, trans, len(v), len(value))\n\t} else {\n\t\tfor i := 0; i < len(v); i++ {\n\t\t\tif v[i] != value[i] {\n\t\t\t\tt.Errorf(\"%s: %T %T %s != %s\", \"ReadWriteBinary\", p, trans, v, value)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc UnmatchedBeginEndProtocolTest(t *testing.T, protocolFactory TProtocolFactory) {\n\t// NOTE: not all protocol implementations do strict state check to\n\t// return an error on unmatched Begin/End calls.\n\t// This test is only meant to make sure that those unmatched Begin/End\n\t// calls won't cause panic. There's no real \"test\" here.\n\ttrans := NewTMemoryBuffer()\n\tt.Run(\"Read\", func(t *testing.T) {\n\t\tt.Run(\"Message\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.ReadMessageEnd()\n\t\t\tp.ReadMessageEnd()\n\t\t})\n\t\tt.Run(\"Struct\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.ReadStructEnd()\n\t\t\tp.ReadStructEnd()\n\t\t})\n\t\tt.Run(\"Field\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.ReadFieldEnd()\n\t\t\tp.ReadFieldEnd()\n\t\t})\n\t\tt.Run(\"Map\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.ReadMapEnd()\n\t\t\tp.ReadMapEnd()\n\t\t})\n\t\tt.Run(\"List\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.ReadListEnd()\n\t\t\tp.ReadListEnd()\n\t\t})\n\t\tt.Run(\"Set\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.ReadSetEnd()\n\t\t\tp.ReadSetEnd()\n\t\t})\n\t})\n\tt.Run(\"Write\", func(t *testing.T) {\n\t\tt.Run(\"Message\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.WriteMessageEnd()\n\t\t\tp.WriteMessageEnd()\n\t\t})\n\t\tt.Run(\"Struct\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.WriteStructEnd()\n\t\t\tp.WriteStructEnd()\n\t\t})\n\t\tt.Run(\"Field\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.WriteFieldEnd()\n\t\t\tp.WriteFieldEnd()\n\t\t})\n\t\tt.Run(\"Map\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.WriteMapEnd()\n\t\t\tp.WriteMapEnd()\n\t\t})\n\t\tt.Run(\"List\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.WriteListEnd()\n\t\t\tp.WriteListEnd()\n\t\t})\n\t\tt.Run(\"Set\", func(t *testing.T) {\n\t\t\ttrans.Reset()\n\t\t\tp := protocolFactory.GetProtocol(trans)\n\t\t\tp.WriteSetEnd()\n\t\t\tp.WriteSetEnd()\n\t\t})\n\t})\n\ttrans.Close()\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/rich_transport.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport \"io\"\n\ntype RichTransport struct {\n\tTTransport\n}\n\n// Wraps Transport to provide TRichTransport interface\nfunc NewTRichTransport(trans TTransport) *RichTransport {\n\treturn &RichTransport{trans}\n}\n\nfunc (r *RichTransport) ReadByte() (c byte, err error) {\n\treturn readByte(r.TTransport)\n}\n\nfunc (r *RichTransport) WriteByte(c byte) error {\n\treturn writeByte(r.TTransport, c)\n}\n\nfunc (r *RichTransport) WriteString(s string) (n int, err error) {\n\treturn r.Write([]byte(s))\n}\n\nfunc (r *RichTransport) RemainingBytes() (num_bytes uint64) {\n\treturn r.TTransport.RemainingBytes()\n}\n\nfunc readByte(r io.Reader) (c byte, err error) {\n\tv := [1]byte{0}\n\tn, err := r.Read(v[0:1])\n\tif n > 0 && (err == nil || err == io.EOF) {\n\t\treturn v[0], nil\n\t}\n\tif n > 0 && err != nil {\n\t\treturn v[0], err\n\t}\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn v[0], nil\n}\n\nfunc writeByte(w io.Writer, c byte) error {\n\tv := [1]byte{c}\n\t_, err := w.Write(v[0:1])\n\treturn err\n}\n\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/rich_transport_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestEnsureTransportsAreRich(t *testing.T) {\n\tbuf := bytes.NewBuffer(make([]byte, 0, 1024))\n\n\ttransports := []TTransportFactory{\n\t\tNewTMemoryBufferTransportFactory(1024),\n\t\tNewStreamTransportFactory(buf, buf, true),\n\t\tNewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024)),\n\t\tNewTHttpPostClientTransportFactory(\"http://127.0.0.1\"),\n\t}\n\tfor _, tf := range transports {\n\t\ttrans := tf.GetTransport(nil)\n\t\t_, ok := trans.(TRichTransport)\n\t\tif !ok {\n\t\t\tt.Errorf(\"Transport %s does not implement TRichTransport interface\", reflect.ValueOf(trans))\n\t\t}\n\t}\n}\n\n// TestReadByte tests whether readByte handles error cases correctly.\nfunc TestReadByte(t *testing.T) {\n\tfor i, test := range readByteTests {\n\t\tv, err := readByte(test.r)\n\t\tif v != test.v {\n\t\t\tt.Fatalf(\"TestReadByte %d: value differs. Expected %d, got %d\", i, test.v, test.r.v)\n\t\t}\n\t\tif err != test.err {\n\t\t\tt.Fatalf(\"TestReadByte %d: error differs. Expected %s, got %s\", i, test.err, test.r.err)\n\t\t}\n\t}\n}\n\nvar someError = errors.New(\"Some error\")\nvar readByteTests = []struct {\n\tr   *mockReader\n\tv   byte\n\terr error\n}{\n\t{&mockReader{0, 55, io.EOF}, 0, io.EOF},        // reader sends EOF w/o data\n\t{&mockReader{0, 55, someError}, 0, someError},  // reader sends some other error\n\t{&mockReader{1, 55, nil}, 55, nil},             // reader sends data w/o error\n\t{&mockReader{1, 55, io.EOF}, 55, nil},          // reader sends data with EOF\n\t{&mockReader{1, 55, someError}, 55, someError}, // reader sends data withsome error\n}\n\ntype mockReader struct {\n\tn   int\n\tv   byte\n\terr error\n}\n\nfunc (r *mockReader) Read(p []byte) (n int, err error) {\n\tif r.n > 0 {\n\t\tp[0] = r.v\n\t}\n\treturn r.n, r.err\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/serializer.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\ntype TSerializer struct {\n\tTransport *TMemoryBuffer\n\tProtocol  TProtocol\n}\n\ntype TStruct interface {\n\tWrite(p TProtocol) error\n\tRead(p TProtocol) error\n}\n\nfunc NewTSerializer() *TSerializer {\n\ttransport := NewTMemoryBufferLen(1024)\n\tprotocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport)\n\n\treturn &TSerializer{\n\t\ttransport,\n\t\tprotocol}\n}\n\nfunc (t *TSerializer) WriteString(msg TStruct) (s string, err error) {\n\tt.Transport.Reset()\n\n\tif err = msg.Write(t.Protocol); err != nil {\n\t\treturn\n\t}\n\n\tif err = t.Protocol.Flush(); err != nil {\n\t\treturn\n\t}\n\tif err = t.Transport.Flush(); err != nil {\n\t\treturn\n\t}\n\n\treturn t.Transport.String(), nil\n}\n\nfunc (t *TSerializer) Write(msg TStruct) (b []byte, err error) {\n\tt.Transport.Reset()\n\n\tif err = msg.Write(t.Protocol); err != nil {\n\t\treturn\n\t}\n\n\tif err = t.Protocol.Flush(); err != nil {\n\t\treturn\n\t}\n\n\tif err = t.Transport.Flush(); err != nil {\n\t\treturn\n\t}\n\n\tb = append(b, t.Transport.Bytes()...)\n\treturn\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/serializer_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n)\n\ntype ProtocolFactory interface {\n\tGetProtocol(t TTransport) TProtocol\n}\n\nfunc compareStructs(m, m1 MyTestStruct) (bool, error) {\n\tswitch {\n\tcase m.On != m1.On:\n\t\treturn false, errors.New(\"Boolean not equal\")\n\tcase m.B != m1.B:\n\t\treturn false, errors.New(\"Byte not equal\")\n\tcase m.Int16 != m1.Int16:\n\t\treturn false, errors.New(\"Int16 not equal\")\n\tcase m.Int32 != m1.Int32:\n\t\treturn false, errors.New(\"Int32 not equal\")\n\tcase m.Int64 != m1.Int64:\n\t\treturn false, errors.New(\"Int64 not equal\")\n\tcase m.D != m1.D:\n\t\treturn false, errors.New(\"Double not equal\")\n\tcase m.St != m1.St:\n\t\treturn false, errors.New(\"String not equal\")\n\n\tcase len(m.Bin) != len(m1.Bin):\n\t\treturn false, errors.New(\"Binary size not equal\")\n\tcase len(m.Bin) == len(m1.Bin):\n\t\tfor i := range m.Bin {\n\t\t\tif m.Bin[i] != m1.Bin[i] {\n\t\t\t\treturn false, errors.New(\"Binary not equal\")\n\t\t\t}\n\t\t}\n\tcase len(m.StringMap) != len(m1.StringMap):\n\t\treturn false, errors.New(\"StringMap size not equal\")\n\tcase len(m.StringList) != len(m1.StringList):\n\t\treturn false, errors.New(\"StringList size not equal\")\n\tcase len(m.StringSet) != len(m1.StringSet):\n\t\treturn false, errors.New(\"StringSet size not equal\")\n\n\tcase m.E != m1.E:\n\t\treturn false, errors.New(\"MyTestEnum not equal\")\n\n\tdefault:\n\t\treturn true, nil\n\n\t}\n\treturn true, nil\n}\n\nfunc ProtocolTest1(test *testing.T, pf ProtocolFactory) (bool, error) {\n\tt := NewTSerializer()\n\tt.Protocol = pf.GetProtocol(t.Transport)\n\tvar m = MyTestStruct{}\n\tm.On = true\n\tm.B = int8(0)\n\tm.Int16 = 1\n\tm.Int32 = 2\n\tm.Int64 = 3\n\tm.D = 4.1\n\tm.St = \"Test\"\n\tm.Bin = make([]byte, 10)\n\tm.StringMap = make(map[string]string, 5)\n\tm.StringList = make([]string, 5)\n\tm.StringSet = make(map[string]struct{}, 5)\n\tm.E = 2\n\n\ts, err := t.WriteString(&m)\n\tif err != nil {\n\t\treturn false, errors.New(fmt.Sprintf(\"Unable to Serialize struct\\n\\t %s\", err))\n\t}\n\n\tt1 := NewTDeserializer()\n\tt1.Protocol = pf.GetProtocol(t1.Transport)\n\tvar m1 = MyTestStruct{}\n\tif err = t1.ReadString(&m1, s); err != nil {\n\t\treturn false, errors.New(fmt.Sprintf(\"Unable to Deserialize struct\\n\\t %s\", err))\n\n\t}\n\n\treturn compareStructs(m, m1)\n\n}\n\nfunc ProtocolTest2(test *testing.T, pf ProtocolFactory) (bool, error) {\n\tt := NewTSerializer()\n\tt.Protocol = pf.GetProtocol(t.Transport)\n\tvar m = MyTestStruct{}\n\tm.On = false\n\tm.B = int8(0)\n\tm.Int16 = 1\n\tm.Int32 = 2\n\tm.Int64 = 3\n\tm.D = 4.1\n\tm.St = \"Test\"\n\tm.Bin = make([]byte, 10)\n\tm.StringMap = make(map[string]string, 5)\n\tm.StringList = make([]string, 5)\n\tm.StringSet = make(map[string]struct{}, 5)\n\tm.E = 2\n\n\ts, err := t.WriteString(&m)\n\tif err != nil {\n\t\treturn false, errors.New(fmt.Sprintf(\"Unable to Serialize struct\\n\\t %s\", err))\n\n\t}\n\n\tt1 := NewTDeserializer()\n\tt1.Protocol = pf.GetProtocol(t1.Transport)\n\tvar m1 = MyTestStruct{}\n\tif err = t1.ReadString(&m1, s); err != nil {\n\t\treturn false, errors.New(fmt.Sprintf(\"Unable to Deserialize struct\\n\\t %s\", err))\n\n\t}\n\n\treturn compareStructs(m, m1)\n\n}\n\nfunc TestSerializer(t *testing.T) {\n\n\tvar protocol_factories map[string]ProtocolFactory\n\tprotocol_factories = make(map[string]ProtocolFactory)\n\tprotocol_factories[\"Binary\"] = NewTBinaryProtocolFactoryDefault()\n\tprotocol_factories[\"Compact\"] = NewTCompactProtocolFactory()\n\t//protocol_factories[\"SimpleJSON\"] = NewTSimpleJSONProtocolFactory() - write only, can't be read back by design\n\tprotocol_factories[\"JSON\"] = NewTJSONProtocolFactory()\n\n\tvar tests map[string]func(*testing.T, ProtocolFactory) (bool, error)\n\ttests = make(map[string]func(*testing.T, ProtocolFactory) (bool, error))\n\ttests[\"Test 1\"] = ProtocolTest1\n\ttests[\"Test 2\"] = ProtocolTest2\n\t//tests[\"Test 3\"] = ProtocolTest3 // Example of how to add additional tests\n\n\tfor name, pf := range protocol_factories {\n\n\t\tfor test, f := range tests {\n\n\t\t\tif s, err := f(t, pf); !s || err != nil {\n\t\t\t\tt.Errorf(\"%s Failed for %s protocol\\n\\t %s\", test, name, err)\n\t\t\t}\n\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/serializer_types_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\n// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\n/* THE FOLLOWING THRIFT FILE WAS USED TO CREATE THIS\n\nenum MyTestEnum {\n\tFIRST = 1,\n\tSECOND = 2,\n\tTHIRD = 3,\n\tFOURTH = 4,\n}\n\nstruct MyTestStruct {\n\t1: bool on,\n\t2: byte b,\n\t3: i16 int16,\n\t4: i32 int32,\n\t5: i64 int64,\n\t6: double d,\n\t7: string st,\n\t8: binary bin,\n\t9: map<string, string> stringMap,\n\t10: list<string> stringList,\n\t11: set<string> stringSet,\n\t12: MyTestEnum e,\n}\n*/\n\nimport (\n\t\"fmt\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = ZERO\nvar _ = fmt.Printf\n\nvar GoUnusedProtection__ int\n\ntype MyTestEnum int64\n\nconst (\n\tMyTestEnum_FIRST  MyTestEnum = 1\n\tMyTestEnum_SECOND MyTestEnum = 2\n\tMyTestEnum_THIRD  MyTestEnum = 3\n\tMyTestEnum_FOURTH MyTestEnum = 4\n)\n\nfunc (p MyTestEnum) String() string {\n\tswitch p {\n\tcase MyTestEnum_FIRST:\n\t\treturn \"FIRST\"\n\tcase MyTestEnum_SECOND:\n\t\treturn \"SECOND\"\n\tcase MyTestEnum_THIRD:\n\t\treturn \"THIRD\"\n\tcase MyTestEnum_FOURTH:\n\t\treturn \"FOURTH\"\n\t}\n\treturn \"<UNSET>\"\n}\n\nfunc MyTestEnumFromString(s string) (MyTestEnum, error) {\n\tswitch s {\n\tcase \"FIRST\":\n\t\treturn MyTestEnum_FIRST, nil\n\tcase \"SECOND\":\n\t\treturn MyTestEnum_SECOND, nil\n\tcase \"THIRD\":\n\t\treturn MyTestEnum_THIRD, nil\n\tcase \"FOURTH\":\n\t\treturn MyTestEnum_FOURTH, nil\n\t}\n\treturn MyTestEnum(0), fmt.Errorf(\"not a valid MyTestEnum string\")\n}\n\nfunc MyTestEnumPtr(v MyTestEnum) *MyTestEnum { return &v }\n\ntype MyTestStruct struct {\n\tOn         bool                `thrift:\"on,1\" json:\"on\"`\n\tB          int8                `thrift:\"b,2\" json:\"b\"`\n\tInt16      int16               `thrift:\"int16,3\" json:\"int16\"`\n\tInt32      int32               `thrift:\"int32,4\" json:\"int32\"`\n\tInt64      int64               `thrift:\"int64,5\" json:\"int64\"`\n\tD          float64             `thrift:\"d,6\" json:\"d\"`\n\tSt         string              `thrift:\"st,7\" json:\"st\"`\n\tBin        []byte              `thrift:\"bin,8\" json:\"bin\"`\n\tStringMap  map[string]string   `thrift:\"stringMap,9\" json:\"stringMap\"`\n\tStringList []string            `thrift:\"stringList,10\" json:\"stringList\"`\n\tStringSet  map[string]struct{} `thrift:\"stringSet,11\" json:\"stringSet\"`\n\tE          MyTestEnum          `thrift:\"e,12\" json:\"e\"`\n}\n\nfunc NewMyTestStruct() *MyTestStruct {\n\treturn &MyTestStruct{}\n}\n\nfunc (p *MyTestStruct) GetOn() bool {\n\treturn p.On\n}\n\nfunc (p *MyTestStruct) GetB() int8 {\n\treturn p.B\n}\n\nfunc (p *MyTestStruct) GetInt16() int16 {\n\treturn p.Int16\n}\n\nfunc (p *MyTestStruct) GetInt32() int32 {\n\treturn p.Int32\n}\n\nfunc (p *MyTestStruct) GetInt64() int64 {\n\treturn p.Int64\n}\n\nfunc (p *MyTestStruct) GetD() float64 {\n\treturn p.D\n}\n\nfunc (p *MyTestStruct) GetSt() string {\n\treturn p.St\n}\n\nfunc (p *MyTestStruct) GetBin() []byte {\n\treturn p.Bin\n}\n\nfunc (p *MyTestStruct) GetStringMap() map[string]string {\n\treturn p.StringMap\n}\n\nfunc (p *MyTestStruct) GetStringList() []string {\n\treturn p.StringList\n}\n\nfunc (p *MyTestStruct) GetStringSet() map[string]struct{} {\n\treturn p.StringSet\n}\n\nfunc (p *MyTestStruct) GetE() MyTestEnum {\n\treturn p.E\n}\nfunc (p *MyTestStruct) Read(iprot TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.readField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 2:\n\t\t\tif err := p.readField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 3:\n\t\t\tif err := p.readField3(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 4:\n\t\t\tif err := p.readField4(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 5:\n\t\t\tif err := p.readField5(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 6:\n\t\t\tif err := p.readField6(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 7:\n\t\t\tif err := p.readField7(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 8:\n\t\t\tif err := p.readField8(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 9:\n\t\t\tif err := p.readField9(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 10:\n\t\t\tif err := p.readField10(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 11:\n\t\t\tif err := p.readField11(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 12:\n\t\t\tif err := p.readField12(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField1(iprot TProtocol) error {\n\tif v, err := iprot.ReadBool(); err != nil {\n\t\treturn PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.On = v\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField2(iprot TProtocol) error {\n\tif v, err := iprot.ReadByte(); err != nil {\n\t\treturn PrependError(\"error reading field 2: \", err)\n\t} else {\n\t\ttemp := int8(v)\n\t\tp.B = temp\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField3(iprot TProtocol) error {\n\tif v, err := iprot.ReadI16(); err != nil {\n\t\treturn PrependError(\"error reading field 3: \", err)\n\t} else {\n\t\tp.Int16 = v\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField4(iprot TProtocol) error {\n\tif v, err := iprot.ReadI32(); err != nil {\n\t\treturn PrependError(\"error reading field 4: \", err)\n\t} else {\n\t\tp.Int32 = v\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField5(iprot TProtocol) error {\n\tif v, err := iprot.ReadI64(); err != nil {\n\t\treturn PrependError(\"error reading field 5: \", err)\n\t} else {\n\t\tp.Int64 = v\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField6(iprot TProtocol) error {\n\tif v, err := iprot.ReadDouble(); err != nil {\n\t\treturn PrependError(\"error reading field 6: \", err)\n\t} else {\n\t\tp.D = v\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField7(iprot TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn PrependError(\"error reading field 7: \", err)\n\t} else {\n\t\tp.St = v\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField8(iprot TProtocol) error {\n\tif v, err := iprot.ReadBinary(); err != nil {\n\t\treturn PrependError(\"error reading field 8: \", err)\n\t} else {\n\t\tp.Bin = v\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField9(iprot TProtocol) error {\n\t_, _, size, err := iprot.ReadMapBegin()\n\tif err != nil {\n\t\treturn PrependError(\"error reading map begin: \", err)\n\t}\n\ttMap := make(map[string]string, size)\n\tp.StringMap = tMap\n\tfor i := 0; i < size; i++ {\n\t\tvar _key0 string\n\t\tif v, err := iprot.ReadString(); err != nil {\n\t\t\treturn PrependError(\"error reading field 0: \", err)\n\t\t} else {\n\t\t\t_key0 = v\n\t\t}\n\t\tvar _val1 string\n\t\tif v, err := iprot.ReadString(); err != nil {\n\t\t\treturn PrependError(\"error reading field 0: \", err)\n\t\t} else {\n\t\t\t_val1 = v\n\t\t}\n\t\tp.StringMap[_key0] = _val1\n\t}\n\tif err := iprot.ReadMapEnd(); err != nil {\n\t\treturn PrependError(\"error reading map end: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField10(iprot TProtocol) error {\n\t_, size, err := iprot.ReadListBegin()\n\tif err != nil {\n\t\treturn PrependError(\"error reading list begin: \", err)\n\t}\n\ttSlice := make([]string, 0, size)\n\tp.StringList = tSlice\n\tfor i := 0; i < size; i++ {\n\t\tvar _elem2 string\n\t\tif v, err := iprot.ReadString(); err != nil {\n\t\t\treturn PrependError(\"error reading field 0: \", err)\n\t\t} else {\n\t\t\t_elem2 = v\n\t\t}\n\t\tp.StringList = append(p.StringList, _elem2)\n\t}\n\tif err := iprot.ReadListEnd(); err != nil {\n\t\treturn PrependError(\"error reading list end: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField11(iprot TProtocol) error {\n\t_, size, err := iprot.ReadSetBegin()\n\tif err != nil {\n\t\treturn PrependError(\"error reading set begin: \", err)\n\t}\n\ttSet := make(map[string]struct{}, size)\n\tp.StringSet = tSet\n\tfor i := 0; i < size; i++ {\n\t\tvar _elem3 string\n\t\tif v, err := iprot.ReadString(); err != nil {\n\t\t\treturn PrependError(\"error reading field 0: \", err)\n\t\t} else {\n\t\t\t_elem3 = v\n\t\t}\n\t\tp.StringSet[_elem3] = struct{}{}\n\t}\n\tif err := iprot.ReadSetEnd(); err != nil {\n\t\treturn PrependError(\"error reading set end: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) readField12(iprot TProtocol) error {\n\tif v, err := iprot.ReadI32(); err != nil {\n\t\treturn PrependError(\"error reading field 12: \", err)\n\t} else {\n\t\ttemp := MyTestEnum(v)\n\t\tp.E = temp\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) Write(oprot TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"MyTestStruct\"); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField3(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField4(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField5(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField6(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField7(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField8(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField9(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField10(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField11(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField12(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MyTestStruct) writeField1(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"on\", BOOL, 1); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 1:on: \", p), err)\n\t}\n\tif err := oprot.WriteBool(bool(p.On)); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T.on (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 1:on: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) writeField2(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"b\", BYTE, 2); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 2:b: \", p), err)\n\t}\n\tif err := oprot.WriteByte(int8(p.B)); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T.b (2) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 2:b: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) writeField3(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"int16\", I16, 3); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 3:int16: \", p), err)\n\t}\n\tif err := oprot.WriteI16(int16(p.Int16)); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T.int16 (3) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 3:int16: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) writeField4(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"int32\", I32, 4); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 4:int32: \", p), err)\n\t}\n\tif err := oprot.WriteI32(int32(p.Int32)); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T.int32 (4) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 4:int32: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) writeField5(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"int64\", I64, 5); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 5:int64: \", p), err)\n\t}\n\tif err := oprot.WriteI64(int64(p.Int64)); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T.int64 (5) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 5:int64: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) writeField6(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"d\", DOUBLE, 6); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 6:d: \", p), err)\n\t}\n\tif err := oprot.WriteDouble(float64(p.D)); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T.d (6) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 6:d: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) writeField7(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"st\", STRING, 7); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 7:st: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.St)); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T.st (7) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 7:st: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) writeField8(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"bin\", STRING, 8); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 8:bin: \", p), err)\n\t}\n\tif err := oprot.WriteBinary(p.Bin); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T.bin (8) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 8:bin: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) writeField9(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"stringMap\", MAP, 9); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 9:stringMap: \", p), err)\n\t}\n\tif err := oprot.WriteMapBegin(STRING, STRING, len(p.StringMap)); err != nil {\n\t\treturn PrependError(\"error writing map begin: \", err)\n\t}\n\tfor k, v := range p.StringMap {\n\t\tif err := oprot.WriteString(string(k)); err != nil {\n\t\t\treturn PrependError(fmt.Sprintf(\"%T. (0) field write error: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteString(string(v)); err != nil {\n\t\t\treturn PrependError(fmt.Sprintf(\"%T. (0) field write error: \", p), err)\n\t\t}\n\t}\n\tif err := oprot.WriteMapEnd(); err != nil {\n\t\treturn PrependError(\"error writing map end: \", err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 9:stringMap: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) writeField10(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"stringList\", LIST, 10); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 10:stringList: \", p), err)\n\t}\n\tif err := oprot.WriteListBegin(STRING, len(p.StringList)); err != nil {\n\t\treturn PrependError(\"error writing list begin: \", err)\n\t}\n\tfor _, v := range p.StringList {\n\t\tif err := oprot.WriteString(string(v)); err != nil {\n\t\t\treturn PrependError(fmt.Sprintf(\"%T. (0) field write error: \", p), err)\n\t\t}\n\t}\n\tif err := oprot.WriteListEnd(); err != nil {\n\t\treturn PrependError(\"error writing list end: \", err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 10:stringList: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) writeField11(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"stringSet\", SET, 11); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 11:stringSet: \", p), err)\n\t}\n\tif err := oprot.WriteSetBegin(STRING, len(p.StringSet)); err != nil {\n\t\treturn PrependError(\"error writing set begin: \", err)\n\t}\n\tfor v, _ := range p.StringSet {\n\t\tif err := oprot.WriteString(string(v)); err != nil {\n\t\t\treturn PrependError(fmt.Sprintf(\"%T. (0) field write error: \", p), err)\n\t\t}\n\t}\n\tif err := oprot.WriteSetEnd(); err != nil {\n\t\treturn PrependError(\"error writing set end: \", err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 11:stringSet: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) writeField12(oprot TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"e\", I32, 12); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field begin error 12:e: \", p), err)\n\t}\n\tif err := oprot.WriteI32(int32(p.E)); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T.e (12) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn PrependError(fmt.Sprintf(\"%T write field end error 12:e: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MyTestStruct) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"MyTestStruct(%+v)\", *p)\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/server.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\ntype TServer interface {\n\tProcessorFactory() TProcessorFactory\n\tServerTransport() TServerTransport\n\tInputTransportFactory() TTransportFactory\n\tOutputTransportFactory() TTransportFactory\n\tInputProtocolFactory() TProtocolFactory\n\tOutputProtocolFactory() TProtocolFactory\n\n\t// Starts the server\n\tServe() error\n\t// Stops the server. This is optional on a per-implementation basis. Not\n\t// all servers are required to be cleanly stoppable.\n\tStop() error\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/server_socket.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype TServerSocket struct {\n\tlistener      net.Listener\n\taddr          net.Addr\n\tclientTimeout time.Duration\n\n\t// Protects the interrupted value to make it thread safe.\n\tmu          sync.RWMutex\n\tinterrupted bool\n}\n\nfunc NewTServerSocket(listenAddr string) (*TServerSocket, error) {\n\treturn NewTServerSocketTimeout(listenAddr, 0)\n}\n\nfunc NewTServerSocketTimeout(listenAddr string, clientTimeout time.Duration) (*TServerSocket, error) {\n\taddr, err := net.ResolveTCPAddr(\"tcp\", listenAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &TServerSocket{addr: addr, clientTimeout: clientTimeout}, nil\n}\n\nfunc (p *TServerSocket) Listen() error {\n\tif p.IsListening() {\n\t\treturn nil\n\t}\n\tl, err := net.Listen(p.addr.Network(), p.addr.String())\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.listener = l\n\treturn nil\n}\n\nfunc (p *TServerSocket) Accept() (TTransport, error) {\n\tp.mu.RLock()\n\tinterrupted := p.interrupted\n\tp.mu.RUnlock()\n\n\tif interrupted {\n\t\treturn nil, errTransportInterrupted\n\t}\n\tif p.listener == nil {\n\t\treturn nil, NewTTransportException(NOT_OPEN, \"No underlying server socket\")\n\t}\n\tconn, err := p.listener.Accept()\n\tif err != nil {\n\t\treturn nil, NewTTransportExceptionFromError(err)\n\t}\n\treturn NewTSocketFromConnTimeout(conn, p.clientTimeout), nil\n}\n\n// Checks whether the socket is listening.\nfunc (p *TServerSocket) IsListening() bool {\n\treturn p.listener != nil\n}\n\n// Connects the socket, creating a new socket object if necessary.\nfunc (p *TServerSocket) Open() error {\n\tif p.IsListening() {\n\t\treturn NewTTransportException(ALREADY_OPEN, \"Server socket already open\")\n\t}\n\tif l, err := net.Listen(p.addr.Network(), p.addr.String()); err != nil {\n\t\treturn err\n\t} else {\n\t\tp.listener = l\n\t}\n\treturn nil\n}\n\nfunc (p *TServerSocket) Addr() net.Addr {\n\tif p.listener != nil {\n\t\treturn p.listener.Addr()\n\t}\n\treturn p.addr\n}\n\nfunc (p *TServerSocket) Close() error {\n\tdefer func() {\n\t\tp.listener = nil\n\t}()\n\tif p.IsListening() {\n\t\treturn p.listener.Close()\n\t}\n\treturn nil\n}\n\nfunc (p *TServerSocket) Interrupt() error {\n\tp.mu.Lock()\n\tp.interrupted = true\n\tp.Close()\n\tp.mu.Unlock()\n\n\treturn nil\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/server_socket_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestSocketIsntListeningAfterInterrupt(t *testing.T) {\n\thost := \"127.0.0.1\"\n\tport := 9090\n\taddr := fmt.Sprintf(\"%s:%d\", host, port)\n\n\tsocket := CreateServerSocket(t, addr)\n\tsocket.Listen()\n\tsocket.Interrupt()\n\n\tnewSocket := CreateServerSocket(t, addr)\n\terr := newSocket.Listen()\n\tdefer newSocket.Interrupt()\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to rebinds: %s\", err)\n\t}\n}\n\nfunc CreateServerSocket(t *testing.T, addr string) *TServerSocket {\n\tsocket, err := NewTServerSocket(addr)\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to create server socket: %s\", err)\n\t}\n\treturn socket\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/server_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"testing\"\n)\n\nfunc TestNothing(t *testing.T) {\n\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/server_transport.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\n// Server transport. Object which provides client transports.\ntype TServerTransport interface {\n\tListen() error\n\tAccept() (TTransport, error)\n\tClose() error\n\n\t// Optional method implementation. This signals to the server transport\n\t// that it should break out of any accept() or listen() that it is currently\n\t// blocked on. This method, if implemented, MUST be thread safe, as it may\n\t// be called from a different thread context than the other TServerTransport\n\t// methods.\n\tInterrupt() error\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/simple_json_protocol.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"strconv\"\n)\n\ntype _ParseContext int\n\nconst (\n\t_CONTEXT_IN_TOPLEVEL          _ParseContext = 1\n\t_CONTEXT_IN_LIST_FIRST        _ParseContext = 2\n\t_CONTEXT_IN_LIST              _ParseContext = 3\n\t_CONTEXT_IN_OBJECT_FIRST      _ParseContext = 4\n\t_CONTEXT_IN_OBJECT_NEXT_KEY   _ParseContext = 5\n\t_CONTEXT_IN_OBJECT_NEXT_VALUE _ParseContext = 6\n)\n\nfunc (p _ParseContext) String() string {\n\tswitch p {\n\tcase _CONTEXT_IN_TOPLEVEL:\n\t\treturn \"TOPLEVEL\"\n\tcase _CONTEXT_IN_LIST_FIRST:\n\t\treturn \"LIST-FIRST\"\n\tcase _CONTEXT_IN_LIST:\n\t\treturn \"LIST\"\n\tcase _CONTEXT_IN_OBJECT_FIRST:\n\t\treturn \"OBJECT-FIRST\"\n\tcase _CONTEXT_IN_OBJECT_NEXT_KEY:\n\t\treturn \"OBJECT-NEXT-KEY\"\n\tcase _CONTEXT_IN_OBJECT_NEXT_VALUE:\n\t\treturn \"OBJECT-NEXT-VALUE\"\n\t}\n\treturn \"UNKNOWN-PARSE-CONTEXT\"\n}\n\n// JSON protocol implementation for thrift.\n//\n// This protocol produces/consumes a simple output format\n// suitable for parsing by scripting languages.  It should not be\n// confused with the full-featured TJSONProtocol.\n//\ntype TSimpleJSONProtocol struct {\n\ttrans TTransport\n\n\tparseContextStack []int\n\tdumpContext       []int\n\n\twriter *bufio.Writer\n\treader *bufio.Reader\n}\n\n// Constructor\nfunc NewTSimpleJSONProtocol(t TTransport) *TSimpleJSONProtocol {\n\tv := &TSimpleJSONProtocol{trans: t,\n\t\twriter: bufio.NewWriter(t),\n\t\treader: bufio.NewReader(t),\n\t}\n\tv.parseContextStack = append(v.parseContextStack, int(_CONTEXT_IN_TOPLEVEL))\n\tv.dumpContext = append(v.dumpContext, int(_CONTEXT_IN_TOPLEVEL))\n\treturn v\n}\n\n// Factory\ntype TSimpleJSONProtocolFactory struct{}\n\nfunc (p *TSimpleJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol {\n\treturn NewTSimpleJSONProtocol(trans)\n}\n\nfunc NewTSimpleJSONProtocolFactory() *TSimpleJSONProtocolFactory {\n\treturn &TSimpleJSONProtocolFactory{}\n}\n\nvar (\n\tJSON_COMMA                   []byte\n\tJSON_COLON                   []byte\n\tJSON_LBRACE                  []byte\n\tJSON_RBRACE                  []byte\n\tJSON_LBRACKET                []byte\n\tJSON_RBRACKET                []byte\n\tJSON_QUOTE                   byte\n\tJSON_QUOTE_BYTES             []byte\n\tJSON_NULL                    []byte\n\tJSON_TRUE                    []byte\n\tJSON_FALSE                   []byte\n\tJSON_INFINITY                string\n\tJSON_NEGATIVE_INFINITY       string\n\tJSON_NAN                     string\n\tJSON_INFINITY_BYTES          []byte\n\tJSON_NEGATIVE_INFINITY_BYTES []byte\n\tJSON_NAN_BYTES               []byte\n\tjson_nonbase_map_elem_bytes  []byte\n)\n\nfunc init() {\n\tJSON_COMMA = []byte{','}\n\tJSON_COLON = []byte{':'}\n\tJSON_LBRACE = []byte{'{'}\n\tJSON_RBRACE = []byte{'}'}\n\tJSON_LBRACKET = []byte{'['}\n\tJSON_RBRACKET = []byte{']'}\n\tJSON_QUOTE = '\"'\n\tJSON_QUOTE_BYTES = []byte{'\"'}\n\tJSON_NULL = []byte{'n', 'u', 'l', 'l'}\n\tJSON_TRUE = []byte{'t', 'r', 'u', 'e'}\n\tJSON_FALSE = []byte{'f', 'a', 'l', 's', 'e'}\n\tJSON_INFINITY = \"Infinity\"\n\tJSON_NEGATIVE_INFINITY = \"-Infinity\"\n\tJSON_NAN = \"NaN\"\n\tJSON_INFINITY_BYTES = []byte{'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'}\n\tJSON_NEGATIVE_INFINITY_BYTES = []byte{'-', 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'}\n\tJSON_NAN_BYTES = []byte{'N', 'a', 'N'}\n\tjson_nonbase_map_elem_bytes = []byte{']', ',', '['}\n}\n\nfunc jsonQuote(s string) string {\n\tb, _ := json.Marshal(s)\n\ts1 := string(b)\n\treturn s1\n}\n\nfunc jsonUnquote(s string) (string, bool) {\n\ts1 := new(string)\n\terr := json.Unmarshal([]byte(s), s1)\n\treturn *s1, err == nil\n}\n\nfunc mismatch(expected, actual string) error {\n\treturn fmt.Errorf(\"Expected '%s' but found '%s' while parsing JSON.\", expected, actual)\n}\n\nfunc (p *TSimpleJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error {\n\tp.resetContextStack() // THRIFT-3735\n\tif e := p.OutputListBegin(); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteString(name); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteByte(int8(typeId)); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteI32(seqId); e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) WriteMessageEnd() error {\n\treturn p.OutputListEnd()\n}\n\nfunc (p *TSimpleJSONProtocol) WriteStructBegin(name string) error {\n\tif e := p.OutputObjectBegin(); e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) WriteStructEnd() error {\n\treturn p.OutputObjectEnd()\n}\n\nfunc (p *TSimpleJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {\n\tif e := p.WriteString(name); e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) WriteFieldEnd() error {\n\t//return p.OutputListEnd()\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) WriteFieldStop() error { return nil }\n\nfunc (p *TSimpleJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {\n\tif e := p.OutputListBegin(); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteByte(int8(keyType)); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteByte(int8(valueType)); e != nil {\n\t\treturn e\n\t}\n\treturn p.WriteI32(int32(size))\n}\n\nfunc (p *TSimpleJSONProtocol) WriteMapEnd() error {\n\treturn p.OutputListEnd()\n}\n\nfunc (p *TSimpleJSONProtocol) WriteListBegin(elemType TType, size int) error {\n\treturn p.OutputElemListBegin(elemType, size)\n}\n\nfunc (p *TSimpleJSONProtocol) WriteListEnd() error {\n\treturn p.OutputListEnd()\n}\n\nfunc (p *TSimpleJSONProtocol) WriteSetBegin(elemType TType, size int) error {\n\treturn p.OutputElemListBegin(elemType, size)\n}\n\nfunc (p *TSimpleJSONProtocol) WriteSetEnd() error {\n\treturn p.OutputListEnd()\n}\n\nfunc (p *TSimpleJSONProtocol) WriteBool(b bool) error {\n\treturn p.OutputBool(b)\n}\n\nfunc (p *TSimpleJSONProtocol) WriteByte(b int8) error {\n\treturn p.WriteI32(int32(b))\n}\n\nfunc (p *TSimpleJSONProtocol) WriteI16(v int16) error {\n\treturn p.WriteI32(int32(v))\n}\n\nfunc (p *TSimpleJSONProtocol) WriteI32(v int32) error {\n\treturn p.OutputI64(int64(v))\n}\n\nfunc (p *TSimpleJSONProtocol) WriteI64(v int64) error {\n\treturn p.OutputI64(int64(v))\n}\n\nfunc (p *TSimpleJSONProtocol) WriteDouble(v float64) error {\n\treturn p.OutputF64(v)\n}\n\nfunc (p *TSimpleJSONProtocol) WriteString(v string) error {\n\treturn p.OutputString(v)\n}\n\nfunc (p *TSimpleJSONProtocol) WriteBinary(v []byte) error {\n\t// JSON library only takes in a string,\n\t// not an arbitrary byte array, to ensure bytes are transmitted\n\t// efficiently we must convert this into a valid JSON string\n\t// therefore we use base64 encoding to avoid excessive escaping/quoting\n\tif e := p.OutputPreValue(); e != nil {\n\t\treturn e\n\t}\n\tif _, e := p.write(JSON_QUOTE_BYTES); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\twriter := base64.NewEncoder(base64.StdEncoding, p.writer)\n\tif _, e := writer.Write(v); e != nil {\n\t\tp.writer.Reset(p.trans) // THRIFT-3735\n\t\treturn NewTProtocolException(e)\n\t}\n\tif e := writer.Close(); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\tif _, e := p.write(JSON_QUOTE_BYTES); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\treturn p.OutputPostValue()\n}\n\n// Reading methods.\nfunc (p *TSimpleJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) {\n\tp.resetContextStack() // THRIFT-3735\n\tif isNull, err := p.ParseListBegin(); isNull || err != nil {\n\t\treturn name, typeId, seqId, err\n\t}\n\tif name, err = p.ReadString(); err != nil {\n\t\treturn name, typeId, seqId, err\n\t}\n\tbTypeId, err := p.ReadByte()\n\ttypeId = TMessageType(bTypeId)\n\tif err != nil {\n\t\treturn name, typeId, seqId, err\n\t}\n\tif seqId, err = p.ReadI32(); err != nil {\n\t\treturn name, typeId, seqId, err\n\t}\n\treturn name, typeId, seqId, nil\n}\n\nfunc (p *TSimpleJSONProtocol) ReadMessageEnd() error {\n\treturn p.ParseListEnd()\n}\n\nfunc (p *TSimpleJSONProtocol) ReadStructBegin() (name string, err error) {\n\t_, err = p.ParseObjectStart()\n\treturn \"\", err\n}\n\nfunc (p *TSimpleJSONProtocol) ReadStructEnd() error {\n\treturn p.ParseObjectEnd()\n}\n\nfunc (p *TSimpleJSONProtocol) ReadFieldBegin() (string, TType, int16, error) {\n\tif err := p.ParsePreValue(); err != nil {\n\t\treturn \"\", STOP, 0, err\n\t}\n\tb, _ := p.reader.Peek(1)\n\tif len(b) > 0 {\n\t\tswitch b[0] {\n\t\tcase JSON_RBRACE[0]:\n\t\t\treturn \"\", STOP, 0, nil\n\t\tcase JSON_QUOTE:\n\t\t\tp.reader.ReadByte()\n\t\t\tname, err := p.ParseStringBody()\n\t\t\t// simplejson is not meant to be read back into thrift\n\t\t\t// - see http://wiki.apache.org/thrift/ThriftUsageJava\n\t\t\t// - use JSON instead\n\t\t\tif err != nil {\n\t\t\t\treturn name, STOP, 0, err\n\t\t\t}\n\t\t\treturn name, STOP, -1, p.ParsePostValue()\n\t\t\t/*\n\t\t\t   if err = p.ParsePostValue(); err != nil {\n\t\t\t     return name, STOP, 0, err\n\t\t\t   }\n\t\t\t   if isNull, err := p.ParseListBegin(); isNull || err != nil {\n\t\t\t     return name, STOP, 0, err\n\t\t\t   }\n\t\t\t   bType, err := p.ReadByte()\n\t\t\t   thetype := TType(bType)\n\t\t\t   if err != nil {\n\t\t\t     return name, thetype, 0, err\n\t\t\t   }\n\t\t\t   id, err := p.ReadI16()\n\t\t\t   return name, thetype, id, err\n\t\t\t*/\n\t\t}\n\t\te := fmt.Errorf(\"Expected \\\"}\\\" or '\\\"', but found: '%s'\", string(b))\n\t\treturn \"\", STOP, 0, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t}\n\treturn \"\", STOP, 0, NewTProtocolException(io.EOF)\n}\n\nfunc (p *TSimpleJSONProtocol) ReadFieldEnd() error {\n\treturn nil\n\t//return p.ParseListEnd()\n}\n\nfunc (p *TSimpleJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e error) {\n\tif isNull, e := p.ParseListBegin(); isNull || e != nil {\n\t\treturn VOID, VOID, 0, e\n\t}\n\n\t// read keyType\n\tbKeyType, e := p.ReadByte()\n\tkeyType = TType(bKeyType)\n\tif e != nil {\n\t\treturn keyType, valueType, size, e\n\t}\n\n\t// read valueType\n\tbValueType, e := p.ReadByte()\n\tvalueType = TType(bValueType)\n\tif e != nil {\n\t\treturn keyType, valueType, size, e\n\t}\n\n\t// read size\n\tiSize, err := p.ReadI64()\n\tsize = int(iSize)\n\treturn keyType, valueType, size, err\n}\n\nfunc (p *TSimpleJSONProtocol) ReadMapEnd() error {\n\treturn p.ParseListEnd()\n}\n\nfunc (p *TSimpleJSONProtocol) ReadListBegin() (elemType TType, size int, e error) {\n\treturn p.ParseElemListBegin()\n}\n\nfunc (p *TSimpleJSONProtocol) ReadListEnd() error {\n\treturn p.ParseListEnd()\n}\n\nfunc (p *TSimpleJSONProtocol) ReadSetBegin() (elemType TType, size int, e error) {\n\treturn p.ParseElemListBegin()\n}\n\nfunc (p *TSimpleJSONProtocol) ReadSetEnd() error {\n\treturn p.ParseListEnd()\n}\n\nfunc (p *TSimpleJSONProtocol) ReadBool() (bool, error) {\n\tvar value bool\n\n\tif err := p.ParsePreValue(); err != nil {\n\t\treturn value, err\n\t}\n\tf, _ := p.reader.Peek(1)\n\tif len(f) > 0 {\n\t\tswitch f[0] {\n\t\tcase JSON_TRUE[0]:\n\t\t\tb := make([]byte, len(JSON_TRUE))\n\t\t\t_, err := p.reader.Read(b)\n\t\t\tif err != nil {\n\t\t\t\treturn false, NewTProtocolException(err)\n\t\t\t}\n\t\t\tif string(b) == string(JSON_TRUE) {\n\t\t\t\tvalue = true\n\t\t\t} else {\n\t\t\t\te := fmt.Errorf(\"Expected \\\"true\\\" but found: %s\", string(b))\n\t\t\t\treturn value, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t\tbreak\n\t\tcase JSON_FALSE[0]:\n\t\t\tb := make([]byte, len(JSON_FALSE))\n\t\t\t_, err := p.reader.Read(b)\n\t\t\tif err != nil {\n\t\t\t\treturn false, NewTProtocolException(err)\n\t\t\t}\n\t\t\tif string(b) == string(JSON_FALSE) {\n\t\t\t\tvalue = false\n\t\t\t} else {\n\t\t\t\te := fmt.Errorf(\"Expected \\\"false\\\" but found: %s\", string(b))\n\t\t\t\treturn value, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t\tbreak\n\t\tcase JSON_NULL[0]:\n\t\t\tb := make([]byte, len(JSON_NULL))\n\t\t\t_, err := p.reader.Read(b)\n\t\t\tif err != nil {\n\t\t\t\treturn false, NewTProtocolException(err)\n\t\t\t}\n\t\t\tif string(b) == string(JSON_NULL) {\n\t\t\t\tvalue = false\n\t\t\t} else {\n\t\t\t\te := fmt.Errorf(\"Expected \\\"null\\\" but found: %s\", string(b))\n\t\t\t\treturn value, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\tdefault:\n\t\t\te := fmt.Errorf(\"Expected \\\"true\\\", \\\"false\\\", or \\\"null\\\" but found: %s\", string(f))\n\t\t\treturn value, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t}\n\t}\n\treturn value, p.ParsePostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) ReadByte() (int8, error) {\n\tv, err := p.ReadI64()\n\treturn int8(v), err\n}\n\nfunc (p *TSimpleJSONProtocol) ReadI16() (int16, error) {\n\tv, err := p.ReadI64()\n\treturn int16(v), err\n}\n\nfunc (p *TSimpleJSONProtocol) ReadI32() (int32, error) {\n\tv, err := p.ReadI64()\n\treturn int32(v), err\n}\n\nfunc (p *TSimpleJSONProtocol) ReadI64() (int64, error) {\n\tv, _, err := p.ParseI64()\n\treturn v, err\n}\n\nfunc (p *TSimpleJSONProtocol) ReadDouble() (float64, error) {\n\tv, _, err := p.ParseF64()\n\treturn v, err\n}\n\nfunc (p *TSimpleJSONProtocol) ReadString() (string, error) {\n\tvar v string\n\tif err := p.ParsePreValue(); err != nil {\n\t\treturn v, err\n\t}\n\tf, _ := p.reader.Peek(1)\n\tif len(f) > 0 && f[0] == JSON_QUOTE {\n\t\tp.reader.ReadByte()\n\t\tvalue, err := p.ParseStringBody()\n\t\tv = value\n\t\tif err != nil {\n\t\t\treturn v, err\n\t\t}\n\t} else if len(f) > 0 && f[0] == JSON_NULL[0] {\n\t\tb := make([]byte, len(JSON_NULL))\n\t\t_, err := p.reader.Read(b)\n\t\tif err != nil {\n\t\t\treturn v, NewTProtocolException(err)\n\t\t}\n\t\tif string(b) != string(JSON_NULL) {\n\t\t\te := fmt.Errorf(\"Expected a JSON string, found unquoted data started with %s\", string(b))\n\t\t\treturn v, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t}\n\t} else {\n\t\te := fmt.Errorf(\"Expected a JSON string, found unquoted data started with %s\", string(f))\n\t\treturn v, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t}\n\treturn v, p.ParsePostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) ReadBinary() ([]byte, error) {\n\tvar v []byte\n\tif err := p.ParsePreValue(); err != nil {\n\t\treturn nil, err\n\t}\n\tf, _ := p.reader.Peek(1)\n\tif len(f) > 0 && f[0] == JSON_QUOTE {\n\t\tp.reader.ReadByte()\n\t\tvalue, err := p.ParseBase64EncodedBody()\n\t\tv = value\n\t\tif err != nil {\n\t\t\treturn v, err\n\t\t}\n\t} else if len(f) > 0 && f[0] == JSON_NULL[0] {\n\t\tb := make([]byte, len(JSON_NULL))\n\t\t_, err := p.reader.Read(b)\n\t\tif err != nil {\n\t\t\treturn v, NewTProtocolException(err)\n\t\t}\n\t\tif string(b) != string(JSON_NULL) {\n\t\t\te := fmt.Errorf(\"Expected a JSON string, found unquoted data started with %s\", string(b))\n\t\t\treturn v, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t}\n\t} else {\n\t\te := fmt.Errorf(\"Expected a JSON string, found unquoted data started with %s\", string(f))\n\t\treturn v, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t}\n\n\treturn v, p.ParsePostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) Flush() (err error) {\n\treturn NewTProtocolException(p.writer.Flush())\n}\n\nfunc (p *TSimpleJSONProtocol) Skip(fieldType TType) (err error) {\n\treturn SkipDefaultDepth(p, fieldType)\n}\n\nfunc (p *TSimpleJSONProtocol) Transport() TTransport {\n\treturn p.trans\n}\n\nfunc (p *TSimpleJSONProtocol) OutputPreValue() error {\n\tcxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1])\n\tswitch cxt {\n\tcase _CONTEXT_IN_LIST, _CONTEXT_IN_OBJECT_NEXT_KEY:\n\t\tif _, e := p.write(JSON_COMMA); e != nil {\n\t\t\treturn NewTProtocolException(e)\n\t\t}\n\t\tbreak\n\tcase _CONTEXT_IN_OBJECT_NEXT_VALUE:\n\t\tif _, e := p.write(JSON_COLON); e != nil {\n\t\t\treturn NewTProtocolException(e)\n\t\t}\n\t\tbreak\n\t}\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) OutputPostValue() error {\n\tcxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1])\n\tswitch cxt {\n\tcase _CONTEXT_IN_LIST_FIRST:\n\t\tp.dumpContext = p.dumpContext[:len(p.dumpContext)-1]\n\t\tp.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST))\n\t\tbreak\n\tcase _CONTEXT_IN_OBJECT_FIRST:\n\t\tp.dumpContext = p.dumpContext[:len(p.dumpContext)-1]\n\t\tp.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE))\n\t\tbreak\n\tcase _CONTEXT_IN_OBJECT_NEXT_KEY:\n\t\tp.dumpContext = p.dumpContext[:len(p.dumpContext)-1]\n\t\tp.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE))\n\t\tbreak\n\tcase _CONTEXT_IN_OBJECT_NEXT_VALUE:\n\t\tp.dumpContext = p.dumpContext[:len(p.dumpContext)-1]\n\t\tp.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_KEY))\n\t\tbreak\n\t}\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) OutputBool(value bool) error {\n\tif e := p.OutputPreValue(); e != nil {\n\t\treturn e\n\t}\n\tvar v string\n\tif value {\n\t\tv = string(JSON_TRUE)\n\t} else {\n\t\tv = string(JSON_FALSE)\n\t}\n\tswitch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) {\n\tcase _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY:\n\t\tv = jsonQuote(v)\n\tdefault:\n\t}\n\tif e := p.OutputStringData(v); e != nil {\n\t\treturn e\n\t}\n\treturn p.OutputPostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) OutputNull() error {\n\tif e := p.OutputPreValue(); e != nil {\n\t\treturn e\n\t}\n\tif _, e := p.write(JSON_NULL); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\treturn p.OutputPostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) OutputF64(value float64) error {\n\tif e := p.OutputPreValue(); e != nil {\n\t\treturn e\n\t}\n\tvar v string\n\tif math.IsNaN(value) {\n\t\tv = string(JSON_QUOTE) + JSON_NAN + string(JSON_QUOTE)\n\t} else if math.IsInf(value, 1) {\n\t\tv = string(JSON_QUOTE) + JSON_INFINITY + string(JSON_QUOTE)\n\t} else if math.IsInf(value, -1) {\n\t\tv = string(JSON_QUOTE) + JSON_NEGATIVE_INFINITY + string(JSON_QUOTE)\n\t} else {\n\t\tv = strconv.FormatFloat(value, 'g', -1, 64)\n\t\tswitch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) {\n\t\tcase _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY:\n\t\t\tv = string(JSON_QUOTE) + v + string(JSON_QUOTE)\n\t\tdefault:\n\t\t}\n\t}\n\tif e := p.OutputStringData(v); e != nil {\n\t\treturn e\n\t}\n\treturn p.OutputPostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) OutputI64(value int64) error {\n\tif e := p.OutputPreValue(); e != nil {\n\t\treturn e\n\t}\n\tv := strconv.FormatInt(value, 10)\n\tswitch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) {\n\tcase _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY:\n\t\tv = jsonQuote(v)\n\tdefault:\n\t}\n\tif e := p.OutputStringData(v); e != nil {\n\t\treturn e\n\t}\n\treturn p.OutputPostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) OutputString(s string) error {\n\tif e := p.OutputPreValue(); e != nil {\n\t\treturn e\n\t}\n\tif e := p.OutputStringData(jsonQuote(s)); e != nil {\n\t\treturn e\n\t}\n\treturn p.OutputPostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) OutputStringData(s string) error {\n\t_, e := p.write([]byte(s))\n\treturn NewTProtocolException(e)\n}\n\nfunc (p *TSimpleJSONProtocol) OutputObjectBegin() error {\n\tif e := p.OutputPreValue(); e != nil {\n\t\treturn e\n\t}\n\tif _, e := p.write(JSON_LBRACE); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\tp.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_FIRST))\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) OutputObjectEnd() error {\n\tif _, e := p.write(JSON_RBRACE); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\tp.dumpContext = p.dumpContext[:len(p.dumpContext)-1]\n\tif e := p.OutputPostValue(); e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) OutputListBegin() error {\n\tif e := p.OutputPreValue(); e != nil {\n\t\treturn e\n\t}\n\tif _, e := p.write(JSON_LBRACKET); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\tp.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST_FIRST))\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) OutputListEnd() error {\n\tif _, e := p.write(JSON_RBRACKET); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\tp.dumpContext = p.dumpContext[:len(p.dumpContext)-1]\n\tif e := p.OutputPostValue(); e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) OutputElemListBegin(elemType TType, size int) error {\n\tif e := p.OutputListBegin(); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteByte(int8(elemType)); e != nil {\n\t\treturn e\n\t}\n\tif e := p.WriteI64(int64(size)); e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) ParsePreValue() error {\n\tif e := p.readNonSignificantWhitespace(); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\tcxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1])\n\tb, _ := p.reader.Peek(1)\n\tswitch cxt {\n\tcase _CONTEXT_IN_LIST:\n\t\tif len(b) > 0 {\n\t\t\tswitch b[0] {\n\t\t\tcase JSON_RBRACKET[0]:\n\t\t\t\treturn nil\n\t\t\tcase JSON_COMMA[0]:\n\t\t\t\tp.reader.ReadByte()\n\t\t\t\tif e := p.readNonSignificantWhitespace(); e != nil {\n\t\t\t\t\treturn NewTProtocolException(e)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\tdefault:\n\t\t\t\te := fmt.Errorf(\"Expected \\\"]\\\" or \\\",\\\" in list context, but found \\\"%s\\\"\", string(b))\n\t\t\t\treturn NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t}\n\t\tbreak\n\tcase _CONTEXT_IN_OBJECT_NEXT_KEY:\n\t\tif len(b) > 0 {\n\t\t\tswitch b[0] {\n\t\t\tcase JSON_RBRACE[0]:\n\t\t\t\treturn nil\n\t\t\tcase JSON_COMMA[0]:\n\t\t\t\tp.reader.ReadByte()\n\t\t\t\tif e := p.readNonSignificantWhitespace(); e != nil {\n\t\t\t\t\treturn NewTProtocolException(e)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\tdefault:\n\t\t\t\te := fmt.Errorf(\"Expected \\\"}\\\" or \\\",\\\" in object context, but found \\\"%s\\\"\", string(b))\n\t\t\t\treturn NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t}\n\t\tbreak\n\tcase _CONTEXT_IN_OBJECT_NEXT_VALUE:\n\t\tif len(b) > 0 {\n\t\t\tswitch b[0] {\n\t\t\tcase JSON_COLON[0]:\n\t\t\t\tp.reader.ReadByte()\n\t\t\t\tif e := p.readNonSignificantWhitespace(); e != nil {\n\t\t\t\t\treturn NewTProtocolException(e)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\tdefault:\n\t\t\t\te := fmt.Errorf(\"Expected \\\":\\\" in object context, but found \\\"%s\\\"\", string(b))\n\t\t\t\treturn NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t}\n\t\tbreak\n\t}\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) ParsePostValue() error {\n\tif e := p.readNonSignificantWhitespace(); e != nil {\n\t\treturn NewTProtocolException(e)\n\t}\n\tcxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1])\n\tswitch cxt {\n\tcase _CONTEXT_IN_LIST_FIRST:\n\t\tp.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1]\n\t\tp.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST))\n\t\tbreak\n\tcase _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY:\n\t\tp.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1]\n\t\tp.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_VALUE))\n\t\tbreak\n\tcase _CONTEXT_IN_OBJECT_NEXT_VALUE:\n\t\tp.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1]\n\t\tp.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_KEY))\n\t\tbreak\n\t}\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) readNonSignificantWhitespace() error {\n\tfor {\n\t\tb, _ := p.reader.Peek(1)\n\t\tif len(b) < 1 {\n\t\t\treturn nil\n\t\t}\n\t\tswitch b[0] {\n\t\tcase ' ', '\\r', '\\n', '\\t':\n\t\t\tp.reader.ReadByte()\n\t\t\tcontinue\n\t\tdefault:\n\t\t\tbreak\n\t\t}\n\t\tbreak\n\t}\n\treturn nil\n}\n\nfunc (p *TSimpleJSONProtocol) ParseStringBody() (string, error) {\n\tline, err := p.reader.ReadString(JSON_QUOTE)\n\tif err != nil {\n\t\treturn \"\", NewTProtocolException(err)\n\t}\n\tl := len(line)\n\t// count number of escapes to see if we need to keep going\n\ti := 1\n\tfor ; i < l; i++ {\n\t\tif line[l-i-1] != '\\\\' {\n\t\t\tbreak\n\t\t}\n\t}\n\tif i&0x01 == 1 {\n\t\tv, ok := jsonUnquote(string(JSON_QUOTE) + line)\n\t\tif !ok {\n\t\t\treturn \"\", NewTProtocolException(err)\n\t\t}\n\t\treturn v, nil\n\t}\n\ts, err := p.ParseQuotedStringBody()\n\tif err != nil {\n\t\treturn \"\", NewTProtocolException(err)\n\t}\n\tstr := string(JSON_QUOTE) + line + s\n\tv, ok := jsonUnquote(str)\n\tif !ok {\n\t\te := fmt.Errorf(\"Unable to parse as JSON string %s\", str)\n\t\treturn \"\", NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t}\n\treturn v, nil\n}\n\nfunc (p *TSimpleJSONProtocol) ParseQuotedStringBody() (string, error) {\n\tline, err := p.reader.ReadString(JSON_QUOTE)\n\tif err != nil {\n\t\treturn \"\", NewTProtocolException(err)\n\t}\n\tl := len(line)\n\t// count number of escapes to see if we need to keep going\n\ti := 1\n\tfor ; i < l; i++ {\n\t\tif line[l-i-1] != '\\\\' {\n\t\t\tbreak\n\t\t}\n\t}\n\tif i&0x01 == 1 {\n\t\treturn line, nil\n\t}\n\ts, err := p.ParseQuotedStringBody()\n\tif err != nil {\n\t\treturn \"\", NewTProtocolException(err)\n\t}\n\tv := line + s\n\treturn v, nil\n}\n\nfunc (p *TSimpleJSONProtocol) ParseBase64EncodedBody() ([]byte, error) {\n\tline, err := p.reader.ReadBytes(JSON_QUOTE)\n\tif err != nil {\n\t\treturn line, NewTProtocolException(err)\n\t}\n\tline2 := line[0 : len(line)-1]\n\tl := len(line2)\n\tif (l % 4) != 0 {\n\t\tpad := 4 - (l % 4)\n\t\tfill := [...]byte{'=', '=', '='}\n\t\tline2 = append(line2, fill[:pad]...)\n\t\tl = len(line2)\n\t}\n\toutput := make([]byte, base64.StdEncoding.DecodedLen(l))\n\tn, err := base64.StdEncoding.Decode(output, line2)\n\treturn output[0:n], NewTProtocolException(err)\n}\n\nfunc (p *TSimpleJSONProtocol) ParseI64() (int64, bool, error) {\n\tif err := p.ParsePreValue(); err != nil {\n\t\treturn 0, false, err\n\t}\n\tvar value int64\n\tvar isnull bool\n\tif p.safePeekContains(JSON_NULL) {\n\t\tp.reader.Read(make([]byte, len(JSON_NULL)))\n\t\tisnull = true\n\t} else {\n\t\tnum, err := p.readNumeric()\n\t\tisnull = (num == nil)\n\t\tif !isnull {\n\t\t\tvalue = num.Int64()\n\t\t}\n\t\tif err != nil {\n\t\t\treturn value, isnull, err\n\t\t}\n\t}\n\treturn value, isnull, p.ParsePostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) ParseF64() (float64, bool, error) {\n\tif err := p.ParsePreValue(); err != nil {\n\t\treturn 0, false, err\n\t}\n\tvar value float64\n\tvar isnull bool\n\tif p.safePeekContains(JSON_NULL) {\n\t\tp.reader.Read(make([]byte, len(JSON_NULL)))\n\t\tisnull = true\n\t} else {\n\t\tnum, err := p.readNumeric()\n\t\tisnull = (num == nil)\n\t\tif !isnull {\n\t\t\tvalue = num.Float64()\n\t\t}\n\t\tif err != nil {\n\t\t\treturn value, isnull, err\n\t\t}\n\t}\n\treturn value, isnull, p.ParsePostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) ParseObjectStart() (bool, error) {\n\tif err := p.ParsePreValue(); err != nil {\n\t\treturn false, err\n\t}\n\tvar b []byte\n\tb, err := p.reader.Peek(1)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif len(b) > 0 && b[0] == JSON_LBRACE[0] {\n\t\tp.reader.ReadByte()\n\t\tp.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_FIRST))\n\t\treturn false, nil\n\t} else if p.safePeekContains(JSON_NULL) {\n\t\treturn true, nil\n\t}\n\te := fmt.Errorf(\"Expected '{' or null, but found '%s'\", string(b))\n\treturn false, NewTProtocolExceptionWithType(INVALID_DATA, e)\n}\n\nfunc (p *TSimpleJSONProtocol) ParseObjectEnd() error {\n\tif isNull, err := p.readIfNull(); isNull || err != nil {\n\t\treturn err\n\t}\n\tcxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1])\n\tif (cxt != _CONTEXT_IN_OBJECT_FIRST) && (cxt != _CONTEXT_IN_OBJECT_NEXT_KEY) {\n\t\te := fmt.Errorf(\"Expected to be in the Object Context, but not in Object Context (%d)\", cxt)\n\t\treturn NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t}\n\tline, err := p.reader.ReadString(JSON_RBRACE[0])\n\tif err != nil {\n\t\treturn NewTProtocolException(err)\n\t}\n\tfor _, char := range line {\n\t\tswitch char {\n\t\tdefault:\n\t\t\te := fmt.Errorf(\"Expecting end of object \\\"}\\\", but found: \\\"%s\\\"\", line)\n\t\t\treturn NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\tcase ' ', '\\n', '\\r', '\\t', '}':\n\t\t\tbreak\n\t\t}\n\t}\n\tp.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1]\n\treturn p.ParsePostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) ParseListBegin() (isNull bool, err error) {\n\tif e := p.ParsePreValue(); e != nil {\n\t\treturn false, e\n\t}\n\tvar b []byte\n\tb, err = p.reader.Peek(1)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif len(b) >= 1 && b[0] == JSON_LBRACKET[0] {\n\t\tp.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST_FIRST))\n\t\tp.reader.ReadByte()\n\t\tisNull = false\n\t} else if p.safePeekContains(JSON_NULL) {\n\t\tisNull = true\n\t} else {\n\t\terr = fmt.Errorf(\"Expected \\\"null\\\" or \\\"[\\\", received %q\", b)\n\t}\n\treturn isNull, NewTProtocolExceptionWithType(INVALID_DATA, err)\n}\n\nfunc (p *TSimpleJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) {\n\tif isNull, e := p.ParseListBegin(); isNull || e != nil {\n\t\treturn VOID, 0, e\n\t}\n\tbElemType, err := p.ReadByte()\n\telemType = TType(bElemType)\n\tif err != nil {\n\t\treturn elemType, size, err\n\t}\n\tnSize, err2 := p.ReadI64()\n\tsize = int(nSize)\n\treturn elemType, size, err2\n}\n\nfunc (p *TSimpleJSONProtocol) ParseListEnd() error {\n\tif isNull, err := p.readIfNull(); isNull || err != nil {\n\t\treturn err\n\t}\n\tcxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1])\n\tif cxt != _CONTEXT_IN_LIST {\n\t\te := fmt.Errorf(\"Expected to be in the List Context, but not in List Context (%d)\", cxt)\n\t\treturn NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t}\n\tline, err := p.reader.ReadString(JSON_RBRACKET[0])\n\tif err != nil {\n\t\treturn NewTProtocolException(err)\n\t}\n\tfor _, char := range line {\n\t\tswitch char {\n\t\tdefault:\n\t\t\te := fmt.Errorf(\"Expecting end of list \\\"]\\\", but found: \\\"\", line, \"\\\"\")\n\t\t\treturn NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\tcase ' ', '\\n', '\\r', '\\t', rune(JSON_RBRACKET[0]):\n\t\t\tbreak\n\t\t}\n\t}\n\tp.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1]\n\tif _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) == _CONTEXT_IN_TOPLEVEL {\n\t\treturn nil\n\t}\n\treturn p.ParsePostValue()\n}\n\nfunc (p *TSimpleJSONProtocol) readSingleValue() (interface{}, TType, error) {\n\te := p.readNonSignificantWhitespace()\n\tif e != nil {\n\t\treturn nil, VOID, NewTProtocolException(e)\n\t}\n\tb, e := p.reader.Peek(1)\n\tif len(b) > 0 {\n\t\tc := b[0]\n\t\tswitch c {\n\t\tcase JSON_NULL[0]:\n\t\t\tbuf := make([]byte, len(JSON_NULL))\n\t\t\t_, e := p.reader.Read(buf)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, VOID, NewTProtocolException(e)\n\t\t\t}\n\t\t\tif string(JSON_NULL) != string(buf) {\n\t\t\t\te = mismatch(string(JSON_NULL), string(buf))\n\t\t\t\treturn nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t\treturn nil, VOID, nil\n\t\tcase JSON_QUOTE:\n\t\t\tp.reader.ReadByte()\n\t\t\tv, e := p.ParseStringBody()\n\t\t\tif e != nil {\n\t\t\t\treturn v, UTF8, NewTProtocolException(e)\n\t\t\t}\n\t\t\tif v == JSON_INFINITY {\n\t\t\t\treturn INFINITY, DOUBLE, nil\n\t\t\t} else if v == JSON_NEGATIVE_INFINITY {\n\t\t\t\treturn NEGATIVE_INFINITY, DOUBLE, nil\n\t\t\t} else if v == JSON_NAN {\n\t\t\t\treturn NAN, DOUBLE, nil\n\t\t\t}\n\t\t\treturn v, UTF8, nil\n\t\tcase JSON_TRUE[0]:\n\t\t\tbuf := make([]byte, len(JSON_TRUE))\n\t\t\t_, e := p.reader.Read(buf)\n\t\t\tif e != nil {\n\t\t\t\treturn true, BOOL, NewTProtocolException(e)\n\t\t\t}\n\t\t\tif string(JSON_TRUE) != string(buf) {\n\t\t\t\te := mismatch(string(JSON_TRUE), string(buf))\n\t\t\t\treturn true, BOOL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t\treturn true, BOOL, nil\n\t\tcase JSON_FALSE[0]:\n\t\t\tbuf := make([]byte, len(JSON_FALSE))\n\t\t\t_, e := p.reader.Read(buf)\n\t\t\tif e != nil {\n\t\t\t\treturn false, BOOL, NewTProtocolException(e)\n\t\t\t}\n\t\t\tif string(JSON_FALSE) != string(buf) {\n\t\t\t\te := mismatch(string(JSON_FALSE), string(buf))\n\t\t\t\treturn false, BOOL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t\treturn false, BOOL, nil\n\t\tcase JSON_LBRACKET[0]:\n\t\t\t_, e := p.reader.ReadByte()\n\t\t\treturn make([]interface{}, 0), LIST, NewTProtocolException(e)\n\t\tcase JSON_LBRACE[0]:\n\t\t\t_, e := p.reader.ReadByte()\n\t\t\treturn make(map[string]interface{}), STRUCT, NewTProtocolException(e)\n\t\tcase '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', 'E', '.', '+', '-', JSON_INFINITY[0], JSON_NAN[0]:\n\t\t\t// assume numeric\n\t\t\tv, e := p.readNumeric()\n\t\t\treturn v, DOUBLE, e\n\t\tdefault:\n\t\t\te := fmt.Errorf(\"Expected element in list but found '%s' while parsing JSON.\", string(c))\n\t\t\treturn nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t}\n\t}\n\te = fmt.Errorf(\"Cannot read a single element while parsing JSON.\")\n\treturn nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\n}\n\nfunc (p *TSimpleJSONProtocol) readIfNull() (bool, error) {\n\tcont := true\n\tfor cont {\n\t\tb, _ := p.reader.Peek(1)\n\t\tif len(b) < 1 {\n\t\t\treturn false, nil\n\t\t}\n\t\tswitch b[0] {\n\t\tdefault:\n\t\t\treturn false, nil\n\t\tcase JSON_NULL[0]:\n\t\t\tcont = false\n\t\t\tbreak\n\t\tcase ' ', '\\n', '\\r', '\\t':\n\t\t\tp.reader.ReadByte()\n\t\t\tbreak\n\t\t}\n\t}\n\tif p.safePeekContains(JSON_NULL) {\n\t\tp.reader.Read(make([]byte, len(JSON_NULL)))\n\t\treturn true, nil\n\t}\n\treturn false, nil\n}\n\nfunc (p *TSimpleJSONProtocol) readQuoteIfNext() {\n\tb, _ := p.reader.Peek(1)\n\tif len(b) > 0 && b[0] == JSON_QUOTE {\n\t\tp.reader.ReadByte()\n\t}\n}\n\nfunc (p *TSimpleJSONProtocol) readNumeric() (Numeric, error) {\n\tisNull, err := p.readIfNull()\n\tif isNull || err != nil {\n\t\treturn NUMERIC_NULL, err\n\t}\n\thasDecimalPoint := false\n\tnextCanBeSign := true\n\thasE := false\n\tMAX_LEN := 40\n\tbuf := bytes.NewBuffer(make([]byte, 0, MAX_LEN))\n\tcontinueFor := true\n\tinQuotes := false\n\tfor continueFor {\n\t\tc, err := p.reader.ReadByte()\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn NUMERIC_NULL, NewTProtocolException(err)\n\t\t}\n\t\tswitch c {\n\t\tcase '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':\n\t\t\tbuf.WriteByte(c)\n\t\t\tnextCanBeSign = false\n\t\tcase '.':\n\t\t\tif hasDecimalPoint {\n\t\t\t\te := fmt.Errorf(\"Unable to parse number with multiple decimal points '%s.'\", buf.String())\n\t\t\t\treturn NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t\tif hasE {\n\t\t\t\te := fmt.Errorf(\"Unable to parse number with decimal points in the exponent '%s.'\", buf.String())\n\t\t\t\treturn NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t\tbuf.WriteByte(c)\n\t\t\thasDecimalPoint, nextCanBeSign = true, false\n\t\tcase 'e', 'E':\n\t\t\tif hasE {\n\t\t\t\te := fmt.Errorf(\"Unable to parse number with multiple exponents '%s%c'\", buf.String(), c)\n\t\t\t\treturn NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t\tbuf.WriteByte(c)\n\t\t\thasE, nextCanBeSign = true, true\n\t\tcase '-', '+':\n\t\t\tif !nextCanBeSign {\n\t\t\t\te := fmt.Errorf(\"Negative sign within number\")\n\t\t\t\treturn NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\t\tbuf.WriteByte(c)\n\t\t\tnextCanBeSign = false\n\t\tcase ' ', 0, '\\t', '\\n', '\\r', JSON_RBRACE[0], JSON_RBRACKET[0], JSON_COMMA[0], JSON_COLON[0]:\n\t\t\tp.reader.UnreadByte()\n\t\t\tcontinueFor = false\n\t\tcase JSON_NAN[0]:\n\t\t\tif buf.Len() == 0 {\n\t\t\t\tbuffer := make([]byte, len(JSON_NAN))\n\t\t\t\tbuffer[0] = c\n\t\t\t\t_, e := p.reader.Read(buffer[1:])\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn NUMERIC_NULL, NewTProtocolException(e)\n\t\t\t\t}\n\t\t\t\tif JSON_NAN != string(buffer) {\n\t\t\t\t\te := mismatch(JSON_NAN, string(buffer))\n\t\t\t\t\treturn NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t\t}\n\t\t\t\tif inQuotes {\n\t\t\t\t\tp.readQuoteIfNext()\n\t\t\t\t}\n\t\t\t\treturn NAN, nil\n\t\t\t} else {\n\t\t\t\te := fmt.Errorf(\"Unable to parse number starting with character '%c'\", c)\n\t\t\t\treturn NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\tcase JSON_INFINITY[0]:\n\t\t\tif buf.Len() == 0 || (buf.Len() == 1 && buf.Bytes()[0] == '+') {\n\t\t\t\tbuffer := make([]byte, len(JSON_INFINITY))\n\t\t\t\tbuffer[0] = c\n\t\t\t\t_, e := p.reader.Read(buffer[1:])\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn NUMERIC_NULL, NewTProtocolException(e)\n\t\t\t\t}\n\t\t\t\tif JSON_INFINITY != string(buffer) {\n\t\t\t\t\te := mismatch(JSON_INFINITY, string(buffer))\n\t\t\t\t\treturn NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t\t}\n\t\t\t\tif inQuotes {\n\t\t\t\t\tp.readQuoteIfNext()\n\t\t\t\t}\n\t\t\t\treturn INFINITY, nil\n\t\t\t} else if buf.Len() == 1 && buf.Bytes()[0] == JSON_NEGATIVE_INFINITY[0] {\n\t\t\t\tbuffer := make([]byte, len(JSON_NEGATIVE_INFINITY))\n\t\t\t\tbuffer[0] = JSON_NEGATIVE_INFINITY[0]\n\t\t\t\tbuffer[1] = c\n\t\t\t\t_, e := p.reader.Read(buffer[2:])\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn NUMERIC_NULL, NewTProtocolException(e)\n\t\t\t\t}\n\t\t\t\tif JSON_NEGATIVE_INFINITY != string(buffer) {\n\t\t\t\t\te := mismatch(JSON_NEGATIVE_INFINITY, string(buffer))\n\t\t\t\t\treturn NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t\t}\n\t\t\t\tif inQuotes {\n\t\t\t\t\tp.readQuoteIfNext()\n\t\t\t\t}\n\t\t\t\treturn NEGATIVE_INFINITY, nil\n\t\t\t} else {\n\t\t\t\te := fmt.Errorf(\"Unable to parse number starting with character '%c' due to existing buffer %s\", c, buf.String())\n\t\t\t\treturn NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t\t}\n\t\tcase JSON_QUOTE:\n\t\t\tif !inQuotes {\n\t\t\t\tinQuotes = true\n\t\t\t} else {\n\t\t\t\tbreak\n\t\t\t}\n\t\tdefault:\n\t\t\te := fmt.Errorf(\"Unable to parse number starting with character '%c'\", c)\n\t\t\treturn NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t\t}\n\t}\n\tif buf.Len() == 0 {\n\t\te := fmt.Errorf(\"Unable to parse number from empty string ''\")\n\t\treturn NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e)\n\t}\n\treturn NewNumericFromJSONString(buf.String(), false), nil\n}\n\n// Safely peeks into the buffer, reading only what is necessary\nfunc (p *TSimpleJSONProtocol) safePeekContains(b []byte) bool {\n\tfor i := 0; i < len(b); i++ {\n\t\ta, _ := p.reader.Peek(i + 1)\n\t\tif len(a) == 0 || a[i] != b[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Reset the context stack to its initial state.\nfunc (p *TSimpleJSONProtocol) resetContextStack() {\n\tp.parseContextStack = []int{int(_CONTEXT_IN_TOPLEVEL)}\n\tp.dumpContext = []int{int(_CONTEXT_IN_TOPLEVEL)}\n}\n\nfunc (p *TSimpleJSONProtocol) write(b []byte) (int, error) {\n\tn, err := p.writer.Write(b)\n\tif err != nil {\n\t\tp.writer.Reset(p.trans) // THRIFT-3735\n\t}\n\treturn n, err\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/simple_json_protocol_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestWriteSimpleJSONProtocolBool(t *testing.T) {\n\tthetype := \"boolean\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\tfor _, value := range BOOL_VALUES {\n\t\tif e := p.WriteBool(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif s != fmt.Sprint(value) {\n\t\t\tt.Fatalf(\"Bad value for %s %v: %s\", thetype, value, s)\n\t\t}\n\t\tv := false\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadSimpleJSONProtocolBool(t *testing.T) {\n\tthetype := \"boolean\"\n\tfor _, value := range BOOL_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTSimpleJSONProtocol(trans)\n\t\tif value {\n\t\t\ttrans.Write(JSON_TRUE)\n\t\t} else {\n\t\t\ttrans.Write(JSON_FALSE)\n\t\t}\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadBool()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestWriteSimpleJSONProtocolByte(t *testing.T) {\n\tthetype := \"byte\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\tfor _, value := range BYTE_VALUES {\n\t\tif e := p.WriteByte(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif s != fmt.Sprint(value) {\n\t\t\tt.Fatalf(\"Bad value for %s %v: %s\", thetype, value, s)\n\t\t}\n\t\tv := int8(0)\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadSimpleJSONProtocolByte(t *testing.T) {\n\tthetype := \"byte\"\n\tfor _, value := range BYTE_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTSimpleJSONProtocol(trans)\n\t\ttrans.WriteString(strconv.Itoa(int(value)))\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadByte()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestWriteSimpleJSONProtocolI16(t *testing.T) {\n\tthetype := \"int16\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\tfor _, value := range INT16_VALUES {\n\t\tif e := p.WriteI16(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif s != fmt.Sprint(value) {\n\t\t\tt.Fatalf(\"Bad value for %s %v: %s\", thetype, value, s)\n\t\t}\n\t\tv := int16(0)\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadSimpleJSONProtocolI16(t *testing.T) {\n\tthetype := \"int16\"\n\tfor _, value := range INT16_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTSimpleJSONProtocol(trans)\n\t\ttrans.WriteString(strconv.Itoa(int(value)))\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadI16()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestWriteSimpleJSONProtocolI32(t *testing.T) {\n\tthetype := \"int32\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\tfor _, value := range INT32_VALUES {\n\t\tif e := p.WriteI32(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif s != fmt.Sprint(value) {\n\t\t\tt.Fatalf(\"Bad value for %s %v: %s\", thetype, value, s)\n\t\t}\n\t\tv := int32(0)\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadSimpleJSONProtocolI32(t *testing.T) {\n\tthetype := \"int32\"\n\tfor _, value := range INT32_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTSimpleJSONProtocol(trans)\n\t\ttrans.WriteString(strconv.Itoa(int(value)))\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadI32()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestReadSimpleJSONProtocolI32Null(t *testing.T) {\n\tthetype := \"int32\"\n\tvalue := \"null\"\n\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\ttrans.WriteString(value)\n\ttrans.Flush()\n\ts := trans.String()\n\tv, e := p.ReadI32()\n\n\tif e != nil {\n\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t}\n\tif v != 0 {\n\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t}\n\ttrans.Reset()\n\ttrans.Close()\n}\n\nfunc TestWriteSimpleJSONProtocolI64(t *testing.T) {\n\tthetype := \"int64\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\tfor _, value := range INT64_VALUES {\n\t\tif e := p.WriteI64(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif s != fmt.Sprint(value) {\n\t\t\tt.Fatalf(\"Bad value for %s %v: %s\", thetype, value, s)\n\t\t}\n\t\tv := int64(0)\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadSimpleJSONProtocolI64(t *testing.T) {\n\tthetype := \"int64\"\n\tfor _, value := range INT64_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTSimpleJSONProtocol(trans)\n\t\ttrans.WriteString(strconv.FormatInt(value, 10))\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadI64()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestReadSimpleJSONProtocolI64Null(t *testing.T) {\n\tthetype := \"int32\"\n\tvalue := \"null\"\n\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\ttrans.WriteString(value)\n\ttrans.Flush()\n\ts := trans.String()\n\tv, e := p.ReadI64()\n\n\tif e != nil {\n\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t}\n\tif v != 0 {\n\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t}\n\ttrans.Reset()\n\ttrans.Close()\n}\n\nfunc TestWriteSimpleJSONProtocolDouble(t *testing.T) {\n\tthetype := \"double\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\tfor _, value := range DOUBLE_VALUES {\n\t\tif e := p.WriteDouble(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif math.IsInf(value, 1) {\n\t\t\tif s != jsonQuote(JSON_INFINITY) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, expected: %v\", thetype, value, s, jsonQuote(JSON_INFINITY))\n\t\t\t}\n\t\t} else if math.IsInf(value, -1) {\n\t\t\tif s != jsonQuote(JSON_NEGATIVE_INFINITY) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, expected: %v\", thetype, value, s, jsonQuote(JSON_NEGATIVE_INFINITY))\n\t\t\t}\n\t\t} else if math.IsNaN(value) {\n\t\t\tif s != jsonQuote(JSON_NAN) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, expected: %v\", thetype, value, s, jsonQuote(JSON_NAN))\n\t\t\t}\n\t\t} else {\n\t\t\tif s != fmt.Sprint(value) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v: %s\", thetype, value, s)\n\t\t\t}\n\t\t\tv := float64(0)\n\t\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t\t}\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadSimpleJSONProtocolDouble(t *testing.T) {\n\tthetype := \"double\"\n\tfor _, value := range DOUBLE_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTSimpleJSONProtocol(trans)\n\t\tn := NewNumericFromDouble(value)\n\t\ttrans.WriteString(n.String())\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadDouble()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif math.IsInf(value, 1) {\n\t\t\tif !math.IsInf(v, 1) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t\t}\n\t\t} else if math.IsInf(value, -1) {\n\t\t\tif !math.IsInf(v, -1) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t\t}\n\t\t} else if math.IsNaN(value) {\n\t\t\tif !math.IsNaN(v) {\n\t\t\t\tt.Fatalf(\"Bad value for %s %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t\t}\n\t\t} else {\n\t\t\tif v != value {\n\t\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t\t}\n\t\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t\t}\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\n\nfunc TestWriteSimpleJSONProtocolString(t *testing.T) {\n\tthetype := \"string\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\tfor _, value := range STRING_VALUES {\n\t\tif e := p.WriteString(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif e := p.Flush(); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t\t}\n\t\ts := trans.String()\n\t\tif s[0] != '\"' || s[len(s)-1] != '\"' {\n\t\t\tt.Fatalf(\"Bad value for %s '%v', wrote '%v', expected: %v\", thetype, value, s, fmt.Sprint(\"\\\"\", value, \"\\\"\"))\n\t\t}\n\t\tv := new(string)\n\t\tif err := json.Unmarshal([]byte(s), v); err != nil || *v != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, *v)\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadSimpleJSONProtocolString(t *testing.T) {\n\tthetype := \"string\"\n\tfor _, value := range STRING_VALUES {\n\t\ttrans := NewTMemoryBuffer()\n\t\tp := NewTSimpleJSONProtocol(trans)\n\t\ttrans.WriteString(jsonQuote(value))\n\t\ttrans.Flush()\n\t\ts := trans.String()\n\t\tv, e := p.ReadString()\n\t\tif e != nil {\n\t\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t\tif v != value {\n\t\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t\t}\n\t\tv1 := new(string)\n\t\tif err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != value {\n\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, *v1)\n\t\t}\n\t\ttrans.Reset()\n\t\ttrans.Close()\n\t}\n}\nfunc TestReadSimpleJSONProtocolStringNull(t *testing.T) {\n\tthetype := \"string\"\n\tvalue := \"null\"\n\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\ttrans.WriteString(value)\n\ttrans.Flush()\n\ts := trans.String()\n\tv, e := p.ReadString()\n\tif e != nil {\n\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t}\n\tif v != \"\" {\n\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t}\n\ttrans.Reset()\n\ttrans.Close()\n}\n\nfunc TestWriteSimpleJSONProtocolBinary(t *testing.T) {\n\tthetype := \"binary\"\n\tvalue := protocol_bdata\n\tb64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata)))\n\tbase64.StdEncoding.Encode(b64value, value)\n\tb64String := string(b64value)\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\tif e := p.WriteBinary(value); e != nil {\n\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t}\n\tif e := p.Flush(); e != nil {\n\t\tt.Fatalf(\"Unable to write %s value %v due to error flushing: %s\", thetype, value, e.Error())\n\t}\n\ts := trans.String()\n\tif s != fmt.Sprint(\"\\\"\", b64String, \"\\\"\") {\n\t\tt.Fatalf(\"Bad value for %s %v\\n  wrote: %v\\nexpected: %v\", thetype, value, s, \"\\\"\"+b64String+\"\\\"\")\n\t}\n\tv1 := new(string)\n\tif err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String {\n\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, *v1)\n\t}\n\ttrans.Close()\n}\n\nfunc TestReadSimpleJSONProtocolBinary(t *testing.T) {\n\tthetype := \"binary\"\n\tvalue := protocol_bdata\n\tb64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata)))\n\tbase64.StdEncoding.Encode(b64value, value)\n\tb64String := string(b64value)\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\ttrans.WriteString(jsonQuote(b64String))\n\ttrans.Flush()\n\ts := trans.String()\n\tv, e := p.ReadBinary()\n\tif e != nil {\n\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t}\n\tif len(v) != len(value) {\n\t\tt.Fatalf(\"Bad value for %s value length %v, wrote: %v, received length: %v\", thetype, len(value), s, len(v))\n\t}\n\tfor i := 0; i < len(v); i++ {\n\t\tif v[i] != value[i] {\n\t\t\tt.Fatalf(\"Bad value for %s at index %d value %v, wrote: %v, received: %v\", thetype, i, value[i], s, v[i])\n\t\t}\n\t}\n\tv1 := new(string)\n\tif err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String {\n\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, *v1)\n\t}\n\ttrans.Reset()\n\ttrans.Close()\n}\n\nfunc TestReadSimpleJSONProtocolBinaryNull(t *testing.T) {\n\tthetype := \"binary\"\n\tvalue := \"null\"\n\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\ttrans.WriteString(value)\n\ttrans.Flush()\n\ts := trans.String()\n\tb, e := p.ReadBinary()\n\tv := string(b)\n\n\tif e != nil {\n\t\tt.Fatalf(\"Unable to read %s value %v due to error: %s\", thetype, value, e.Error())\n\t}\n\tif v != \"\" {\n\t\tt.Fatalf(\"Bad value for %s value %v, wrote: %v, received: %v\", thetype, value, s, v)\n\t}\n\ttrans.Reset()\n\ttrans.Close()\n}\n\nfunc TestWriteSimpleJSONProtocolList(t *testing.T) {\n\tthetype := \"list\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\tp.WriteListBegin(TType(DOUBLE), len(DOUBLE_VALUES))\n\tfor _, value := range DOUBLE_VALUES {\n\t\tif e := p.WriteDouble(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t}\n\tp.WriteListEnd()\n\tif e := p.Flush(); e != nil {\n\t\tt.Fatalf(\"Unable to write %s due to error flushing: %s\", thetype, e.Error())\n\t}\n\tstr := trans.String()\n\tstr1 := new([]interface{})\n\terr := json.Unmarshal([]byte(str), str1)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to decode %s, wrote: %s\", thetype, str)\n\t}\n\tl := *str1\n\tif len(l) < 2 {\n\t\tt.Fatalf(\"List must be at least of length two to include metadata\")\n\t}\n\tif int(l[0].(float64)) != DOUBLE {\n\t\tt.Fatal(\"Invalid type for list, expected: \", DOUBLE, \", but was: \", l[0])\n\t}\n\tif int(l[1].(float64)) != len(DOUBLE_VALUES) {\n\t\tt.Fatal(\"Invalid length for list, expected: \", len(DOUBLE_VALUES), \", but was: \", l[1])\n\t}\n\tfor k, value := range DOUBLE_VALUES {\n\t\ts := l[k+2]\n\t\tif math.IsInf(value, 1) {\n\t\t\tif s.(string) != JSON_INFINITY {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_INFINITY), str)\n\t\t\t}\n\t\t} else if math.IsInf(value, 0) {\n\t\t\tif s.(string) != JSON_NEGATIVE_INFINITY {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str)\n\t\t\t}\n\t\t} else if math.IsNaN(value) {\n\t\t\tif s.(string) != JSON_NAN {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v  %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_NAN), str)\n\t\t\t}\n\t\t} else {\n\t\t\tif s.(float64) != value {\n\t\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s'\", thetype, value, s)\n\t\t\t}\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestWriteSimpleJSONProtocolSet(t *testing.T) {\n\tthetype := \"set\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\tp.WriteSetBegin(TType(DOUBLE), len(DOUBLE_VALUES))\n\tfor _, value := range DOUBLE_VALUES {\n\t\tif e := p.WriteDouble(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t}\n\tp.WriteSetEnd()\n\tif e := p.Flush(); e != nil {\n\t\tt.Fatalf(\"Unable to write %s due to error flushing: %s\", thetype, e.Error())\n\t}\n\tstr := trans.String()\n\tstr1 := new([]interface{})\n\terr := json.Unmarshal([]byte(str), str1)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to decode %s, wrote: %s\", thetype, str)\n\t}\n\tl := *str1\n\tif len(l) < 2 {\n\t\tt.Fatalf(\"Set must be at least of length two to include metadata\")\n\t}\n\tif int(l[0].(float64)) != DOUBLE {\n\t\tt.Fatal(\"Invalid type for set, expected: \", DOUBLE, \", but was: \", l[0])\n\t}\n\tif int(l[1].(float64)) != len(DOUBLE_VALUES) {\n\t\tt.Fatal(\"Invalid length for set, expected: \", len(DOUBLE_VALUES), \", but was: \", l[1])\n\t}\n\tfor k, value := range DOUBLE_VALUES {\n\t\ts := l[k+2]\n\t\tif math.IsInf(value, 1) {\n\t\t\tif s.(string) != JSON_INFINITY {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_INFINITY), str)\n\t\t\t}\n\t\t} else if math.IsInf(value, 0) {\n\t\t\tif s.(string) != JSON_NEGATIVE_INFINITY {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str)\n\t\t\t}\n\t\t} else if math.IsNaN(value) {\n\t\t\tif s.(string) != JSON_NAN {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v  %v, wrote: %q, expected: %q, originally wrote: %q\", thetype, k, value, s, jsonQuote(JSON_NAN), str)\n\t\t\t}\n\t\t} else {\n\t\t\tif s.(float64) != value {\n\t\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s'\", thetype, value, s)\n\t\t\t}\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n\nfunc TestWriteSimpleJSONProtocolMap(t *testing.T) {\n\tthetype := \"map\"\n\ttrans := NewTMemoryBuffer()\n\tp := NewTSimpleJSONProtocol(trans)\n\tp.WriteMapBegin(TType(I32), TType(DOUBLE), len(DOUBLE_VALUES))\n\tfor k, value := range DOUBLE_VALUES {\n\t\tif e := p.WriteI32(int32(k)); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s key int32 value %v due to error: %s\", thetype, k, e.Error())\n\t\t}\n\t\tif e := p.WriteDouble(value); e != nil {\n\t\t\tt.Fatalf(\"Unable to write %s value float64 value %v due to error: %s\", thetype, value, e.Error())\n\t\t}\n\t}\n\tp.WriteMapEnd()\n\tif e := p.Flush(); e != nil {\n\t\tt.Fatalf(\"Unable to write %s due to error flushing: %s\", thetype, e.Error())\n\t}\n\tstr := trans.String()\n\tif str[0] != '[' || str[len(str)-1] != ']' {\n\t\tt.Fatalf(\"Bad value for %s, wrote: %q, in go: %q\", thetype, str, DOUBLE_VALUES)\n\t}\n\tl := strings.Split(str[1:len(str)-1], \",\")\n\tif len(l) < 3 {\n\t\tt.Fatal(\"Expected list of at least length 3 for map for metadata, but was of length \", len(l))\n\t}\n\texpectedKeyType, _ := strconv.Atoi(l[0])\n\texpectedValueType, _ := strconv.Atoi(l[1])\n\texpectedSize, _ := strconv.Atoi(l[2])\n\tif expectedKeyType != I32 {\n\t\tt.Fatal(\"Expected map key type \", I32, \", but was \", l[0])\n\t}\n\tif expectedValueType != DOUBLE {\n\t\tt.Fatal(\"Expected map value type \", DOUBLE, \", but was \", l[1])\n\t}\n\tif expectedSize != len(DOUBLE_VALUES) {\n\t\tt.Fatal(\"Expected map size of \", len(DOUBLE_VALUES), \", but was \", l[2])\n\t}\n\tfor k, value := range DOUBLE_VALUES {\n\t\tstrk := l[k*2+3]\n\t\tstrv := l[k*2+4]\n\t\tik, err := strconv.Atoi(strk)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Bad value for %s index %v, wrote: %v, expected: %v, error: %s\", thetype, k, strk, string(k), err.Error())\n\t\t}\n\t\tif ik != k {\n\t\t\tt.Fatalf(\"Bad value for %s index %v, wrote: %v, expected: %v\", thetype, k, strk, k)\n\t\t}\n\t\ts := strv\n\t\tif math.IsInf(value, 1) {\n\t\t\tif s != jsonQuote(JSON_INFINITY) {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %v, expected: %v\", thetype, k, value, s, jsonQuote(JSON_INFINITY))\n\t\t\t}\n\t\t} else if math.IsInf(value, 0) {\n\t\t\tif s != jsonQuote(JSON_NEGATIVE_INFINITY) {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %v, expected: %v\", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY))\n\t\t\t}\n\t\t} else if math.IsNaN(value) {\n\t\t\tif s != jsonQuote(JSON_NAN) {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v  %v, wrote: %v, expected: %v\", thetype, k, value, s, jsonQuote(JSON_NAN))\n\t\t\t}\n\t\t} else {\n\t\t\texpected := strconv.FormatFloat(value, 'g', 10, 64)\n\t\t\tif s != expected {\n\t\t\t\tt.Fatalf(\"Bad value for %s at index %v %v, wrote: %v, expected %v\", thetype, k, value, s, expected)\n\t\t\t}\n\t\t\tv := float64(0)\n\t\t\tif err := json.Unmarshal([]byte(s), &v); err != nil || v != value {\n\t\t\t\tt.Fatalf(\"Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'\", thetype, value, s, v)\n\t\t\t}\n\t\t}\n\t\ttrans.Reset()\n\t}\n\ttrans.Close()\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/simple_server.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"log\"\n\t\"runtime/debug\"\n\t\"sync\"\n)\n\n// Simple, non-concurrent server for testing.\ntype TSimpleServer struct {\n\tquit chan struct{}\n\n\tprocessorFactory       TProcessorFactory\n\tserverTransport        TServerTransport\n\tinputTransportFactory  TTransportFactory\n\toutputTransportFactory TTransportFactory\n\tinputProtocolFactory   TProtocolFactory\n\toutputProtocolFactory  TProtocolFactory\n}\n\nfunc NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer {\n\treturn NewTSimpleServerFactory2(NewTProcessorFactory(processor), serverTransport)\n}\n\nfunc NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {\n\treturn NewTSimpleServerFactory4(NewTProcessorFactory(processor),\n\t\tserverTransport,\n\t\ttransportFactory,\n\t\tprotocolFactory,\n\t)\n}\n\nfunc NewTSimpleServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {\n\treturn NewTSimpleServerFactory6(NewTProcessorFactory(processor),\n\t\tserverTransport,\n\t\tinputTransportFactory,\n\t\toutputTransportFactory,\n\t\tinputProtocolFactory,\n\t\toutputProtocolFactory,\n\t)\n}\n\nfunc NewTSimpleServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TSimpleServer {\n\treturn NewTSimpleServerFactory6(processorFactory,\n\t\tserverTransport,\n\t\tNewTTransportFactory(),\n\t\tNewTTransportFactory(),\n\t\tNewTBinaryProtocolFactoryDefault(),\n\t\tNewTBinaryProtocolFactoryDefault(),\n\t)\n}\n\nfunc NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {\n\treturn NewTSimpleServerFactory6(processorFactory,\n\t\tserverTransport,\n\t\ttransportFactory,\n\t\ttransportFactory,\n\t\tprotocolFactory,\n\t\tprotocolFactory,\n\t)\n}\n\nfunc NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {\n\treturn &TSimpleServer{\n\t\tprocessorFactory:       processorFactory,\n\t\tserverTransport:        serverTransport,\n\t\tinputTransportFactory:  inputTransportFactory,\n\t\toutputTransportFactory: outputTransportFactory,\n\t\tinputProtocolFactory:   inputProtocolFactory,\n\t\toutputProtocolFactory:  outputProtocolFactory,\n\t\tquit: make(chan struct{}, 1),\n\t}\n}\n\nfunc (p *TSimpleServer) ProcessorFactory() TProcessorFactory {\n\treturn p.processorFactory\n}\n\nfunc (p *TSimpleServer) ServerTransport() TServerTransport {\n\treturn p.serverTransport\n}\n\nfunc (p *TSimpleServer) InputTransportFactory() TTransportFactory {\n\treturn p.inputTransportFactory\n}\n\nfunc (p *TSimpleServer) OutputTransportFactory() TTransportFactory {\n\treturn p.outputTransportFactory\n}\n\nfunc (p *TSimpleServer) InputProtocolFactory() TProtocolFactory {\n\treturn p.inputProtocolFactory\n}\n\nfunc (p *TSimpleServer) OutputProtocolFactory() TProtocolFactory {\n\treturn p.outputProtocolFactory\n}\n\nfunc (p *TSimpleServer) Listen() error {\n\treturn p.serverTransport.Listen()\n}\n\nfunc (p *TSimpleServer) AcceptLoop() error {\n\tfor {\n\t\tclient, err := p.serverTransport.Accept()\n\t\tif err != nil {\n\t\t\tselect {\n\t\t\tcase <-p.quit:\n\t\t\t\treturn nil\n\t\t\tdefault:\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif client != nil {\n\t\t\tgo func() {\n\t\t\t\tif err := p.processRequests(client); err != nil {\n\t\t\t\t\tlog.Println(\"error processing request:\", err)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t}\n}\n\nfunc (p *TSimpleServer) Serve() error {\n\terr := p.Listen()\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.AcceptLoop()\n\treturn nil\n}\n\nvar once sync.Once\n\nfunc (p *TSimpleServer) Stop() error {\n\tq := func() {\n\t\tp.quit <- struct{}{}\n\t\tp.serverTransport.Interrupt()\n\t}\n\tonce.Do(q)\n\treturn nil\n}\n\nfunc (p *TSimpleServer) processRequests(client TTransport) error {\n\tprocessor := p.processorFactory.GetProcessor(client)\n\tinputTransport := p.inputTransportFactory.GetTransport(client)\n\toutputTransport := p.outputTransportFactory.GetTransport(client)\n\tinputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport)\n\toutputProtocol := p.outputProtocolFactory.GetProtocol(outputTransport)\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tlog.Printf(\"panic in processor: %s: %s\", e, debug.Stack())\n\t\t}\n\t}()\n\tif inputTransport != nil {\n\t\tdefer inputTransport.Close()\n\t}\n\tif outputTransport != nil {\n\t\tdefer outputTransport.Close()\n\t}\n\tfor {\n\t\tok, err := processor.Process(inputProtocol, outputProtocol)\n\t\tif err, ok := err.(TTransportException); ok && err.TypeId() == END_OF_FILE {\n\t\t\treturn nil\n\t\t} else if err != nil {\n\t\t\tlog.Printf(\"error processing request: %s\", err)\n\t\t\treturn err\n\t\t}\n\t\tif err, ok := err.(TApplicationException); ok && err.TypeId() == UNKNOWN_METHOD {\n\t\t\tcontinue\n\t\t}\n \t\tif !ok {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/socket.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"net\"\n\t\"time\"\n)\n\ntype TSocket struct {\n\tconn    net.Conn\n\taddr    net.Addr\n\ttimeout time.Duration\n}\n\n// NewTSocket creates a net.Conn-backed TTransport, given a host and port\n//\n// Example:\n// \ttrans, err := thrift.NewTSocket(\"localhost:9090\")\nfunc NewTSocket(hostPort string) (*TSocket, error) {\n\treturn NewTSocketTimeout(hostPort, 0)\n}\n\n// NewTSocketTimeout creates a net.Conn-backed TTransport, given a host and port\n// it also accepts a timeout as a time.Duration\nfunc NewTSocketTimeout(hostPort string, timeout time.Duration) (*TSocket, error) {\n\t//conn, err := net.DialTimeout(network, address, timeout)\n\taddr, err := net.ResolveTCPAddr(\"tcp\", hostPort)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewTSocketFromAddrTimeout(addr, timeout), nil\n}\n\n// Creates a TSocket from a net.Addr\nfunc NewTSocketFromAddrTimeout(addr net.Addr, timeout time.Duration) *TSocket {\n\treturn &TSocket{addr: addr, timeout: timeout}\n}\n\n// Creates a TSocket from an existing net.Conn\nfunc NewTSocketFromConnTimeout(conn net.Conn, timeout time.Duration) *TSocket {\n\treturn &TSocket{conn: conn, addr: conn.RemoteAddr(), timeout: timeout}\n}\n\n// Sets the socket timeout\nfunc (p *TSocket) SetTimeout(timeout time.Duration) error {\n\tp.timeout = timeout\n\treturn nil\n}\n\nfunc (p *TSocket) pushDeadline(read, write bool) {\n\tvar t time.Time\n\tif p.timeout > 0 {\n\t\tt = time.Now().Add(time.Duration(p.timeout))\n\t}\n\tif read && write {\n\t\tp.conn.SetDeadline(t)\n\t} else if read {\n\t\tp.conn.SetReadDeadline(t)\n\t} else if write {\n\t\tp.conn.SetWriteDeadline(t)\n\t}\n}\n\n// Connects the socket, creating a new socket object if necessary.\nfunc (p *TSocket) Open() error {\n\tif p.IsOpen() {\n\t\treturn NewTTransportException(ALREADY_OPEN, \"Socket already connected.\")\n\t}\n\tif p.addr == nil {\n\t\treturn NewTTransportException(NOT_OPEN, \"Cannot open nil address.\")\n\t}\n\tif len(p.addr.Network()) == 0 {\n\t\treturn NewTTransportException(NOT_OPEN, \"Cannot open bad network name.\")\n\t}\n\tif len(p.addr.String()) == 0 {\n\t\treturn NewTTransportException(NOT_OPEN, \"Cannot open bad address.\")\n\t}\n\tvar err error\n\tif p.conn, err = net.DialTimeout(p.addr.Network(), p.addr.String(), p.timeout); err != nil {\n\t\treturn NewTTransportException(NOT_OPEN, err.Error())\n\t}\n\treturn nil\n}\n\n// Retrieve the underlying net.Conn\nfunc (p *TSocket) Conn() net.Conn {\n\treturn p.conn\n}\n\n// Returns true if the connection is open\nfunc (p *TSocket) IsOpen() bool {\n\tif p.conn == nil {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// Closes the socket.\nfunc (p *TSocket) Close() error {\n\t// Close the socket\n\tif p.conn != nil {\n\t\terr := p.conn.Close()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tp.conn = nil\n\t}\n\treturn nil\n}\n\n//Returns the remote address of the socket.\nfunc (p *TSocket) Addr() net.Addr {\n\treturn p.addr\n}\n\nfunc (p *TSocket) Read(buf []byte) (int, error) {\n\tif !p.IsOpen() {\n\t\treturn 0, NewTTransportException(NOT_OPEN, \"Connection not open\")\n\t}\n\tp.pushDeadline(true, false)\n\tn, err := p.conn.Read(buf)\n\treturn n, NewTTransportExceptionFromError(err)\n}\n\nfunc (p *TSocket) Write(buf []byte) (int, error) {\n\tif !p.IsOpen() {\n\t\treturn 0, NewTTransportException(NOT_OPEN, \"Connection not open\")\n\t}\n\tp.pushDeadline(false, true)\n\treturn p.conn.Write(buf)\n}\n\nfunc (p *TSocket) Flush() error {\n\treturn nil\n}\n\nfunc (p *TSocket) Interrupt() error {\n\tif !p.IsOpen() {\n\t\treturn nil\n\t}\n\treturn p.conn.Close()\n}\n\nfunc (p *TSocket) RemainingBytes() (num_bytes uint64) {\n\tconst maxSize = ^uint64(0)\n\treturn maxSize  // the thruth is, we just don't know unless framed is used\n}\n\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/ssl_server_socket.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"net\"\n\t\"time\"\n\t\"crypto/tls\"\n)\n\ntype TSSLServerSocket struct {\n\tlistener      net.Listener\n\taddr          net.Addr\n\tclientTimeout time.Duration\n\tinterrupted   bool\n\tcfg           *tls.Config\n}\n\nfunc NewTSSLServerSocket(listenAddr string, cfg *tls.Config) (*TSSLServerSocket, error) {\n\treturn NewTSSLServerSocketTimeout(listenAddr, cfg, 0)\n}\n\nfunc NewTSSLServerSocketTimeout(listenAddr string, cfg *tls.Config, clientTimeout time.Duration) (*TSSLServerSocket, error) {\n\taddr, err := net.ResolveTCPAddr(\"tcp\", listenAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &TSSLServerSocket{addr: addr, clientTimeout: clientTimeout, cfg: cfg}, nil\n}\n\nfunc (p *TSSLServerSocket) Listen() error {\n\tif p.IsListening() {\n\t\treturn nil\n\t}\n\tl, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.listener = l\n\treturn nil\n}\n\nfunc (p *TSSLServerSocket) Accept() (TTransport, error) {\n\tif p.interrupted {\n\t\treturn nil, errTransportInterrupted\n\t}\n\tif p.listener == nil {\n\t\treturn nil, NewTTransportException(NOT_OPEN, \"No underlying server socket\")\n\t}\n\tconn, err := p.listener.Accept()\n\tif err != nil {\n\t\treturn nil, NewTTransportExceptionFromError(err)\n\t}\n\treturn NewTSSLSocketFromConnTimeout(conn, p.cfg, p.clientTimeout), nil\n}\n\n// Checks whether the socket is listening.\nfunc (p *TSSLServerSocket) IsListening() bool {\n\treturn p.listener != nil\n}\n\n// Connects the socket, creating a new socket object if necessary.\nfunc (p *TSSLServerSocket) Open() error {\n\tif p.IsListening() {\n\t\treturn NewTTransportException(ALREADY_OPEN, \"Server socket already open\")\n\t}\n\tif l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg); err != nil {\n\t\treturn err\n\t} else {\n\t\tp.listener = l\n\t}\n\treturn nil\n}\n\nfunc (p *TSSLServerSocket) Addr() net.Addr {\n\treturn p.addr\n}\n\nfunc (p *TSSLServerSocket) Close() error {\n\tdefer func() {\n\t\tp.listener = nil\n\t}()\n\tif p.IsListening() {\n\t\treturn p.listener.Close()\n\t}\n\treturn nil\n}\n\nfunc (p *TSSLServerSocket) Interrupt() error {\n\tp.interrupted = true\n\treturn nil\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/ssl_socket.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"crypto/tls\"\n\t\"net\"\n\t\"time\"\n)\n\ntype TSSLSocket struct {\n\tconn net.Conn\n\t// hostPort contains host:port (e.g. \"asdf.com:12345\"). The field is\n\t// only valid if addr is nil.\n\thostPort string\n\t// addr is nil when hostPort is not \"\", and is only used when the\n\t// TSSLSocket is constructed from a net.Addr.\n\taddr    net.Addr\n\ttimeout time.Duration\n\tcfg     *tls.Config\n}\n\n// NewTSSLSocket creates a net.Conn-backed TTransport, given a host and port and tls Configuration\n//\n// Example:\n// \ttrans, err := thrift.NewTSSLSocket(\"localhost:9090\", nil)\nfunc NewTSSLSocket(hostPort string, cfg *tls.Config) (*TSSLSocket, error) {\n\treturn NewTSSLSocketTimeout(hostPort, cfg, 0)\n}\n\n// NewTSSLSocketTimeout creates a net.Conn-backed TTransport, given a host and port\n// it also accepts a tls Configuration and a timeout as a time.Duration\nfunc NewTSSLSocketTimeout(hostPort string, cfg *tls.Config, timeout time.Duration) (*TSSLSocket, error) {\n\treturn &TSSLSocket{hostPort: hostPort, timeout: timeout, cfg: cfg}, nil\n}\n\n// Creates a TSSLSocket from a net.Addr\nfunc NewTSSLSocketFromAddrTimeout(addr net.Addr, cfg *tls.Config, timeout time.Duration) *TSSLSocket {\n\treturn &TSSLSocket{addr: addr, timeout: timeout, cfg: cfg}\n}\n\n// Creates a TSSLSocket from an existing net.Conn\nfunc NewTSSLSocketFromConnTimeout(conn net.Conn, cfg *tls.Config, timeout time.Duration) *TSSLSocket {\n\treturn &TSSLSocket{conn: conn, addr: conn.RemoteAddr(), timeout: timeout, cfg: cfg}\n}\n\n// Sets the socket timeout\nfunc (p *TSSLSocket) SetTimeout(timeout time.Duration) error {\n\tp.timeout = timeout\n\treturn nil\n}\n\nfunc (p *TSSLSocket) pushDeadline(read, write bool) {\n\tvar t time.Time\n\tif p.timeout > 0 {\n\t\tt = time.Now().Add(time.Duration(p.timeout))\n\t}\n\tif read && write {\n\t\tp.conn.SetDeadline(t)\n\t} else if read {\n\t\tp.conn.SetReadDeadline(t)\n\t} else if write {\n\t\tp.conn.SetWriteDeadline(t)\n\t}\n}\n\n// Connects the socket, creating a new socket object if necessary.\nfunc (p *TSSLSocket) Open() error {\n\tvar err error\n\t// If we have a hostname, we need to pass the hostname to tls.Dial for\n\t// certificate hostname checks.\n\tif p.hostPort != \"\" {\n\t\tif p.conn, err = tls.Dial(\"tcp\", p.hostPort, p.cfg); err != nil {\n\t\t\treturn NewTTransportException(NOT_OPEN, err.Error())\n\t\t}\n\t} else {\n\t\tif p.IsOpen() {\n\t\t\treturn NewTTransportException(ALREADY_OPEN, \"Socket already connected.\")\n\t\t}\n\t\tif p.addr == nil {\n\t\t\treturn NewTTransportException(NOT_OPEN, \"Cannot open nil address.\")\n\t\t}\n\t\tif len(p.addr.Network()) == 0 {\n\t\t\treturn NewTTransportException(NOT_OPEN, \"Cannot open bad network name.\")\n\t\t}\n\t\tif len(p.addr.String()) == 0 {\n\t\t\treturn NewTTransportException(NOT_OPEN, \"Cannot open bad address.\")\n\t\t}\n\t\tif p.conn, err = tls.Dial(p.addr.Network(), p.addr.String(), p.cfg); err != nil {\n\t\t\treturn NewTTransportException(NOT_OPEN, err.Error())\n\t\t}\n\t}\n\treturn nil\n}\n\n// Retrieve the underlying net.Conn\nfunc (p *TSSLSocket) Conn() net.Conn {\n\treturn p.conn\n}\n\n// Returns true if the connection is open\nfunc (p *TSSLSocket) IsOpen() bool {\n\tif p.conn == nil {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// Closes the socket.\nfunc (p *TSSLSocket) Close() error {\n\t// Close the socket\n\tif p.conn != nil {\n\t\terr := p.conn.Close()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tp.conn = nil\n\t}\n\treturn nil\n}\n\nfunc (p *TSSLSocket) Read(buf []byte) (int, error) {\n\tif !p.IsOpen() {\n\t\treturn 0, NewTTransportException(NOT_OPEN, \"Connection not open\")\n\t}\n\tp.pushDeadline(true, false)\n\tn, err := p.conn.Read(buf)\n\treturn n, NewTTransportExceptionFromError(err)\n}\n\nfunc (p *TSSLSocket) Write(buf []byte) (int, error) {\n\tif !p.IsOpen() {\n\t\treturn 0, NewTTransportException(NOT_OPEN, \"Connection not open\")\n\t}\n\tp.pushDeadline(false, true)\n\treturn p.conn.Write(buf)\n}\n\nfunc (p *TSSLSocket) Flush() error {\n\treturn nil\n}\n\nfunc (p *TSSLSocket) Interrupt() error {\n\tif !p.IsOpen() {\n\t\treturn nil\n\t}\n\treturn p.conn.Close()\n}\n\nfunc (p *TSSLSocket) RemainingBytes() (num_bytes uint64) {\n\tconst maxSize = ^uint64(0)\n\treturn maxSize  // the thruth is, we just don't know unless framed is used\n}\n\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/transport.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"errors\"\n\t\"io\"\n)\n\nvar errTransportInterrupted = errors.New(\"Transport Interrupted\")\n\ntype Flusher interface {\n\tFlush() (err error)\n}\n\ntype ReadSizeProvider interface {\n\tRemainingBytes() (num_bytes uint64)\n}\n\n\n// Encapsulates the I/O layer\ntype TTransport interface {\n\tio.ReadWriteCloser\n\tFlusher\n\tReadSizeProvider\n\n\t// Opens the transport for communication\n\tOpen() error\n\n\t// Returns true if the transport is open\n\tIsOpen() bool\n}\n\ntype stringWriter interface {\n\tWriteString(s string) (n int, err error)\n}\n\n\n// This is \"enchanced\" transport with extra capabilities. You need to use one of these\n// to construct protocol.\n// Notably, TSocket does not implement this interface, and it is always a mistake to use\n// TSocket directly in protocol.\ntype TRichTransport interface {\n\tio.ReadWriter\n\tio.ByteReader\n\tio.ByteWriter\n\tstringWriter\n\tFlusher\n\tReadSizeProvider\n}\n\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/transport_exception.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"errors\"\n\t\"io\"\n)\n\ntype timeoutable interface {\n\tTimeout() bool\n}\n\n// Thrift Transport exception\ntype TTransportException interface {\n\tTException\n\tTypeId() int\n\tErr() error\n}\n\nconst (\n\tUNKNOWN_TRANSPORT_EXCEPTION = 0\n\tNOT_OPEN                    = 1\n\tALREADY_OPEN                = 2\n\tTIMED_OUT                   = 3\n\tEND_OF_FILE                 = 4\n)\n\ntype tTransportException struct {\n\ttypeId int\n\terr    error\n}\n\nfunc (p *tTransportException) TypeId() int {\n\treturn p.typeId\n}\n\nfunc (p *tTransportException) Error() string {\n\treturn p.err.Error()\n}\n\nfunc (p *tTransportException) Err() error {\n\treturn p.err\n}\n\nfunc NewTTransportException(t int, e string) TTransportException {\n\treturn &tTransportException{typeId: t, err: errors.New(e)}\n}\n\nfunc NewTTransportExceptionFromError(e error) TTransportException {\n\tif e == nil {\n\t\treturn nil\n\t}\n\n\tif t, ok := e.(TTransportException); ok {\n\t\treturn t\n\t}\n\n\tswitch v := e.(type) {\n\tcase TTransportException:\n\t\treturn v\n\tcase timeoutable:\n\t\tif v.Timeout() {\n\t\t\treturn &tTransportException{typeId: TIMED_OUT, err: e}\n\t\t}\n\t}\n\n\tif e == io.EOF {\n\t\treturn &tTransportException{typeId: END_OF_FILE, err: e}\n\t}\n\n\treturn &tTransportException{typeId: UNKNOWN_TRANSPORT_EXCEPTION, err: e}\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/transport_exception_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"testing\"\n)\n\ntype timeout struct{ timedout bool }\n\nfunc (t *timeout) Timeout() bool {\n\treturn t.timedout\n}\n\nfunc (t *timeout) Error() string {\n\treturn fmt.Sprintf(\"Timeout: %v\", t.timedout)\n}\n\nfunc TestTExceptionTimeout(t *testing.T) {\n\ttimeout := &timeout{true}\n\texception := NewTTransportExceptionFromError(timeout)\n\tif timeout.Error() != exception.Error() {\n\t\tt.Fatalf(\"Error did not match: expected %q, got %q\", timeout.Error(), exception.Error())\n\t}\n\n\tif exception.TypeId() != TIMED_OUT {\n\t\tt.Fatalf(\"TypeId was not TIMED_OUT: expected %v, got %v\", TIMED_OUT, exception.TypeId())\n\t}\n}\n\nfunc TestTExceptionEOF(t *testing.T) {\n\texception := NewTTransportExceptionFromError(io.EOF)\n\tif io.EOF.Error() != exception.Error() {\n\t\tt.Fatalf(\"Error did not match: expected %q, got %q\", io.EOF.Error(), exception.Error())\n\t}\n\n\tif exception.TypeId() != END_OF_FILE {\n\t\tt.Fatalf(\"TypeId was not END_OF_FILE: expected %v, got %v\", END_OF_FILE, exception.TypeId())\n\t}\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/transport_factory.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\n// Factory class used to create wrapped instance of Transports.\n// This is used primarily in servers, which get Transports from\n// a ServerTransport and then may want to mutate them (i.e. create\n// a BufferedTransport from the underlying base transport)\ntype TTransportFactory interface {\n\tGetTransport(trans TTransport) TTransport\n}\n\ntype tTransportFactory struct{}\n\n// Return a wrapped instance of the base Transport.\nfunc (p *tTransportFactory) GetTransport(trans TTransport) TTransport {\n\treturn trans\n}\n\nfunc NewTTransportFactory() TTransportFactory {\n\treturn &tTransportFactory{}\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/transport_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"strconv\"\n\t\"testing\"\n)\n\nconst TRANSPORT_BINARY_DATA_SIZE = 4096\n\nvar (\n\ttransport_bdata  []byte // test data for writing; same as data\n\ttransport_header map[string]string\n)\n\nfunc init() {\n\ttransport_bdata = make([]byte, TRANSPORT_BINARY_DATA_SIZE)\n\tfor i := 0; i < TRANSPORT_BINARY_DATA_SIZE; i++ {\n\t\ttransport_bdata[i] = byte((i + 'a') % 255)\n\t}\n\ttransport_header = map[string]string{\"key\": \"User-Agent\",\n\t\t\"value\": \"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36\"}\n}\n\nfunc TransportTest(t *testing.T, writeTrans TTransport, readTrans TTransport) {\n\tbuf := make([]byte, TRANSPORT_BINARY_DATA_SIZE)\n\tif !writeTrans.IsOpen() {\n\t\tt.Fatalf(\"Transport %T not open: %s\", writeTrans, writeTrans)\n\t}\n\tif !readTrans.IsOpen() {\n\t\tt.Fatalf(\"Transport %T not open: %s\", readTrans, readTrans)\n\t}\n\t_, err := writeTrans.Write(transport_bdata)\n\tif err != nil {\n\t\tt.Fatalf(\"Transport %T cannot write binary data of length %d: %s\", writeTrans, len(transport_bdata), err)\n\t}\n\terr = writeTrans.Flush()\n\tif err != nil {\n\t\tt.Fatalf(\"Transport %T cannot flush write of binary data: %s\", writeTrans, err)\n\t}\n\tn, err := io.ReadFull(readTrans, buf)\n\tif err != nil {\n\t\tt.Errorf(\"Transport %T cannot read binary data of length %d: %s\", readTrans, TRANSPORT_BINARY_DATA_SIZE, err)\n\t}\n\tif n != TRANSPORT_BINARY_DATA_SIZE {\n\t\tt.Errorf(\"Transport %T read only %d instead of %d bytes of binary data\", readTrans, n, TRANSPORT_BINARY_DATA_SIZE)\n\t}\n\tfor k, v := range buf {\n\t\tif v != transport_bdata[k] {\n\t\t\tt.Fatalf(\"Transport %T read %d instead of %d for index %d of binary data 2\", readTrans, v, transport_bdata[k], k)\n\t\t}\n\t}\n\t_, err = writeTrans.Write(transport_bdata)\n\tif err != nil {\n\t\tt.Fatalf(\"Transport %T cannot write binary data 2 of length %d: %s\", writeTrans, len(transport_bdata), err)\n\t}\n\terr = writeTrans.Flush()\n\tif err != nil {\n\t\tt.Fatalf(\"Transport %T cannot flush write binary data 2: %s\", writeTrans, err)\n\t}\n\tbuf = make([]byte, TRANSPORT_BINARY_DATA_SIZE)\n\tread := 1\n\tfor n = 0; n < TRANSPORT_BINARY_DATA_SIZE && read != 0; {\n\t\tread, err = readTrans.Read(buf[n:])\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Transport %T cannot read binary data 2 of total length %d from offset %d: %s\", readTrans, TRANSPORT_BINARY_DATA_SIZE, n, err)\n\t\t}\n\t\tn += read\n\t}\n\tif n != TRANSPORT_BINARY_DATA_SIZE {\n\t\tt.Errorf(\"Transport %T read only %d instead of %d bytes of binary data 2\", readTrans, n, TRANSPORT_BINARY_DATA_SIZE)\n\t}\n\tfor k, v := range buf {\n\t\tif v != transport_bdata[k] {\n\t\t\tt.Fatalf(\"Transport %T read %d instead of %d for index %d of binary data 2\", readTrans, v, transport_bdata[k], k)\n\t\t}\n\t}\n}\n\nfunc TransportHeaderTest(t *testing.T, writeTrans TTransport, readTrans TTransport) {\n\tbuf := make([]byte, TRANSPORT_BINARY_DATA_SIZE)\n\tif !writeTrans.IsOpen() {\n\t\tt.Fatalf(\"Transport %T not open: %s\", writeTrans, writeTrans)\n\t}\n\tif !readTrans.IsOpen() {\n\t\tt.Fatalf(\"Transport %T not open: %s\", readTrans, readTrans)\n\t}\n\t// Need to assert type of TTransport to THttpClient to expose the Setter\n\thttpWPostTrans := writeTrans.(*THttpClient)\n\thttpWPostTrans.SetHeader(transport_header[\"key\"], transport_header[\"value\"])\n\n\t_, err := writeTrans.Write(transport_bdata)\n\tif err != nil {\n\t\tt.Fatalf(\"Transport %T cannot write binary data of length %d: %s\", writeTrans, len(transport_bdata), err)\n\t}\n\terr = writeTrans.Flush()\n\tif err != nil {\n\t\tt.Fatalf(\"Transport %T cannot flush write of binary data: %s\", writeTrans, err)\n\t}\n\t// Need to assert type of TTransport to THttpClient to expose the Getter\n\thttpRPostTrans := readTrans.(*THttpClient)\n\treadHeader := httpRPostTrans.GetHeader(transport_header[\"key\"])\n\tif err != nil {\n\t\tt.Errorf(\"Transport %T cannot read HTTP Header Value\", httpRPostTrans)\n\t}\n\n\tif transport_header[\"value\"] != readHeader {\n\t\tt.Errorf(\"Expected HTTP Header Value %s, got %s\", transport_header[\"value\"], readHeader)\n\t}\n\tn, err := io.ReadFull(readTrans, buf)\n\tif err != nil {\n\t\tt.Errorf(\"Transport %T cannot read binary data of length %d: %s\", readTrans, TRANSPORT_BINARY_DATA_SIZE, err)\n\t}\n\tif n != TRANSPORT_BINARY_DATA_SIZE {\n\t\tt.Errorf(\"Transport %T read only %d instead of %d bytes of binary data\", readTrans, n, TRANSPORT_BINARY_DATA_SIZE)\n\t}\n\tfor k, v := range buf {\n\t\tif v != transport_bdata[k] {\n\t\t\tt.Fatalf(\"Transport %T read %d instead of %d for index %d of binary data 2\", readTrans, v, transport_bdata[k], k)\n\t\t}\n\t}\n}\n\nfunc CloseTransports(t *testing.T, readTrans TTransport, writeTrans TTransport) {\n\terr := readTrans.Close()\n\tif err != nil {\n\t\tt.Errorf(\"Transport %T cannot close read transport: %s\", readTrans, err)\n\t}\n\tif writeTrans != readTrans {\n\t\terr = writeTrans.Close()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Transport %T cannot close write transport: %s\", writeTrans, err)\n\t\t}\n\t}\n}\n\nfunc FindAvailableTCPServerPort(startPort int) (net.Addr, error) {\n\tfor i := startPort; i < 65535; i++ {\n\t\ts := \"127.0.0.1:\" + strconv.Itoa(i)\n\t\tl, err := net.Listen(\"tcp\", s)\n\t\tif err == nil {\n\t\t\tl.Close()\n\t\t\treturn net.ResolveTCPAddr(\"tcp\", s)\n\t\t}\n\t}\n\treturn nil, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, \"Could not find available server port\")\n}\n\nfunc valueInSlice(value string, slice []string) bool {\n\tfor _, v := range slice {\n\t\tif value == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/type.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\n// Type constants in the Thrift protocol\ntype TType byte\n\nconst (\n\tSTOP   = 0\n\tVOID   = 1\n\tBOOL   = 2\n\tBYTE   = 3\n\tI08    = 3\n\tDOUBLE = 4\n\tI16    = 6\n\tI32    = 8\n\tI64    = 10\n\tSTRING = 11\n\tUTF7   = 11\n\tSTRUCT = 12\n\tMAP    = 13\n\tSET    = 14\n\tLIST   = 15\n\tUTF8   = 16\n\tUTF16  = 17\n\t//BINARY = 18   wrong and unusued\n)\n\nvar typeNames = map[int]string{\n\tSTOP:   \"STOP\",\n\tVOID:   \"VOID\",\n\tBOOL:   \"BOOL\",\n\tBYTE:   \"BYTE\",\n\tDOUBLE: \"DOUBLE\",\n\tI16:    \"I16\",\n\tI32:    \"I32\",\n\tI64:    \"I64\",\n\tSTRING: \"STRING\",\n\tSTRUCT: \"STRUCT\",\n\tMAP:    \"MAP\",\n\tSET:    \"SET\",\n\tLIST:   \"LIST\",\n\tUTF8:   \"UTF8\",\n\tUTF16:  \"UTF16\",\n}\n\nfunc (p TType) String() string {\n\tif s, ok := typeNames[int(p)]; ok {\n\t\treturn s\n\t}\n\treturn \"Unknown\"\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/zlib_transport.go",
    "content": "/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n */\n\npackage thrift\n\nimport (\n\t\"compress/zlib\"\n\t\"io\"\n\t\"log\"\n)\n\n// TZlibTransportFactory is a factory for TZlibTransport instances\ntype TZlibTransportFactory struct {\n\tlevel int\n}\n\n// TZlibTransport is a TTransport implementation that makes use of zlib compression.\ntype TZlibTransport struct {\n\treader    io.ReadCloser\n\ttransport TTransport\n\twriter    *zlib.Writer\n}\n\n// GetTransport constructs a new instance of NewTZlibTransport\nfunc (p *TZlibTransportFactory) GetTransport(trans TTransport) TTransport {\n\tt, _ := NewTZlibTransport(trans, p.level)\n\treturn t\n}\n\n// NewTZlibTransportFactory constructs a new instance of NewTZlibTransportFactory\nfunc NewTZlibTransportFactory(level int) *TZlibTransportFactory {\n\treturn &TZlibTransportFactory{level: level}\n}\n\n// NewTZlibTransport constructs a new instance of TZlibTransport\nfunc NewTZlibTransport(trans TTransport, level int) (*TZlibTransport, error) {\n\tw, err := zlib.NewWriterLevel(trans, level)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn nil, err\n\t}\n\n\treturn &TZlibTransport{\n\t\twriter:    w,\n\t\ttransport: trans,\n\t}, nil\n}\n\n// Close closes the reader and writer (flushing any unwritten data) and closes\n// the underlying transport.\nfunc (z *TZlibTransport) Close() error {\n\tif z.reader != nil {\n\t\tif err := z.reader.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := z.writer.Close(); err != nil {\n\t\treturn err\n\t}\n\treturn z.transport.Close()\n}\n\n// Flush flushes the writer and its underlying transport.\nfunc (z *TZlibTransport) Flush() error {\n\tif err := z.writer.Flush(); err != nil {\n\t\treturn err\n\t}\n\treturn z.transport.Flush()\n}\n\n// IsOpen returns true if the transport is open\nfunc (z *TZlibTransport) IsOpen() bool {\n\treturn z.transport.IsOpen()\n}\n\n// Open opens the transport for communication\nfunc (z *TZlibTransport) Open() error {\n\treturn z.transport.Open()\n}\n\nfunc (z *TZlibTransport) Read(p []byte) (int, error) {\n\tif z.reader == nil {\n\t\tr, err := zlib.NewReader(z.transport)\n\t\tif err != nil {\n\t\t\treturn 0, NewTTransportExceptionFromError(err)\n\t\t}\n\t\tz.reader = r\n\t}\n\n\treturn z.reader.Read(p)\n}\n\n// RemainingBytes returns the size in bytes of the data that is still to be\n// read.\nfunc (z *TZlibTransport) RemainingBytes() uint64 {\n\treturn z.transport.RemainingBytes()\n}\n\nfunc (z *TZlibTransport) Write(p []byte) (int, error) {\n\treturn z.writer.Write(p)\n}\n"
  },
  {
    "path": "thirdparty/github.com/apache/thrift/lib/go/thrift/zlib_transport_test.go",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\npackage thrift\n\nimport (\n\t\"compress/zlib\"\n\t\"testing\"\n)\n\nfunc TestZlibTransport(t *testing.T) {\n\ttrans, err := NewTZlibTransport(NewTMemoryBuffer(), zlib.BestCompression)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tTransportTest(t, trans, trans)\n}\n"
  },
  {
    "path": "thrift/arg2/kv_iterator.go",
    "content": "// Package arg2 contains tchannel thrift Arg2 interfaces for external use.\n//\n// These interfaces are currently unstable, and aren't covered by the API\n// backwards-compatibility guarantee.\npackage arg2\n\nimport (\n\t\"encoding/binary\"\n\t\"io\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\n// KeyValIterator is a iterator for reading tchannel-thrift Arg2 Scheme,\n// which has key/value pairs (k~2 v~2).\n// NOTE: to be optimized for performance, we try to limit the allocation\n// done in the process of iteration.\ntype KeyValIterator struct {\n\tremaining     []byte\n\tleftPairCount int\n\tkey           []byte\n\tval           []byte\n}\n\n// NewKeyValIterator inits a KeyValIterator with the buffer pointing at\n// start of Arg2. Return io.EOF if no iterator is available.\n// NOTE: tchannel-thrift Arg Scheme starts with number of key/value pair.\nfunc NewKeyValIterator(arg2Payload []byte) (KeyValIterator, error) {\n\tif len(arg2Payload) < 2 {\n\t\treturn KeyValIterator{}, io.EOF\n\t}\n\n\tleftPairCount := binary.BigEndian.Uint16(arg2Payload[0:2])\n\treturn KeyValIterator{\n\t\tleftPairCount: int(leftPairCount),\n\t\tremaining:     arg2Payload[2:],\n\t}.Next()\n}\n\n// Key Returns the key.\nfunc (i KeyValIterator) Key() []byte {\n\treturn i.key\n}\n\n// Value returns value.\nfunc (i KeyValIterator) Value() []byte {\n\treturn i.val\n}\n\n// Remaining returns whether there's any pairs left to consume.\nfunc (i KeyValIterator) Remaining() bool {\n\treturn i.leftPairCount > 0\n}\n\n// Next returns next iterator. Return io.EOF if no more key/value pair is\n// available.\n//\n// Note: We used named returns because of an unexpected performance improvement\n// See https://github.com/golang/go/issues/40638\nfunc (i KeyValIterator) Next() (kv KeyValIterator, _ error) {\n\tif i.leftPairCount <= 0 {\n\t\treturn KeyValIterator{}, io.EOF\n\t}\n\n\trbuf := typed.NewReadBuffer(i.remaining)\n\tkeyLen := int(rbuf.ReadUint16())\n\tkey := rbuf.ReadBytes(keyLen)\n\tvalLen := int(rbuf.ReadUint16())\n\tval := rbuf.ReadBytes(valLen)\n\tif rbuf.Err() != nil {\n\t\treturn KeyValIterator{}, rbuf.Err()\n\t}\n\n\tleftPairCount := i.leftPairCount - 1\n\n\tkv = KeyValIterator{\n\t\tremaining:     rbuf.Remaining(),\n\t\tleftPairCount: leftPairCount,\n\t\tkey:           key,\n\t\tval:           val,\n\t}\n\treturn kv, nil\n}\n"
  },
  {
    "path": "thrift/arg2/kv_iterator_test.go",
    "content": "package arg2\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/uber/tchannel-go/testutils/thriftarg2test\"\n)\n\nfunc TestKeyValIterator(t *testing.T) {\n\tconst (\n\t\ttestBufSize = 100\n\t\tnh          = 5\n\t)\n\n\tkv := make(map[string]string, nh)\n\tfor i := 0; i < nh; i++ {\n\t\tkv[fmt.Sprintf(\"key%v\", i)] = fmt.Sprintf(\"value%v\", i)\n\t}\n\tbuf := thriftarg2test.BuildKVBuffer(kv)\n\n\titer, err := NewKeyValIterator(buf)\n\tgotKV := make(map[string]string)\n\tfor i := 0; i < nh; i++ {\n\t\tassert.NoError(t, err)\n\t\tgotKV[fmt.Sprintf(\"key%v\", i)] = fmt.Sprintf(\"value%v\", i)\n\n\t\tremaining := iter.Remaining()\n\t\titer, err = iter.Next()\n\n\t\tassert.Equal(t, err == nil, remaining, \"Expect remaining to be true if there's no errors\")\n\t}\n\tassert.Equal(t, io.EOF, err)\n\tassert.Equal(t, kv, gotKV)\n\n\tt.Run(\"init iterator w/o Arg2\", func(t *testing.T) {\n\t\t_, err := NewKeyValIterator(nil)\n\t\tassert.Equal(t, io.EOF, err)\n\t})\n\n\tt.Run(\"init iterator w/o pairs\", func(t *testing.T) {\n\t\tbuf := thriftarg2test.BuildKVBuffer(nil /*kv*/)\n\t\t_, err := NewKeyValIterator(buf)\n\t\tassert.Equal(t, io.EOF, err)\n\t})\n\n\tt.Run(\"bad key value length\", func(t *testing.T) {\n\t\tbuf := thriftarg2test.BuildKVBuffer(map[string]string{\n\t\t\t\"key\": \"value\",\n\t\t})\n\t\ttests := []struct {\n\t\t\tmsg     string\n\t\t\targ2Len int\n\t\t\twantErr string\n\t\t}{\n\t\t\t{\n\t\t\t\tmsg:     \"ok\",\n\t\t\t\targ2Len: len(buf),\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:     \"not enough to read key len\",\n\t\t\t\targ2Len: 3, // nh (2) + 1\n\t\t\t\twantErr: \"buffer is too small\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:     \"not enough to hold key value\",\n\t\t\t\targ2Len: 6, // nh (2) + 2 + len(key) - 1\n\t\t\t\twantErr: \"buffer is too small\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:     \"not enough to read value len\",\n\t\t\t\targ2Len: 8, // nh (2) + 2 + len(key) + 1\n\t\t\t\twantErr: \"buffer is too small\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tmsg:     \"not enough to iterate value\",\n\t\t\t\targ2Len: 13, // nh (2) + 2 + len(key) + 2 + len(value) = 14\n\t\t\t\twantErr: \"buffer is too small\",\n\t\t\t},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\t\titer, err := NewKeyValIterator(buf[:tt.arg2Len])\n\t\t\t\tif tt.wantErr == \"\" {\n\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, \"key\", string(iter.Key()), \"unexpected key\")\n\t\t\t\t\tassert.Equal(t, \"value\", string(iter.Value()), \"unexpected value\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\trequire.Error(t, err, \"should not create iterator\")\n\t\t\t\tassert.Contains(t, err.Error(), tt.wantErr)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc BenchmarkKeyValIterator(b *testing.B) {\n\tkvBuffer := thriftarg2test.BuildKVBuffer(map[string]string{\n\t\t\"foo\":  \"bar\",\n\t\t\"baz\":  \"qux\",\n\t\t\"quux\": \"corge\",\n\t})\n\n\tfor i := 0; i < b.N; i++ {\n\t\titer, err := NewKeyValIterator(kvBuffer)\n\t\tif err != nil {\n\t\t\tb.Fatalf(\"unexpected err %v\", err)\n\t\t}\n\n\t\tfor iter.Remaining() {\n\t\t\titer, err = iter.Next()\n\t\t\tif err != nil {\n\t\t\t\tb.Fatalf(\"unexpected err %v\", err)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "thrift/client.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/internal/argreader\"\n\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n\t\"golang.org/x/net/context\"\n)\n\n// client implements TChanClient and makes outgoing Thrift calls.\ntype client struct {\n\tch          *tchannel.Channel\n\tsc          *tchannel.SubChannel\n\tserviceName string\n\topts        ClientOptions\n}\n\n// ClientOptions are options to customize the client.\ntype ClientOptions struct {\n\t// HostPort specifies a specific server to hit.\n\tHostPort string\n}\n\n// NewClient returns a Client that makes calls over the given tchannel to the given Hyperbahn service.\nfunc NewClient(ch *tchannel.Channel, serviceName string, opts *ClientOptions) TChanClient {\n\tclient := &client{\n\t\tch:          ch,\n\t\tsc:          ch.GetSubChannel(serviceName),\n\t\tserviceName: serviceName,\n\t}\n\tif opts != nil {\n\t\tclient.opts = *opts\n\t}\n\treturn client\n}\n\nfunc (c *client) startCall(ctx context.Context, method string, callOptions *tchannel.CallOptions) (*tchannel.OutboundCall, error) {\n\tif c.opts.HostPort != \"\" {\n\t\treturn c.ch.BeginCall(ctx, c.opts.HostPort, c.serviceName, method, callOptions)\n\t}\n\treturn c.sc.BeginCall(ctx, method, callOptions)\n}\n\nfunc writeArgs(call *tchannel.OutboundCall, headers map[string]string, req thrift.TStruct) error {\n\twriter, err := call.Arg2Writer()\n\tif err != nil {\n\t\treturn err\n\t}\n\theaders = tchannel.InjectOutboundSpan(call.Response(), headers)\n\tif err := WriteHeaders(writer, headers); err != nil {\n\t\treturn err\n\t}\n\tif err := writer.Close(); err != nil {\n\t\treturn err\n\t}\n\n\twriter, err = call.Arg3Writer()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := WriteStruct(writer, req); err != nil {\n\t\treturn err\n\t}\n\n\treturn writer.Close()\n}\n\n// readResponse reads the response struct into resp, and returns:\n// (response headers, whether there was an application error, unexpected error).\nfunc readResponse(response *tchannel.OutboundCallResponse, resp thrift.TStruct) (map[string]string, bool, error) {\n\treader, err := response.Arg2Reader()\n\tif err != nil {\n\t\treturn nil, false, err\n\t}\n\n\theaders, err := ReadHeaders(reader)\n\tif err != nil {\n\t\treturn nil, false, err\n\t}\n\n\tif err := argreader.EnsureEmpty(reader, \"reading response headers\"); err != nil {\n\t\treturn nil, false, err\n\t}\n\n\tif err := reader.Close(); err != nil {\n\t\treturn nil, false, err\n\t}\n\n\tsuccess := !response.ApplicationError()\n\treader, err = response.Arg3Reader()\n\tif err != nil {\n\t\treturn headers, success, err\n\t}\n\n\tif err := ReadStruct(reader, resp); err != nil {\n\t\treturn headers, success, err\n\t}\n\n\tif err := argreader.EnsureEmpty(reader, \"reading response body\"); err != nil {\n\t\treturn nil, false, err\n\t}\n\n\treturn headers, success, reader.Close()\n}\n\nfunc (c *client) Call(ctx Context, thriftService, methodName string, req, resp thrift.TStruct) (bool, error) {\n\tvar (\n\t\theaders = ctx.Headers()\n\n\t\trespHeaders map[string]string\n\t\tisOK        bool\n\t)\n\n\terr := c.ch.RunWithRetry(ctx, func(ctx context.Context, rs *tchannel.RequestState) error {\n\t\trespHeaders, isOK = nil, false\n\n\t\tcall, err := c.startCall(ctx, thriftService+\"::\"+methodName, &tchannel.CallOptions{\n\t\t\tFormat:       tchannel.Thrift,\n\t\t\tRequestState: rs,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := writeArgs(call, headers, req); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\trespHeaders, isOK, err = readResponse(call.Response(), resp)\n\t\treturn err\n\t})\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tctx.SetResponseHeaders(respHeaders)\n\treturn isOK, nil\n}\n"
  },
  {
    "path": "thrift/context.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"golang.org/x/net/context\"\n)\n\n// Context is a Thrift Context which contains request and response headers.\ntype Context tchannel.ContextWithHeaders\n\n// NewContext returns a Context that can be used to make Thrift calls.\nfunc NewContext(timeout time.Duration) (Context, context.CancelFunc) {\n\tctx, cancel := tchannel.NewContext(timeout)\n\treturn Wrap(ctx), cancel\n}\n\n// Wrap returns a Thrift Context that wraps around a Context.\nfunc Wrap(ctx context.Context) Context {\n\treturn tchannel.Wrap(ctx)\n}\n\n// WithHeaders returns a Context that can be used to make a call with request headers.\nfunc WithHeaders(ctx context.Context, headers map[string]string) Context {\n\treturn tchannel.WrapWithHeaders(ctx, headers)\n}\n"
  },
  {
    "path": "thrift/context_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go/thrift\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\tgen \"github.com/uber/tchannel-go/thrift/gen-go/test\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"golang.org/x/net/context\"\n)\n\nfunc TestWrapContext(t *testing.T) {\n\ttctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\theaders := map[string]string{\"h1\": \"v1\"}\n\tctx := context.WithValue(WithHeaders(tctx, headers), \"1\", \"2\")\n\twrapped := Wrap(ctx)\n\tassert.NotNil(t, wrapped, \"Should not return nil.\")\n\n\tassert.Equal(t, headers, wrapped.Headers(), \"Unexpected headers\")\n\tassert.Equal(t, \"2\", wrapped.Value(\"1\"), \"Unexpected value\")\n}\n\nfunc TestContextBuilder(t *testing.T) {\n\tctx, cancel := tchannel.NewContextBuilder(time.Second).SetShardKey(\"shard\").Build()\n\tdefer cancel()\n\n\tvar called bool\n\ttestutils.WithServer(t, nil, func(ch *tchannel.Channel, hostPort string) {\n\t\tpeerInfo := ch.PeerInfo()\n\n\t\ttestutils.RegisterFunc(ch, \"SecondService::Echo\", func(ctx context.Context, args *raw.Args) (*raw.Res, error) {\n\t\t\tcall := tchannel.CurrentCall(ctx)\n\t\t\tassert.Equal(t, peerInfo.ServiceName, call.CallerName(), \"unexpected caller name\")\n\t\t\tassert.Equal(t, \"shard\", call.ShardKey(), \"unexpected shard key\")\n\t\t\tassert.Equal(t, tchannel.Thrift, args.Format)\n\t\t\tcalled = true\n\t\t\treturn nil, errors.New(\"err\")\n\t\t})\n\n\t\tclient := NewClient(ch, ch.PeerInfo().ServiceName, &ClientOptions{\n\t\t\tHostPort: peerInfo.HostPort,\n\t\t})\n\t\tsecondClient := gen.NewTChanSecondServiceClient(client)\n\t\tsecondClient.Echo(ctx, \"asd\")\n\t\tassert.True(t, called, \"test not called\")\n\t})\n}\n"
  },
  {
    "path": "thrift/doc.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n/*\nPackage thrift adds support to use Thrift services over TChannel.\n\nTo start listening to a Thrift service using TChannel, create the channel,\nand register the service using:\n\n\tserver := thrift.NewServer(tchan)\n\tserver.Register(gen.NewTChan[SERVICE]Server(handler)\n\n\t// Any number of services can be registered on the same Thrift server.\n\tserver.Register(gen.NewTChan[SERVICE2]Server(handler)\n\nTo use a Thrift client use the generated TChan client:\n\n\tthriftClient := thrift.NewClient(ch, \"hyperbahnService\", nil)\n\tclient := gen.NewTChan[SERVICE]Client(thriftClient)\n\n\t// Any number of service clients can be made using the same Thrift client.\n\tclient2 := gen.NewTChan[SERVICE2]Client(thriftClient)\n\nThis client can be used similar to a standard Thrift client, except a Context\nis passed with options (such as timeout).\n\nTODO(prashant): Add and document header support.\n*/\npackage thrift\n"
  },
  {
    "path": "thrift/errors_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t// Test is in a separate package to avoid circular dependencies.\n\t. \"github.com/uber/tchannel-go/thrift\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/raw\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\tgen \"github.com/uber/tchannel-go/thrift/gen-go/test\"\n\t\"github.com/uber/tchannel-go/thrift/mocks\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\nfunc serializeStruct(t *testing.T, s thrift.TStruct) []byte {\n\ttrans := thrift.NewTMemoryBuffer()\n\tp := thrift.NewTBinaryProtocolTransport(trans)\n\trequire.NoError(t, s.Write(p), \"Struct serialization failed\")\n\treturn trans.Bytes()\n}\n\nfunc TestInvalidThriftBytes(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\tch := testutils.NewClient(t, nil)\n\tsCh := testutils.NewServer(t, nil)\n\tdefer sCh.Close()\n\n\tsvr := NewServer(sCh)\n\tsvr.Register(gen.NewTChanSecondServiceServer(new(mocks.TChanSecondService)))\n\n\ttests := []struct {\n\t\tname string\n\t\targ3 []byte\n\t}{\n\t\t{\n\t\t\tname: \"missing bytes\",\n\t\t\targ3: serializeStruct(t, &gen.SecondServiceEchoArgs{Arg: \"Hello world\"})[:5],\n\t\t},\n\t\t{\n\t\t\tname: \"wrong struct\",\n\t\t\targ3: serializeStruct(t, &gen.Data{B1: true}),\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tsPeer := sCh.PeerInfo()\n\t\tcall, err := ch.BeginCall(ctx, sPeer.HostPort, sPeer.ServiceName, \"SecondService::Echo\", &tchannel.CallOptions{\n\t\t\tFormat: tchannel.Thrift,\n\t\t})\n\t\trequire.NoError(t, err, \"BeginCall failed\")\n\t\trequire.NoError(t, tchannel.NewArgWriter(call.Arg2Writer()).Write([]byte{0, 0}), \"Write arg2 failed\")\n\n\t\twriter, err := call.Arg3Writer()\n\t\trequire.NoError(t, err, \"Arg3Writer failed\")\n\n\t\t_, err = writer.Write(tt.arg3)\n\t\trequire.NoError(t, err, \"Write arg3 failed\")\n\t\trequire.NoError(t, writer.Close(), \"Close failed\")\n\n\t\tresponse := call.Response()\n\t\t_, _, err = raw.ReadArgsV2(response)\n\t\tassert.Error(t, err, \"%v: Expected error\", tt.name)\n\t\tassert.Equal(t, tchannel.ErrCodeBadRequest, tchannel.GetSystemErrorCode(err),\n\t\t\t\"%v: Expected bad request, got %v\", tt.name, err)\n\t}\n}\n"
  },
  {
    "path": "thrift/gen-go/meta/constants.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage meta\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\nfunc init() {\n}\n"
  },
  {
    "path": "thrift/gen-go/meta/meta.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage meta\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\ntype Meta interface {\n\t// Parameters:\n\t//  - Hr\n\tHealth(hr *HealthRequest) (r *HealthStatus, err error)\n\tThriftIDL() (r *ThriftIDLs, err error)\n\tVersionInfo() (r *VersionInfo, err error)\n}\n\ntype MetaClient struct {\n\tTransport       thrift.TTransport\n\tProtocolFactory thrift.TProtocolFactory\n\tInputProtocol   thrift.TProtocol\n\tOutputProtocol  thrift.TProtocol\n\tSeqId           int32\n}\n\nfunc NewMetaClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *MetaClient {\n\treturn &MetaClient{Transport: t,\n\t\tProtocolFactory: f,\n\t\tInputProtocol:   f.GetProtocol(t),\n\t\tOutputProtocol:  f.GetProtocol(t),\n\t\tSeqId:           0,\n\t}\n}\n\nfunc NewMetaClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *MetaClient {\n\treturn &MetaClient{Transport: t,\n\t\tProtocolFactory: nil,\n\t\tInputProtocol:   iprot,\n\t\tOutputProtocol:  oprot,\n\t\tSeqId:           0,\n\t}\n}\n\n// Parameters:\n//   - Hr\nfunc (p *MetaClient) Health(hr *HealthRequest) (r *HealthStatus, err error) {\n\tif err = p.sendHealth(hr); err != nil {\n\t\treturn\n\t}\n\treturn p.recvHealth()\n}\n\nfunc (p *MetaClient) sendHealth(hr *HealthRequest) (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"health\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := MetaHealthArgs{\n\t\tHr: hr,\n\t}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *MetaClient) recvHealth() (value *HealthStatus, err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"health\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"health failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"health failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error3 error\n\t\terror3, err = error2.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error3\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"health failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := MetaHealthResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tvalue = result.GetSuccess()\n\treturn\n}\n\nfunc (p *MetaClient) ThriftIDL() (r *ThriftIDLs, err error) {\n\tif err = p.sendThriftIDL(); err != nil {\n\t\treturn\n\t}\n\treturn p.recvThriftIDL()\n}\n\nfunc (p *MetaClient) sendThriftIDL() (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"thriftIDL\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := MetaThriftIDLArgs{}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *MetaClient) recvThriftIDL() (value *ThriftIDLs, err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"thriftIDL\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"thriftIDL failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"thriftIDL failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror4 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error5 error\n\t\terror5, err = error4.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error5\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"thriftIDL failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := MetaThriftIDLResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tvalue = result.GetSuccess()\n\treturn\n}\n\nfunc (p *MetaClient) VersionInfo() (r *VersionInfo, err error) {\n\tif err = p.sendVersionInfo(); err != nil {\n\t\treturn\n\t}\n\treturn p.recvVersionInfo()\n}\n\nfunc (p *MetaClient) sendVersionInfo() (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"versionInfo\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := MetaVersionInfoArgs{}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *MetaClient) recvVersionInfo() (value *VersionInfo, err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"versionInfo\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"versionInfo failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"versionInfo failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror6 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error7 error\n\t\terror7, err = error6.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error7\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"versionInfo failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := MetaVersionInfoResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tvalue = result.GetSuccess()\n\treturn\n}\n\ntype MetaProcessor struct {\n\tprocessorMap map[string]thrift.TProcessorFunction\n\thandler      Meta\n}\n\nfunc (p *MetaProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {\n\tp.processorMap[key] = processor\n}\n\nfunc (p *MetaProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {\n\tprocessor, ok = p.processorMap[key]\n\treturn processor, ok\n}\n\nfunc (p *MetaProcessor) ProcessorMap() map[string]thrift.TProcessorFunction {\n\treturn p.processorMap\n}\n\nfunc NewMetaProcessor(handler Meta) *MetaProcessor {\n\n\tself8 := &MetaProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)}\n\tself8.processorMap[\"health\"] = &metaProcessorHealth{handler: handler}\n\tself8.processorMap[\"thriftIDL\"] = &metaProcessorThriftIDL{handler: handler}\n\tself8.processorMap[\"versionInfo\"] = &metaProcessorVersionInfo{handler: handler}\n\treturn self8\n}\n\nfunc (p *MetaProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\tname, _, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif processor, ok := p.GetProcessorFunction(name); ok {\n\t\treturn processor.Process(seqId, iprot, oprot)\n\t}\n\tiprot.Skip(thrift.STRUCT)\n\tiprot.ReadMessageEnd()\n\tx9 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function \"+name)\n\toprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)\n\tx9.Write(oprot)\n\toprot.WriteMessageEnd()\n\toprot.Flush()\n\treturn false, x9\n\n}\n\ntype metaProcessorHealth struct {\n\thandler Meta\n}\n\nfunc (p *metaProcessorHealth) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := MetaHealthArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"health\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := MetaHealthResult{}\n\tvar retval *HealthStatus\n\tvar err2 error\n\tif retval, err2 = p.handler.Health(args.Hr); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing health: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"health\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t} else {\n\t\tresult.Success = retval\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"health\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\ntype metaProcessorThriftIDL struct {\n\thandler Meta\n}\n\nfunc (p *metaProcessorThriftIDL) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := MetaThriftIDLArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"thriftIDL\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := MetaThriftIDLResult{}\n\tvar retval *ThriftIDLs\n\tvar err2 error\n\tif retval, err2 = p.handler.ThriftIDL(); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing thriftIDL: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"thriftIDL\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t} else {\n\t\tresult.Success = retval\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"thriftIDL\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\ntype metaProcessorVersionInfo struct {\n\thandler Meta\n}\n\nfunc (p *metaProcessorVersionInfo) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := MetaVersionInfoArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"versionInfo\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := MetaVersionInfoResult{}\n\tvar retval *VersionInfo\n\tvar err2 error\n\tif retval, err2 = p.handler.VersionInfo(); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing versionInfo: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"versionInfo\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t} else {\n\t\tresult.Success = retval\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"versionInfo\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\n// HELPER FUNCTIONS AND STRUCTURES\n\n// Attributes:\n//   - Hr\ntype MetaHealthArgs struct {\n\tHr *HealthRequest `thrift:\"hr,1\" db:\"hr\" json:\"hr\"`\n}\n\nfunc NewMetaHealthArgs() *MetaHealthArgs {\n\treturn &MetaHealthArgs{}\n}\n\nvar MetaHealthArgs_Hr_DEFAULT *HealthRequest\n\nfunc (p *MetaHealthArgs) GetHr() *HealthRequest {\n\tif !p.IsSetHr() {\n\t\treturn MetaHealthArgs_Hr_DEFAULT\n\t}\n\treturn p.Hr\n}\nfunc (p *MetaHealthArgs) IsSetHr() bool {\n\treturn p.Hr != nil\n}\n\nfunc (p *MetaHealthArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaHealthArgs) ReadField1(iprot thrift.TProtocol) error {\n\tp.Hr = &HealthRequest{}\n\tif err := p.Hr.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.Hr), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaHealthArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"health_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaHealthArgs) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"hr\", thrift.STRUCT, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:hr: \", p), err)\n\t}\n\tif err := p.Hr.Write(oprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.Hr), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:hr: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *MetaHealthArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"MetaHealthArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Success\ntype MetaHealthResult struct {\n\tSuccess *HealthStatus `thrift:\"success,0\" db:\"success\" json:\"success,omitempty\"`\n}\n\nfunc NewMetaHealthResult() *MetaHealthResult {\n\treturn &MetaHealthResult{}\n}\n\nvar MetaHealthResult_Success_DEFAULT *HealthStatus\n\nfunc (p *MetaHealthResult) GetSuccess() *HealthStatus {\n\tif !p.IsSetSuccess() {\n\t\treturn MetaHealthResult_Success_DEFAULT\n\t}\n\treturn p.Success\n}\nfunc (p *MetaHealthResult) IsSetSuccess() bool {\n\treturn p.Success != nil\n}\n\nfunc (p *MetaHealthResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 0:\n\t\t\tif err := p.ReadField0(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaHealthResult) ReadField0(iprot thrift.TProtocol) error {\n\tp.Success = &HealthStatus{}\n\tif err := p.Success.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.Success), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaHealthResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"health_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField0(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaHealthResult) writeField0(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSuccess() {\n\t\tif err := oprot.WriteFieldBegin(\"success\", thrift.STRUCT, 0); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 0:success: \", p), err)\n\t\t}\n\t\tif err := p.Success.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.Success), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 0:success: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *MetaHealthResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"MetaHealthResult(%+v)\", *p)\n}\n\ntype MetaThriftIDLArgs struct {\n}\n\nfunc NewMetaThriftIDLArgs() *MetaThriftIDLArgs {\n\treturn &MetaThriftIDLArgs{}\n}\n\nfunc (p *MetaThriftIDLArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaThriftIDLArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"thriftIDL_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaThriftIDLArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"MetaThriftIDLArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Success\ntype MetaThriftIDLResult struct {\n\tSuccess *ThriftIDLs `thrift:\"success,0\" db:\"success\" json:\"success,omitempty\"`\n}\n\nfunc NewMetaThriftIDLResult() *MetaThriftIDLResult {\n\treturn &MetaThriftIDLResult{}\n}\n\nvar MetaThriftIDLResult_Success_DEFAULT *ThriftIDLs\n\nfunc (p *MetaThriftIDLResult) GetSuccess() *ThriftIDLs {\n\tif !p.IsSetSuccess() {\n\t\treturn MetaThriftIDLResult_Success_DEFAULT\n\t}\n\treturn p.Success\n}\nfunc (p *MetaThriftIDLResult) IsSetSuccess() bool {\n\treturn p.Success != nil\n}\n\nfunc (p *MetaThriftIDLResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 0:\n\t\t\tif err := p.ReadField0(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaThriftIDLResult) ReadField0(iprot thrift.TProtocol) error {\n\tp.Success = &ThriftIDLs{}\n\tif err := p.Success.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.Success), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaThriftIDLResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"thriftIDL_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField0(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaThriftIDLResult) writeField0(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSuccess() {\n\t\tif err := oprot.WriteFieldBegin(\"success\", thrift.STRUCT, 0); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 0:success: \", p), err)\n\t\t}\n\t\tif err := p.Success.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.Success), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 0:success: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *MetaThriftIDLResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"MetaThriftIDLResult(%+v)\", *p)\n}\n\ntype MetaVersionInfoArgs struct {\n}\n\nfunc NewMetaVersionInfoArgs() *MetaVersionInfoArgs {\n\treturn &MetaVersionInfoArgs{}\n}\n\nfunc (p *MetaVersionInfoArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaVersionInfoArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"versionInfo_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaVersionInfoArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"MetaVersionInfoArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Success\ntype MetaVersionInfoResult struct {\n\tSuccess *VersionInfo `thrift:\"success,0\" db:\"success\" json:\"success,omitempty\"`\n}\n\nfunc NewMetaVersionInfoResult() *MetaVersionInfoResult {\n\treturn &MetaVersionInfoResult{}\n}\n\nvar MetaVersionInfoResult_Success_DEFAULT *VersionInfo\n\nfunc (p *MetaVersionInfoResult) GetSuccess() *VersionInfo {\n\tif !p.IsSetSuccess() {\n\t\treturn MetaVersionInfoResult_Success_DEFAULT\n\t}\n\treturn p.Success\n}\nfunc (p *MetaVersionInfoResult) IsSetSuccess() bool {\n\treturn p.Success != nil\n}\n\nfunc (p *MetaVersionInfoResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 0:\n\t\t\tif err := p.ReadField0(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaVersionInfoResult) ReadField0(iprot thrift.TProtocol) error {\n\tp.Success = &VersionInfo{}\n\tif err := p.Success.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.Success), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaVersionInfoResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"versionInfo_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField0(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaVersionInfoResult) writeField0(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSuccess() {\n\t\tif err := oprot.WriteFieldBegin(\"success\", thrift.STRUCT, 0); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 0:success: \", p), err)\n\t\t}\n\t\tif err := p.Success.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.Success), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 0:success: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *MetaVersionInfoResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"MetaVersionInfoResult(%+v)\", *p)\n}\n"
  },
  {
    "path": "thrift/gen-go/meta/ttypes.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage meta\n\nimport (\n\t\"bytes\"\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\nvar GoUnusedProtection__ int\n\ntype HealthState int64\n\nconst (\n\tHealthState_REFUSING  HealthState = 0\n\tHealthState_ACCEPTING HealthState = 1\n\tHealthState_STOPPING  HealthState = 2\n\tHealthState_STOPPED   HealthState = 3\n)\n\nfunc (p HealthState) String() string {\n\tswitch p {\n\tcase HealthState_REFUSING:\n\t\treturn \"REFUSING\"\n\tcase HealthState_ACCEPTING:\n\t\treturn \"ACCEPTING\"\n\tcase HealthState_STOPPING:\n\t\treturn \"STOPPING\"\n\tcase HealthState_STOPPED:\n\t\treturn \"STOPPED\"\n\t}\n\treturn \"<UNSET>\"\n}\n\nfunc HealthStateFromString(s string) (HealthState, error) {\n\tswitch s {\n\tcase \"REFUSING\":\n\t\treturn HealthState_REFUSING, nil\n\tcase \"ACCEPTING\":\n\t\treturn HealthState_ACCEPTING, nil\n\tcase \"STOPPING\":\n\t\treturn HealthState_STOPPING, nil\n\tcase \"STOPPED\":\n\t\treturn HealthState_STOPPED, nil\n\t}\n\treturn HealthState(0), fmt.Errorf(\"not a valid HealthState string\")\n}\n\nfunc HealthStatePtr(v HealthState) *HealthState { return &v }\n\nfunc (p HealthState) MarshalText() ([]byte, error) {\n\treturn []byte(p.String()), nil\n}\n\nfunc (p *HealthState) UnmarshalText(text []byte) error {\n\tq, err := HealthStateFromString(string(text))\n\tif err != nil {\n\t\treturn err\n\t}\n\t*p = q\n\treturn nil\n}\n\nfunc (p *HealthState) Scan(value interface{}) error {\n\tv, ok := value.(int64)\n\tif !ok {\n\t\treturn errors.New(\"Scan value is not int64\")\n\t}\n\t*p = HealthState(v)\n\treturn nil\n}\n\nfunc (p *HealthState) Value() (driver.Value, error) {\n\tif p == nil {\n\t\treturn nil, nil\n\t}\n\treturn int64(*p), nil\n}\n\ntype HealthRequestType int64\n\nconst (\n\tHealthRequestType_PROCESS HealthRequestType = 0\n\tHealthRequestType_TRAFFIC HealthRequestType = 1\n)\n\nfunc (p HealthRequestType) String() string {\n\tswitch p {\n\tcase HealthRequestType_PROCESS:\n\t\treturn \"PROCESS\"\n\tcase HealthRequestType_TRAFFIC:\n\t\treturn \"TRAFFIC\"\n\t}\n\treturn \"<UNSET>\"\n}\n\nfunc HealthRequestTypeFromString(s string) (HealthRequestType, error) {\n\tswitch s {\n\tcase \"PROCESS\":\n\t\treturn HealthRequestType_PROCESS, nil\n\tcase \"TRAFFIC\":\n\t\treturn HealthRequestType_TRAFFIC, nil\n\t}\n\treturn HealthRequestType(0), fmt.Errorf(\"not a valid HealthRequestType string\")\n}\n\nfunc HealthRequestTypePtr(v HealthRequestType) *HealthRequestType { return &v }\n\nfunc (p HealthRequestType) MarshalText() ([]byte, error) {\n\treturn []byte(p.String()), nil\n}\n\nfunc (p *HealthRequestType) UnmarshalText(text []byte) error {\n\tq, err := HealthRequestTypeFromString(string(text))\n\tif err != nil {\n\t\treturn err\n\t}\n\t*p = q\n\treturn nil\n}\n\nfunc (p *HealthRequestType) Scan(value interface{}) error {\n\tv, ok := value.(int64)\n\tif !ok {\n\t\treturn errors.New(\"Scan value is not int64\")\n\t}\n\t*p = HealthRequestType(v)\n\treturn nil\n}\n\nfunc (p *HealthRequestType) Value() (driver.Value, error) {\n\tif p == nil {\n\t\treturn nil, nil\n\t}\n\treturn int64(*p), nil\n}\n\ntype Filename string\n\nfunc FilenamePtr(v Filename) *Filename { return &v }\n\n// Attributes:\n//   - Type\ntype HealthRequest struct {\n\tType *HealthRequestType `thrift:\"type,1\" db:\"type\" json:\"type,omitempty\"`\n}\n\nfunc NewHealthRequest() *HealthRequest {\n\treturn &HealthRequest{}\n}\n\nvar HealthRequest_Type_DEFAULT HealthRequestType\n\nfunc (p *HealthRequest) GetType() HealthRequestType {\n\tif !p.IsSetType() {\n\t\treturn HealthRequest_Type_DEFAULT\n\t}\n\treturn *p.Type\n}\nfunc (p *HealthRequest) IsSetType() bool {\n\treturn p.Type != nil\n}\n\nfunc (p *HealthRequest) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *HealthRequest) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadI32(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\ttemp := HealthRequestType(v)\n\t\tp.Type = &temp\n\t}\n\treturn nil\n}\n\nfunc (p *HealthRequest) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"HealthRequest\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *HealthRequest) writeField1(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetType() {\n\t\tif err := oprot.WriteFieldBegin(\"type\", thrift.I32, 1); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:type: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteI32(int32(*p.Type)); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.type (1) field write error: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:type: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *HealthRequest) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"HealthRequest(%+v)\", *p)\n}\n\n// Attributes:\n//   - Ok\n//   - Message\n//   - State\ntype HealthStatus struct {\n\tOk      bool         `thrift:\"ok,1,required\" db:\"ok\" json:\"ok\"`\n\tMessage *string      `thrift:\"message,2\" db:\"message\" json:\"message,omitempty\"`\n\tState   *HealthState `thrift:\"state,3\" db:\"state\" json:\"state,omitempty\"`\n}\n\nfunc NewHealthStatus() *HealthStatus {\n\treturn &HealthStatus{}\n}\n\nfunc (p *HealthStatus) GetOk() bool {\n\treturn p.Ok\n}\n\nvar HealthStatus_Message_DEFAULT string\n\nfunc (p *HealthStatus) GetMessage() string {\n\tif !p.IsSetMessage() {\n\t\treturn HealthStatus_Message_DEFAULT\n\t}\n\treturn *p.Message\n}\n\nvar HealthStatus_State_DEFAULT HealthState\n\nfunc (p *HealthStatus) GetState() HealthState {\n\tif !p.IsSetState() {\n\t\treturn HealthStatus_State_DEFAULT\n\t}\n\treturn *p.State\n}\nfunc (p *HealthStatus) IsSetMessage() bool {\n\treturn p.Message != nil\n}\n\nfunc (p *HealthStatus) IsSetState() bool {\n\treturn p.State != nil\n}\n\nfunc (p *HealthStatus) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tvar issetOk bool = false\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetOk = true\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 3:\n\t\t\tif err := p.ReadField3(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\tif !issetOk {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field Ok is not set\"))\n\t}\n\treturn nil\n}\n\nfunc (p *HealthStatus) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadBool(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Ok = v\n\t}\n\treturn nil\n}\n\nfunc (p *HealthStatus) ReadField2(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 2: \", err)\n\t} else {\n\t\tp.Message = &v\n\t}\n\treturn nil\n}\n\nfunc (p *HealthStatus) ReadField3(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadI32(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 3: \", err)\n\t} else {\n\t\ttemp := HealthState(v)\n\t\tp.State = &temp\n\t}\n\treturn nil\n}\n\nfunc (p *HealthStatus) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"HealthStatus\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField3(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *HealthStatus) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"ok\", thrift.BOOL, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:ok: \", p), err)\n\t}\n\tif err := oprot.WriteBool(bool(p.Ok)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.ok (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:ok: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *HealthStatus) writeField2(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetMessage() {\n\t\tif err := oprot.WriteFieldBegin(\"message\", thrift.STRING, 2); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:message: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteString(string(*p.Message)); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.message (2) field write error: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:message: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *HealthStatus) writeField3(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetState() {\n\t\tif err := oprot.WriteFieldBegin(\"state\", thrift.I32, 3); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 3:state: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteI32(int32(*p.State)); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.state (3) field write error: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 3:state: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *HealthStatus) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"HealthStatus(%+v)\", *p)\n}\n\n// Attributes:\n//   - Idls\n//   - EntryPoint\ntype ThriftIDLs struct {\n\tIdls       map[Filename]string `thrift:\"idls,1,required\" db:\"idls\" json:\"idls\"`\n\tEntryPoint Filename            `thrift:\"entryPoint,2,required\" db:\"entryPoint\" json:\"entryPoint\"`\n}\n\nfunc NewThriftIDLs() *ThriftIDLs {\n\treturn &ThriftIDLs{}\n}\n\nfunc (p *ThriftIDLs) GetIdls() map[Filename]string {\n\treturn p.Idls\n}\n\nfunc (p *ThriftIDLs) GetEntryPoint() Filename {\n\treturn p.EntryPoint\n}\nfunc (p *ThriftIDLs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tvar issetIdls bool = false\n\tvar issetEntryPoint bool = false\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetIdls = true\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetEntryPoint = true\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\tif !issetIdls {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field Idls is not set\"))\n\t}\n\tif !issetEntryPoint {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field EntryPoint is not set\"))\n\t}\n\treturn nil\n}\n\nfunc (p *ThriftIDLs) ReadField1(iprot thrift.TProtocol) error {\n\t_, _, size, err := iprot.ReadMapBegin()\n\tif err != nil {\n\t\treturn thrift.PrependError(\"error reading map begin: \", err)\n\t}\n\ttMap := make(map[Filename]string, size)\n\tp.Idls = tMap\n\tfor i := 0; i < size; i++ {\n\t\tvar _key0 Filename\n\t\tif v, err := iprot.ReadString(); err != nil {\n\t\t\treturn thrift.PrependError(\"error reading field 0: \", err)\n\t\t} else {\n\t\t\ttemp := Filename(v)\n\t\t\t_key0 = temp\n\t\t}\n\t\tvar _val1 string\n\t\tif v, err := iprot.ReadString(); err != nil {\n\t\t\treturn thrift.PrependError(\"error reading field 0: \", err)\n\t\t} else {\n\t\t\t_val1 = v\n\t\t}\n\t\tp.Idls[_key0] = _val1\n\t}\n\tif err := iprot.ReadMapEnd(); err != nil {\n\t\treturn thrift.PrependError(\"error reading map end: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *ThriftIDLs) ReadField2(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 2: \", err)\n\t} else {\n\t\ttemp := Filename(v)\n\t\tp.EntryPoint = temp\n\t}\n\treturn nil\n}\n\nfunc (p *ThriftIDLs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"ThriftIDLs\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *ThriftIDLs) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"idls\", thrift.MAP, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:idls: \", p), err)\n\t}\n\tif err := oprot.WriteMapBegin(thrift.STRING, thrift.STRING, len(p.Idls)); err != nil {\n\t\treturn thrift.PrependError(\"error writing map begin: \", err)\n\t}\n\tfor k, v := range p.Idls {\n\t\tif err := oprot.WriteString(string(k)); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T. (0) field write error: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteString(string(v)); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T. (0) field write error: \", p), err)\n\t\t}\n\t}\n\tif err := oprot.WriteMapEnd(); err != nil {\n\t\treturn thrift.PrependError(\"error writing map end: \", err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:idls: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *ThriftIDLs) writeField2(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"entryPoint\", thrift.STRING, 2); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:entryPoint: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.EntryPoint)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.entryPoint (2) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:entryPoint: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *ThriftIDLs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"ThriftIDLs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Language\n//   - LanguageVersion\n//   - Version\ntype VersionInfo struct {\n\tLanguage        string `thrift:\"language,1,required\" db:\"language\" json:\"language\"`\n\tLanguageVersion string `thrift:\"language_version,2,required\" db:\"language_version\" json:\"language_version\"`\n\tVersion         string `thrift:\"version,3,required\" db:\"version\" json:\"version\"`\n}\n\nfunc NewVersionInfo() *VersionInfo {\n\treturn &VersionInfo{}\n}\n\nfunc (p *VersionInfo) GetLanguage() string {\n\treturn p.Language\n}\n\nfunc (p *VersionInfo) GetLanguageVersion() string {\n\treturn p.LanguageVersion\n}\n\nfunc (p *VersionInfo) GetVersion() string {\n\treturn p.Version\n}\nfunc (p *VersionInfo) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tvar issetLanguage bool = false\n\tvar issetLanguageVersion bool = false\n\tvar issetVersion bool = false\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetLanguage = true\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetLanguageVersion = true\n\t\tcase 3:\n\t\t\tif err := p.ReadField3(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetVersion = true\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\tif !issetLanguage {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field Language is not set\"))\n\t}\n\tif !issetLanguageVersion {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field LanguageVersion is not set\"))\n\t}\n\tif !issetVersion {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field Version is not set\"))\n\t}\n\treturn nil\n}\n\nfunc (p *VersionInfo) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Language = v\n\t}\n\treturn nil\n}\n\nfunc (p *VersionInfo) ReadField2(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 2: \", err)\n\t} else {\n\t\tp.LanguageVersion = v\n\t}\n\treturn nil\n}\n\nfunc (p *VersionInfo) ReadField3(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 3: \", err)\n\t} else {\n\t\tp.Version = v\n\t}\n\treturn nil\n}\n\nfunc (p *VersionInfo) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"VersionInfo\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField3(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *VersionInfo) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"language\", thrift.STRING, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:language: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Language)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.language (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:language: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *VersionInfo) writeField2(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"language_version\", thrift.STRING, 2); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:language_version: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.LanguageVersion)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.language_version (2) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:language_version: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *VersionInfo) writeField3(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"version\", thrift.STRING, 3); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 3:version: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Version)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.version (3) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 3:version: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *VersionInfo) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"VersionInfo(%+v)\", *p)\n}\n"
  },
  {
    "path": "thrift/gen-go/test/constants.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\nfunc init() {\n}\n"
  },
  {
    "path": "thrift/gen-go/test/meta.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\ntype Meta interface {\n\tHealth() (r *HealthStatus, err error)\n}\n\ntype MetaClient struct {\n\tTransport       thrift.TTransport\n\tProtocolFactory thrift.TProtocolFactory\n\tInputProtocol   thrift.TProtocol\n\tOutputProtocol  thrift.TProtocol\n\tSeqId           int32\n}\n\nfunc NewMetaClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *MetaClient {\n\treturn &MetaClient{Transport: t,\n\t\tProtocolFactory: f,\n\t\tInputProtocol:   f.GetProtocol(t),\n\t\tOutputProtocol:  f.GetProtocol(t),\n\t\tSeqId:           0,\n\t}\n}\n\nfunc NewMetaClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *MetaClient {\n\treturn &MetaClient{Transport: t,\n\t\tProtocolFactory: nil,\n\t\tInputProtocol:   iprot,\n\t\tOutputProtocol:  oprot,\n\t\tSeqId:           0,\n\t}\n}\n\nfunc (p *MetaClient) Health() (r *HealthStatus, err error) {\n\tif err = p.sendHealth(); err != nil {\n\t\treturn\n\t}\n\treturn p.recvHealth()\n}\n\nfunc (p *MetaClient) sendHealth() (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"health\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := MetaHealthArgs{}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *MetaClient) recvHealth() (value *HealthStatus, err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"health\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"health failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"health failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror19 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error20 error\n\t\terror20, err = error19.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error20\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"health failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := MetaHealthResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tvalue = result.GetSuccess()\n\treturn\n}\n\ntype MetaProcessor struct {\n\tprocessorMap map[string]thrift.TProcessorFunction\n\thandler      Meta\n}\n\nfunc (p *MetaProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {\n\tp.processorMap[key] = processor\n}\n\nfunc (p *MetaProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {\n\tprocessor, ok = p.processorMap[key]\n\treturn processor, ok\n}\n\nfunc (p *MetaProcessor) ProcessorMap() map[string]thrift.TProcessorFunction {\n\treturn p.processorMap\n}\n\nfunc NewMetaProcessor(handler Meta) *MetaProcessor {\n\n\tself21 := &MetaProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)}\n\tself21.processorMap[\"health\"] = &metaProcessorHealth{handler: handler}\n\treturn self21\n}\n\nfunc (p *MetaProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\tname, _, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif processor, ok := p.GetProcessorFunction(name); ok {\n\t\treturn processor.Process(seqId, iprot, oprot)\n\t}\n\tiprot.Skip(thrift.STRUCT)\n\tiprot.ReadMessageEnd()\n\tx22 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function \"+name)\n\toprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)\n\tx22.Write(oprot)\n\toprot.WriteMessageEnd()\n\toprot.Flush()\n\treturn false, x22\n\n}\n\ntype metaProcessorHealth struct {\n\thandler Meta\n}\n\nfunc (p *metaProcessorHealth) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := MetaHealthArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"health\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := MetaHealthResult{}\n\tvar retval *HealthStatus\n\tvar err2 error\n\tif retval, err2 = p.handler.Health(); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing health: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"health\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t} else {\n\t\tresult.Success = retval\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"health\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\n// HELPER FUNCTIONS AND STRUCTURES\n\ntype MetaHealthArgs struct {\n}\n\nfunc NewMetaHealthArgs() *MetaHealthArgs {\n\treturn &MetaHealthArgs{}\n}\n\nfunc (p *MetaHealthArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaHealthArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"health_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaHealthArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"MetaHealthArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Success\ntype MetaHealthResult struct {\n\tSuccess *HealthStatus `thrift:\"success,0\" db:\"success\" json:\"success,omitempty\"`\n}\n\nfunc NewMetaHealthResult() *MetaHealthResult {\n\treturn &MetaHealthResult{}\n}\n\nvar MetaHealthResult_Success_DEFAULT *HealthStatus\n\nfunc (p *MetaHealthResult) GetSuccess() *HealthStatus {\n\tif !p.IsSetSuccess() {\n\t\treturn MetaHealthResult_Success_DEFAULT\n\t}\n\treturn p.Success\n}\nfunc (p *MetaHealthResult) IsSetSuccess() bool {\n\treturn p.Success != nil\n}\n\nfunc (p *MetaHealthResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 0:\n\t\t\tif err := p.ReadField0(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaHealthResult) ReadField0(iprot thrift.TProtocol) error {\n\tp.Success = &HealthStatus{}\n\tif err := p.Success.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.Success), err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaHealthResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"health_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField0(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *MetaHealthResult) writeField0(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSuccess() {\n\t\tif err := oprot.WriteFieldBegin(\"success\", thrift.STRUCT, 0); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 0:success: \", p), err)\n\t\t}\n\t\tif err := p.Success.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.Success), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 0:success: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *MetaHealthResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"MetaHealthResult(%+v)\", *p)\n}\n"
  },
  {
    "path": "thrift/gen-go/test/secondservice.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\ntype SecondService interface {\n\t// Parameters:\n\t//  - Arg\n\tEcho(arg string) (r string, err error)\n}\n\ntype SecondServiceClient struct {\n\tTransport       thrift.TTransport\n\tProtocolFactory thrift.TProtocolFactory\n\tInputProtocol   thrift.TProtocol\n\tOutputProtocol  thrift.TProtocol\n\tSeqId           int32\n}\n\nfunc NewSecondServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *SecondServiceClient {\n\treturn &SecondServiceClient{Transport: t,\n\t\tProtocolFactory: f,\n\t\tInputProtocol:   f.GetProtocol(t),\n\t\tOutputProtocol:  f.GetProtocol(t),\n\t\tSeqId:           0,\n\t}\n}\n\nfunc NewSecondServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *SecondServiceClient {\n\treturn &SecondServiceClient{Transport: t,\n\t\tProtocolFactory: nil,\n\t\tInputProtocol:   iprot,\n\t\tOutputProtocol:  oprot,\n\t\tSeqId:           0,\n\t}\n}\n\n// Parameters:\n//   - Arg\nfunc (p *SecondServiceClient) Echo(arg string) (r string, err error) {\n\tif err = p.sendEcho(arg); err != nil {\n\t\treturn\n\t}\n\treturn p.recvEcho()\n}\n\nfunc (p *SecondServiceClient) sendEcho(arg string) (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"Echo\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := SecondServiceEchoArgs{\n\t\tArg: arg,\n\t}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *SecondServiceClient) recvEcho() (value string, err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"Echo\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"Echo failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"Echo failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror14 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error15 error\n\t\terror15, err = error14.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error15\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"Echo failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := SecondServiceEchoResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tvalue = result.GetSuccess()\n\treturn\n}\n\ntype SecondServiceProcessor struct {\n\tprocessorMap map[string]thrift.TProcessorFunction\n\thandler      SecondService\n}\n\nfunc (p *SecondServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {\n\tp.processorMap[key] = processor\n}\n\nfunc (p *SecondServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {\n\tprocessor, ok = p.processorMap[key]\n\treturn processor, ok\n}\n\nfunc (p *SecondServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction {\n\treturn p.processorMap\n}\n\nfunc NewSecondServiceProcessor(handler SecondService) *SecondServiceProcessor {\n\n\tself16 := &SecondServiceProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)}\n\tself16.processorMap[\"Echo\"] = &secondServiceProcessorEcho{handler: handler}\n\treturn self16\n}\n\nfunc (p *SecondServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\tname, _, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif processor, ok := p.GetProcessorFunction(name); ok {\n\t\treturn processor.Process(seqId, iprot, oprot)\n\t}\n\tiprot.Skip(thrift.STRUCT)\n\tiprot.ReadMessageEnd()\n\tx17 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function \"+name)\n\toprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)\n\tx17.Write(oprot)\n\toprot.WriteMessageEnd()\n\toprot.Flush()\n\treturn false, x17\n\n}\n\ntype secondServiceProcessorEcho struct {\n\thandler SecondService\n}\n\nfunc (p *secondServiceProcessorEcho) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := SecondServiceEchoArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"Echo\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := SecondServiceEchoResult{}\n\tvar retval string\n\tvar err2 error\n\tif retval, err2 = p.handler.Echo(args.Arg); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing Echo: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"Echo\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t} else {\n\t\tresult.Success = &retval\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"Echo\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\n// HELPER FUNCTIONS AND STRUCTURES\n\n// Attributes:\n//   - Arg\ntype SecondServiceEchoArgs struct {\n\tArg string `thrift:\"arg,1\" db:\"arg\" json:\"arg\"`\n}\n\nfunc NewSecondServiceEchoArgs() *SecondServiceEchoArgs {\n\treturn &SecondServiceEchoArgs{}\n}\n\nfunc (p *SecondServiceEchoArgs) GetArg() string {\n\treturn p.Arg\n}\nfunc (p *SecondServiceEchoArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SecondServiceEchoArgs) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Arg = v\n\t}\n\treturn nil\n}\n\nfunc (p *SecondServiceEchoArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Echo_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *SecondServiceEchoArgs) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"arg\", thrift.STRING, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:arg: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Arg)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.arg (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:arg: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *SecondServiceEchoArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"SecondServiceEchoArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Success\ntype SecondServiceEchoResult struct {\n\tSuccess *string `thrift:\"success,0\" db:\"success\" json:\"success,omitempty\"`\n}\n\nfunc NewSecondServiceEchoResult() *SecondServiceEchoResult {\n\treturn &SecondServiceEchoResult{}\n}\n\nvar SecondServiceEchoResult_Success_DEFAULT string\n\nfunc (p *SecondServiceEchoResult) GetSuccess() string {\n\tif !p.IsSetSuccess() {\n\t\treturn SecondServiceEchoResult_Success_DEFAULT\n\t}\n\treturn *p.Success\n}\nfunc (p *SecondServiceEchoResult) IsSetSuccess() bool {\n\treturn p.Success != nil\n}\n\nfunc (p *SecondServiceEchoResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 0:\n\t\t\tif err := p.ReadField0(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SecondServiceEchoResult) ReadField0(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 0: \", err)\n\t} else {\n\t\tp.Success = &v\n\t}\n\treturn nil\n}\n\nfunc (p *SecondServiceEchoResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Echo_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField0(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *SecondServiceEchoResult) writeField0(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSuccess() {\n\t\tif err := oprot.WriteFieldBegin(\"success\", thrift.STRING, 0); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 0:success: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteString(string(*p.Success)); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.success (0) field write error: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 0:success: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *SecondServiceEchoResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"SecondServiceEchoResult(%+v)\", *p)\n}\n"
  },
  {
    "path": "thrift/gen-go/test/simpleservice.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\ntype SimpleService interface {\n\t// Parameters:\n\t//  - Arg\n\tCall(arg *Data) (r *Data, err error)\n\tSimple() (err error)\n\tSimpleFuture() (err error)\n}\n\ntype SimpleServiceClient struct {\n\tTransport       thrift.TTransport\n\tProtocolFactory thrift.TProtocolFactory\n\tInputProtocol   thrift.TProtocol\n\tOutputProtocol  thrift.TProtocol\n\tSeqId           int32\n}\n\nfunc NewSimpleServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *SimpleServiceClient {\n\treturn &SimpleServiceClient{Transport: t,\n\t\tProtocolFactory: f,\n\t\tInputProtocol:   f.GetProtocol(t),\n\t\tOutputProtocol:  f.GetProtocol(t),\n\t\tSeqId:           0,\n\t}\n}\n\nfunc NewSimpleServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *SimpleServiceClient {\n\treturn &SimpleServiceClient{Transport: t,\n\t\tProtocolFactory: nil,\n\t\tInputProtocol:   iprot,\n\t\tOutputProtocol:  oprot,\n\t\tSeqId:           0,\n\t}\n}\n\n// Parameters:\n//   - Arg\nfunc (p *SimpleServiceClient) Call(arg *Data) (r *Data, err error) {\n\tif err = p.sendCall(arg); err != nil {\n\t\treturn\n\t}\n\treturn p.recvCall()\n}\n\nfunc (p *SimpleServiceClient) sendCall(arg *Data) (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"Call\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := SimpleServiceCallArgs{\n\t\tArg: arg,\n\t}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *SimpleServiceClient) recvCall() (value *Data, err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"Call\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"Call failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"Call failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error1 error\n\t\terror1, err = error0.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error1\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"Call failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := SimpleServiceCallResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tvalue = result.GetSuccess()\n\treturn\n}\n\nfunc (p *SimpleServiceClient) Simple() (err error) {\n\tif err = p.sendSimple(); err != nil {\n\t\treturn\n\t}\n\treturn p.recvSimple()\n}\n\nfunc (p *SimpleServiceClient) sendSimple() (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"Simple\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := SimpleServiceSimpleArgs{}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *SimpleServiceClient) recvSimple() (err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"Simple\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"Simple failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"Simple failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error3 error\n\t\terror3, err = error2.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error3\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"Simple failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := SimpleServiceSimpleResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tif result.SimpleErr != nil {\n\t\terr = result.SimpleErr\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (p *SimpleServiceClient) SimpleFuture() (err error) {\n\tif err = p.sendSimpleFuture(); err != nil {\n\t\treturn\n\t}\n\treturn p.recvSimpleFuture()\n}\n\nfunc (p *SimpleServiceClient) sendSimpleFuture() (err error) {\n\toprot := p.OutputProtocol\n\tif oprot == nil {\n\t\toprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.OutputProtocol = oprot\n\t}\n\tp.SeqId++\n\tif err = oprot.WriteMessageBegin(\"SimpleFuture\", thrift.CALL, p.SeqId); err != nil {\n\t\treturn\n\t}\n\targs := SimpleServiceSimpleFutureArgs{}\n\tif err = args.Write(oprot); err != nil {\n\t\treturn\n\t}\n\tif err = oprot.WriteMessageEnd(); err != nil {\n\t\treturn\n\t}\n\treturn oprot.Flush()\n}\n\nfunc (p *SimpleServiceClient) recvSimpleFuture() (err error) {\n\tiprot := p.InputProtocol\n\tif iprot == nil {\n\t\tiprot = p.ProtocolFactory.GetProtocol(p.Transport)\n\t\tp.InputProtocol = iprot\n\t}\n\tmethod, mTypeId, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn\n\t}\n\tif method != \"SimpleFuture\" {\n\t\terr = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, \"SimpleFuture failed: wrong method name\")\n\t\treturn\n\t}\n\tif p.SeqId != seqId {\n\t\terr = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"SimpleFuture failed: out of sequence response\")\n\t\treturn\n\t}\n\tif mTypeId == thrift.EXCEPTION {\n\t\terror4 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")\n\t\tvar error5 error\n\t\terror5, err = error4.Read(iprot)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = error5\n\t\treturn\n\t}\n\tif mTypeId != thrift.REPLY {\n\t\terr = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"SimpleFuture failed: invalid message type\")\n\t\treturn\n\t}\n\tresult := SimpleServiceSimpleFutureResult{}\n\tif err = result.Read(iprot); err != nil {\n\t\treturn\n\t}\n\tif err = iprot.ReadMessageEnd(); err != nil {\n\t\treturn\n\t}\n\tif result.SimpleErr != nil {\n\t\terr = result.SimpleErr\n\t\treturn\n\t} else if result.NewErr_ != nil {\n\t\terr = result.NewErr_\n\t\treturn\n\t}\n\treturn\n}\n\ntype SimpleServiceProcessor struct {\n\tprocessorMap map[string]thrift.TProcessorFunction\n\thandler      SimpleService\n}\n\nfunc (p *SimpleServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {\n\tp.processorMap[key] = processor\n}\n\nfunc (p *SimpleServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {\n\tprocessor, ok = p.processorMap[key]\n\treturn processor, ok\n}\n\nfunc (p *SimpleServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction {\n\treturn p.processorMap\n}\n\nfunc NewSimpleServiceProcessor(handler SimpleService) *SimpleServiceProcessor {\n\n\tself6 := &SimpleServiceProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)}\n\tself6.processorMap[\"Call\"] = &simpleServiceProcessorCall{handler: handler}\n\tself6.processorMap[\"Simple\"] = &simpleServiceProcessorSimple{handler: handler}\n\tself6.processorMap[\"SimpleFuture\"] = &simpleServiceProcessorSimpleFuture{handler: handler}\n\treturn self6\n}\n\nfunc (p *SimpleServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\tname, _, seqId, err := iprot.ReadMessageBegin()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif processor, ok := p.GetProcessorFunction(name); ok {\n\t\treturn processor.Process(seqId, iprot, oprot)\n\t}\n\tiprot.Skip(thrift.STRUCT)\n\tiprot.ReadMessageEnd()\n\tx7 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function \"+name)\n\toprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)\n\tx7.Write(oprot)\n\toprot.WriteMessageEnd()\n\toprot.Flush()\n\treturn false, x7\n\n}\n\ntype simpleServiceProcessorCall struct {\n\thandler SimpleService\n}\n\nfunc (p *simpleServiceProcessorCall) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := SimpleServiceCallArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"Call\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := SimpleServiceCallResult{}\n\tvar retval *Data\n\tvar err2 error\n\tif retval, err2 = p.handler.Call(args.Arg); err2 != nil {\n\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing Call: \"+err2.Error())\n\t\toprot.WriteMessageBegin(\"Call\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn true, err2\n\t} else {\n\t\tresult.Success = retval\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"Call\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\ntype simpleServiceProcessorSimple struct {\n\thandler SimpleService\n}\n\nfunc (p *simpleServiceProcessorSimple) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := SimpleServiceSimpleArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"Simple\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := SimpleServiceSimpleResult{}\n\tvar err2 error\n\tif err2 = p.handler.Simple(); err2 != nil {\n\t\tswitch v := err2.(type) {\n\t\tcase *SimpleErr:\n\t\t\tresult.SimpleErr = v\n\t\tdefault:\n\t\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing Simple: \"+err2.Error())\n\t\t\toprot.WriteMessageBegin(\"Simple\", thrift.EXCEPTION, seqId)\n\t\t\tx.Write(oprot)\n\t\t\toprot.WriteMessageEnd()\n\t\t\toprot.Flush()\n\t\t\treturn true, err2\n\t\t}\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"Simple\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\ntype simpleServiceProcessorSimpleFuture struct {\n\thandler SimpleService\n}\n\nfunc (p *simpleServiceProcessorSimpleFuture) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {\n\targs := SimpleServiceSimpleFutureArgs{}\n\tif err = args.Read(iprot); err != nil {\n\t\tiprot.ReadMessageEnd()\n\t\tx := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())\n\t\toprot.WriteMessageBegin(\"SimpleFuture\", thrift.EXCEPTION, seqId)\n\t\tx.Write(oprot)\n\t\toprot.WriteMessageEnd()\n\t\toprot.Flush()\n\t\treturn false, err\n\t}\n\n\tiprot.ReadMessageEnd()\n\tresult := SimpleServiceSimpleFutureResult{}\n\tvar err2 error\n\tif err2 = p.handler.SimpleFuture(); err2 != nil {\n\t\tswitch v := err2.(type) {\n\t\tcase *SimpleErr:\n\t\t\tresult.SimpleErr = v\n\t\tcase *NewErr_:\n\t\t\tresult.NewErr_ = v\n\t\tdefault:\n\t\t\tx := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing SimpleFuture: \"+err2.Error())\n\t\t\toprot.WriteMessageBegin(\"SimpleFuture\", thrift.EXCEPTION, seqId)\n\t\t\tx.Write(oprot)\n\t\t\toprot.WriteMessageEnd()\n\t\t\toprot.Flush()\n\t\t\treturn true, err2\n\t\t}\n\t}\n\tif err2 = oprot.WriteMessageBegin(\"SimpleFuture\", thrift.REPLY, seqId); err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = result.Write(oprot); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err2 = oprot.Flush(); err == nil && err2 != nil {\n\t\terr = err2\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn true, err\n}\n\n// HELPER FUNCTIONS AND STRUCTURES\n\n// Attributes:\n//   - Arg\ntype SimpleServiceCallArgs struct {\n\tArg *Data `thrift:\"arg,1\" db:\"arg\" json:\"arg\"`\n}\n\nfunc NewSimpleServiceCallArgs() *SimpleServiceCallArgs {\n\treturn &SimpleServiceCallArgs{}\n}\n\nvar SimpleServiceCallArgs_Arg_DEFAULT *Data\n\nfunc (p *SimpleServiceCallArgs) GetArg() *Data {\n\tif !p.IsSetArg() {\n\t\treturn SimpleServiceCallArgs_Arg_DEFAULT\n\t}\n\treturn p.Arg\n}\nfunc (p *SimpleServiceCallArgs) IsSetArg() bool {\n\treturn p.Arg != nil\n}\n\nfunc (p *SimpleServiceCallArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceCallArgs) ReadField1(iprot thrift.TProtocol) error {\n\tp.Arg = &Data{}\n\tif err := p.Arg.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.Arg), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceCallArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Call_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceCallArgs) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"arg\", thrift.STRUCT, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:arg: \", p), err)\n\t}\n\tif err := p.Arg.Write(oprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.Arg), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:arg: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *SimpleServiceCallArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"SimpleServiceCallArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - Success\ntype SimpleServiceCallResult struct {\n\tSuccess *Data `thrift:\"success,0\" db:\"success\" json:\"success,omitempty\"`\n}\n\nfunc NewSimpleServiceCallResult() *SimpleServiceCallResult {\n\treturn &SimpleServiceCallResult{}\n}\n\nvar SimpleServiceCallResult_Success_DEFAULT *Data\n\nfunc (p *SimpleServiceCallResult) GetSuccess() *Data {\n\tif !p.IsSetSuccess() {\n\t\treturn SimpleServiceCallResult_Success_DEFAULT\n\t}\n\treturn p.Success\n}\nfunc (p *SimpleServiceCallResult) IsSetSuccess() bool {\n\treturn p.Success != nil\n}\n\nfunc (p *SimpleServiceCallResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 0:\n\t\t\tif err := p.ReadField0(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceCallResult) ReadField0(iprot thrift.TProtocol) error {\n\tp.Success = &Data{}\n\tif err := p.Success.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.Success), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceCallResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Call_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField0(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceCallResult) writeField0(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSuccess() {\n\t\tif err := oprot.WriteFieldBegin(\"success\", thrift.STRUCT, 0); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 0:success: \", p), err)\n\t\t}\n\t\tif err := p.Success.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.Success), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 0:success: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *SimpleServiceCallResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"SimpleServiceCallResult(%+v)\", *p)\n}\n\ntype SimpleServiceSimpleArgs struct {\n}\n\nfunc NewSimpleServiceSimpleArgs() *SimpleServiceSimpleArgs {\n\treturn &SimpleServiceSimpleArgs{}\n}\n\nfunc (p *SimpleServiceSimpleArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceSimpleArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Simple_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceSimpleArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"SimpleServiceSimpleArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - SimpleErr\ntype SimpleServiceSimpleResult struct {\n\tSimpleErr *SimpleErr `thrift:\"simpleErr,1\" db:\"simpleErr\" json:\"simpleErr,omitempty\"`\n}\n\nfunc NewSimpleServiceSimpleResult() *SimpleServiceSimpleResult {\n\treturn &SimpleServiceSimpleResult{}\n}\n\nvar SimpleServiceSimpleResult_SimpleErr_DEFAULT *SimpleErr\n\nfunc (p *SimpleServiceSimpleResult) GetSimpleErr() *SimpleErr {\n\tif !p.IsSetSimpleErr() {\n\t\treturn SimpleServiceSimpleResult_SimpleErr_DEFAULT\n\t}\n\treturn p.SimpleErr\n}\nfunc (p *SimpleServiceSimpleResult) IsSetSimpleErr() bool {\n\treturn p.SimpleErr != nil\n}\n\nfunc (p *SimpleServiceSimpleResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceSimpleResult) ReadField1(iprot thrift.TProtocol) error {\n\tp.SimpleErr = &SimpleErr{}\n\tif err := p.SimpleErr.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.SimpleErr), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceSimpleResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Simple_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceSimpleResult) writeField1(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSimpleErr() {\n\t\tif err := oprot.WriteFieldBegin(\"simpleErr\", thrift.STRUCT, 1); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:simpleErr: \", p), err)\n\t\t}\n\t\tif err := p.SimpleErr.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.SimpleErr), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:simpleErr: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *SimpleServiceSimpleResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"SimpleServiceSimpleResult(%+v)\", *p)\n}\n\ntype SimpleServiceSimpleFutureArgs struct {\n}\n\nfunc NewSimpleServiceSimpleFutureArgs() *SimpleServiceSimpleFutureArgs {\n\treturn &SimpleServiceSimpleFutureArgs{}\n}\n\nfunc (p *SimpleServiceSimpleFutureArgs) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceSimpleFutureArgs) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"SimpleFuture_args\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceSimpleFutureArgs) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"SimpleServiceSimpleFutureArgs(%+v)\", *p)\n}\n\n// Attributes:\n//   - SimpleErr\n//   - NewErr_\ntype SimpleServiceSimpleFutureResult struct {\n\tSimpleErr *SimpleErr `thrift:\"simpleErr,1\" db:\"simpleErr\" json:\"simpleErr,omitempty\"`\n\tNewErr_   *NewErr_   `thrift:\"newErr,2\" db:\"newErr\" json:\"newErr,omitempty\"`\n}\n\nfunc NewSimpleServiceSimpleFutureResult() *SimpleServiceSimpleFutureResult {\n\treturn &SimpleServiceSimpleFutureResult{}\n}\n\nvar SimpleServiceSimpleFutureResult_SimpleErr_DEFAULT *SimpleErr\n\nfunc (p *SimpleServiceSimpleFutureResult) GetSimpleErr() *SimpleErr {\n\tif !p.IsSetSimpleErr() {\n\t\treturn SimpleServiceSimpleFutureResult_SimpleErr_DEFAULT\n\t}\n\treturn p.SimpleErr\n}\n\nvar SimpleServiceSimpleFutureResult_NewErr__DEFAULT *NewErr_\n\nfunc (p *SimpleServiceSimpleFutureResult) GetNewErr_() *NewErr_ {\n\tif !p.IsSetNewErr_() {\n\t\treturn SimpleServiceSimpleFutureResult_NewErr__DEFAULT\n\t}\n\treturn p.NewErr_\n}\nfunc (p *SimpleServiceSimpleFutureResult) IsSetSimpleErr() bool {\n\treturn p.SimpleErr != nil\n}\n\nfunc (p *SimpleServiceSimpleFutureResult) IsSetNewErr_() bool {\n\treturn p.NewErr_ != nil\n}\n\nfunc (p *SimpleServiceSimpleFutureResult) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceSimpleFutureResult) ReadField1(iprot thrift.TProtocol) error {\n\tp.SimpleErr = &SimpleErr{}\n\tif err := p.SimpleErr.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.SimpleErr), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceSimpleFutureResult) ReadField2(iprot thrift.TProtocol) error {\n\tp.NewErr_ = &NewErr_{}\n\tif err := p.NewErr_.Read(iprot); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", p.NewErr_), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceSimpleFutureResult) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"SimpleFuture_result\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleServiceSimpleFutureResult) writeField1(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetSimpleErr() {\n\t\tif err := oprot.WriteFieldBegin(\"simpleErr\", thrift.STRUCT, 1); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:simpleErr: \", p), err)\n\t\t}\n\t\tif err := p.SimpleErr.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.SimpleErr), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:simpleErr: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *SimpleServiceSimpleFutureResult) writeField2(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetNewErr_() {\n\t\tif err := oprot.WriteFieldBegin(\"newErr\", thrift.STRUCT, 2); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:newErr: \", p), err)\n\t\t}\n\t\tif err := p.NewErr_.Write(oprot); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", p.NewErr_), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:newErr: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *SimpleServiceSimpleFutureResult) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"SimpleServiceSimpleFutureResult(%+v)\", *p)\n}\n"
  },
  {
    "path": "thrift/gen-go/test/tchan-test.go",
    "content": "// @generated Code generated by thrift-gen. Do not modify.\n\n// Package test is generated code used to make or handle TChannel calls using Thrift.\npackage test\n\nimport (\n\t\"fmt\"\n\n\tathrift \"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n\t\"github.com/uber/tchannel-go/thrift\"\n)\n\n// Interfaces for the service and client for the services defined in the IDL.\n\n// TChanMeta is the interface that defines the server handler and client interface.\ntype TChanMeta interface {\n\tHealth(ctx thrift.Context) (*HealthStatus, error)\n}\n\n// TChanSecondService is the interface that defines the server handler and client interface.\ntype TChanSecondService interface {\n\tEcho(ctx thrift.Context, arg string) (string, error)\n}\n\n// TChanSimpleService is the interface that defines the server handler and client interface.\ntype TChanSimpleService interface {\n\tCall(ctx thrift.Context, arg *Data) (*Data, error)\n\tSimple(ctx thrift.Context) error\n\tSimpleFuture(ctx thrift.Context) error\n}\n\n// Implementation of a client and service handler.\n\ntype tchanMetaClient struct {\n\tthriftService string\n\tclient        thrift.TChanClient\n}\n\nfunc NewTChanMetaInheritedClient(thriftService string, client thrift.TChanClient) *tchanMetaClient {\n\treturn &tchanMetaClient{\n\t\tthriftService,\n\t\tclient,\n\t}\n}\n\n// NewTChanMetaClient creates a client that can be used to make remote calls.\nfunc NewTChanMetaClient(client thrift.TChanClient) TChanMeta {\n\treturn NewTChanMetaInheritedClient(\"Meta\", client)\n}\n\nfunc (c *tchanMetaClient) Health(ctx thrift.Context) (*HealthStatus, error) {\n\tvar resp MetaHealthResult\n\targs := MetaHealthArgs{}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"health\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for health\")\n\t\t}\n\t}\n\n\treturn resp.GetSuccess(), err\n}\n\ntype tchanMetaServer struct {\n\thandler TChanMeta\n}\n\n// NewTChanMetaServer wraps a handler for TChanMeta so it can be\n// registered with a thrift.Server.\nfunc NewTChanMetaServer(handler TChanMeta) thrift.TChanServer {\n\treturn &tchanMetaServer{\n\t\thandler,\n\t}\n}\n\nfunc (s *tchanMetaServer) Service() string {\n\treturn \"Meta\"\n}\n\nfunc (s *tchanMetaServer) Methods() []string {\n\treturn []string{\n\t\t\"health\",\n\t}\n}\n\nfunc (s *tchanMetaServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\tcase \"health\":\n\t\treturn s.handleHealth(ctx, protocol)\n\n\tdefault:\n\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\nfunc (s *tchanMetaServer) handleHealth(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req MetaHealthArgs\n\tvar res MetaHealthResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tr, err :=\n\t\ts.handler.Health(ctx)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t} else {\n\t\tres.Success = r\n\t}\n\n\treturn err == nil, &res, nil\n}\n\ntype tchanSecondServiceClient struct {\n\tthriftService string\n\tclient        thrift.TChanClient\n}\n\nfunc NewTChanSecondServiceInheritedClient(thriftService string, client thrift.TChanClient) *tchanSecondServiceClient {\n\treturn &tchanSecondServiceClient{\n\t\tthriftService,\n\t\tclient,\n\t}\n}\n\n// NewTChanSecondServiceClient creates a client that can be used to make remote calls.\nfunc NewTChanSecondServiceClient(client thrift.TChanClient) TChanSecondService {\n\treturn NewTChanSecondServiceInheritedClient(\"SecondService\", client)\n}\n\nfunc (c *tchanSecondServiceClient) Echo(ctx thrift.Context, arg string) (string, error) {\n\tvar resp SecondServiceEchoResult\n\targs := SecondServiceEchoArgs{\n\t\tArg: arg,\n\t}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"Echo\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for Echo\")\n\t\t}\n\t}\n\n\treturn resp.GetSuccess(), err\n}\n\ntype tchanSecondServiceServer struct {\n\thandler TChanSecondService\n}\n\n// NewTChanSecondServiceServer wraps a handler for TChanSecondService so it can be\n// registered with a thrift.Server.\nfunc NewTChanSecondServiceServer(handler TChanSecondService) thrift.TChanServer {\n\treturn &tchanSecondServiceServer{\n\t\thandler,\n\t}\n}\n\nfunc (s *tchanSecondServiceServer) Service() string {\n\treturn \"SecondService\"\n}\n\nfunc (s *tchanSecondServiceServer) Methods() []string {\n\treturn []string{\n\t\t\"Echo\",\n\t}\n}\n\nfunc (s *tchanSecondServiceServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\tcase \"Echo\":\n\t\treturn s.handleEcho(ctx, protocol)\n\n\tdefault:\n\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\nfunc (s *tchanSecondServiceServer) handleEcho(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req SecondServiceEchoArgs\n\tvar res SecondServiceEchoResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tr, err :=\n\t\ts.handler.Echo(ctx, req.Arg)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t} else {\n\t\tres.Success = &r\n\t}\n\n\treturn err == nil, &res, nil\n}\n\ntype tchanSimpleServiceClient struct {\n\tthriftService string\n\tclient        thrift.TChanClient\n}\n\nfunc NewTChanSimpleServiceInheritedClient(thriftService string, client thrift.TChanClient) *tchanSimpleServiceClient {\n\treturn &tchanSimpleServiceClient{\n\t\tthriftService,\n\t\tclient,\n\t}\n}\n\n// NewTChanSimpleServiceClient creates a client that can be used to make remote calls.\nfunc NewTChanSimpleServiceClient(client thrift.TChanClient) TChanSimpleService {\n\treturn NewTChanSimpleServiceInheritedClient(\"SimpleService\", client)\n}\n\nfunc (c *tchanSimpleServiceClient) Call(ctx thrift.Context, arg *Data) (*Data, error) {\n\tvar resp SimpleServiceCallResult\n\targs := SimpleServiceCallArgs{\n\t\tArg: arg,\n\t}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"Call\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for Call\")\n\t\t}\n\t}\n\n\treturn resp.GetSuccess(), err\n}\n\nfunc (c *tchanSimpleServiceClient) Simple(ctx thrift.Context) error {\n\tvar resp SimpleServiceSimpleResult\n\targs := SimpleServiceSimpleArgs{}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"Simple\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tcase resp.SimpleErr != nil:\n\t\t\terr = resp.SimpleErr\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for Simple\")\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (c *tchanSimpleServiceClient) SimpleFuture(ctx thrift.Context) error {\n\tvar resp SimpleServiceSimpleFutureResult\n\targs := SimpleServiceSimpleFutureArgs{}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"SimpleFuture\", &args, &resp)\n\tif err == nil && !success {\n\t\tswitch {\n\t\tcase resp.SimpleErr != nil:\n\t\t\terr = resp.SimpleErr\n\t\tcase resp.NewErr_ != nil:\n\t\t\terr = resp.NewErr_\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"received no result or unknown exception for SimpleFuture\")\n\t\t}\n\t}\n\n\treturn err\n}\n\ntype tchanSimpleServiceServer struct {\n\thandler TChanSimpleService\n}\n\n// NewTChanSimpleServiceServer wraps a handler for TChanSimpleService so it can be\n// registered with a thrift.Server.\nfunc NewTChanSimpleServiceServer(handler TChanSimpleService) thrift.TChanServer {\n\treturn &tchanSimpleServiceServer{\n\t\thandler,\n\t}\n}\n\nfunc (s *tchanSimpleServiceServer) Service() string {\n\treturn \"SimpleService\"\n}\n\nfunc (s *tchanSimpleServiceServer) Methods() []string {\n\treturn []string{\n\t\t\"Call\",\n\t\t\"Simple\",\n\t\t\"SimpleFuture\",\n\t}\n}\n\nfunc (s *tchanSimpleServiceServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\tcase \"Call\":\n\t\treturn s.handleCall(ctx, protocol)\n\tcase \"Simple\":\n\t\treturn s.handleSimple(ctx, protocol)\n\tcase \"SimpleFuture\":\n\t\treturn s.handleSimpleFuture(ctx, protocol)\n\n\tdefault:\n\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\nfunc (s *tchanSimpleServiceServer) handleCall(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req SimpleServiceCallArgs\n\tvar res SimpleServiceCallResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tr, err :=\n\t\ts.handler.Call(ctx, req.Arg)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t} else {\n\t\tres.Success = r\n\t}\n\n\treturn err == nil, &res, nil\n}\n\nfunc (s *tchanSimpleServiceServer) handleSimple(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req SimpleServiceSimpleArgs\n\tvar res SimpleServiceSimpleResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\terr :=\n\t\ts.handler.Simple(ctx)\n\n\tif err != nil {\n\t\tswitch v := err.(type) {\n\t\tcase *SimpleErr:\n\t\t\tif v == nil {\n\t\t\t\treturn false, nil, fmt.Errorf(\"Handler for simpleErr returned non-nil error type *SimpleErr but nil value\")\n\t\t\t}\n\t\t\tres.SimpleErr = v\n\t\tdefault:\n\t\t\treturn false, nil, err\n\t\t}\n\t} else {\n\t}\n\n\treturn err == nil, &res, nil\n}\n\nfunc (s *tchanSimpleServiceServer) handleSimpleFuture(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req SimpleServiceSimpleFutureArgs\n\tvar res SimpleServiceSimpleFutureResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\terr :=\n\t\ts.handler.SimpleFuture(ctx)\n\n\tif err != nil {\n\t\tswitch v := err.(type) {\n\t\tcase *SimpleErr:\n\t\t\tif v == nil {\n\t\t\t\treturn false, nil, fmt.Errorf(\"Handler for simpleErr returned non-nil error type *SimpleErr but nil value\")\n\t\t\t}\n\t\t\tres.SimpleErr = v\n\t\tcase *NewErr_:\n\t\t\tif v == nil {\n\t\t\t\treturn false, nil, fmt.Errorf(\"Handler for newErr returned non-nil error type *NewErr_ but nil value\")\n\t\t\t}\n\t\t\tres.NewErr_ = v\n\t\tdefault:\n\t\t\treturn false, nil, err\n\t\t}\n\t} else {\n\t}\n\n\treturn err == nil, &res, nil\n}\n"
  },
  {
    "path": "thrift/gen-go/test/ttypes.go",
    "content": "// Autogenerated by Thrift Compiler (1.0.0-dev)\n// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\npackage test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// (needed to ensure safety because of naive import list construction.)\nvar _ = thrift.ZERO\nvar _ = fmt.Printf\nvar _ = bytes.Equal\n\nvar GoUnusedProtection__ int\n\n// Attributes:\n//   - B1\n//   - S2\n//   - I3\ntype Data struct {\n\tB1 bool   `thrift:\"b1,1,required\" db:\"b1\" json:\"b1\"`\n\tS2 string `thrift:\"s2,2,required\" db:\"s2\" json:\"s2\"`\n\tI3 int32  `thrift:\"i3,3,required\" db:\"i3\" json:\"i3\"`\n}\n\nfunc NewData() *Data {\n\treturn &Data{}\n}\n\nfunc (p *Data) GetB1() bool {\n\treturn p.B1\n}\n\nfunc (p *Data) GetS2() string {\n\treturn p.S2\n}\n\nfunc (p *Data) GetI3() int32 {\n\treturn p.I3\n}\nfunc (p *Data) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tvar issetB1 bool = false\n\tvar issetS2 bool = false\n\tvar issetI3 bool = false\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetB1 = true\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetS2 = true\n\t\tcase 3:\n\t\t\tif err := p.ReadField3(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetI3 = true\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\tif !issetB1 {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field B1 is not set\"))\n\t}\n\tif !issetS2 {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field S2 is not set\"))\n\t}\n\tif !issetI3 {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field I3 is not set\"))\n\t}\n\treturn nil\n}\n\nfunc (p *Data) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadBool(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.B1 = v\n\t}\n\treturn nil\n}\n\nfunc (p *Data) ReadField2(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 2: \", err)\n\t} else {\n\t\tp.S2 = v\n\t}\n\treturn nil\n}\n\nfunc (p *Data) ReadField3(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadI32(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 3: \", err)\n\t} else {\n\t\tp.I3 = v\n\t}\n\treturn nil\n}\n\nfunc (p *Data) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"Data\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField3(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *Data) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"b1\", thrift.BOOL, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:b1: \", p), err)\n\t}\n\tif err := oprot.WriteBool(bool(p.B1)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.b1 (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:b1: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *Data) writeField2(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"s2\", thrift.STRING, 2); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:s2: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.S2)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.s2 (2) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:s2: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *Data) writeField3(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"i3\", thrift.I32, 3); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 3:i3: \", p), err)\n\t}\n\tif err := oprot.WriteI32(int32(p.I3)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.i3 (3) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 3:i3: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *Data) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"Data(%+v)\", *p)\n}\n\n// Attributes:\n//   - Message\ntype SimpleErr struct {\n\tMessage string `thrift:\"message,1\" db:\"message\" json:\"message\"`\n}\n\nfunc NewSimpleErr() *SimpleErr {\n\treturn &SimpleErr{}\n}\n\nfunc (p *SimpleErr) GetMessage() string {\n\treturn p.Message\n}\nfunc (p *SimpleErr) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleErr) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Message = v\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleErr) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"SimpleErr\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *SimpleErr) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"message\", thrift.STRING, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:message: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Message)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.message (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:message: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *SimpleErr) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"SimpleErr(%+v)\", *p)\n}\n\nfunc (p *SimpleErr) Error() string {\n\treturn p.String()\n}\n\n// Attributes:\n//   - Message\ntype NewErr_ struct {\n\tMessage string `thrift:\"message,1\" db:\"message\" json:\"message\"`\n}\n\nfunc NewNewErr_() *NewErr_ {\n\treturn &NewErr_{}\n}\n\nfunc (p *NewErr_) GetMessage() string {\n\treturn p.Message\n}\nfunc (p *NewErr_) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\treturn nil\n}\n\nfunc (p *NewErr_) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Message = v\n\t}\n\treturn nil\n}\n\nfunc (p *NewErr_) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"NewErr\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *NewErr_) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"message\", thrift.STRING, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:message: \", p), err)\n\t}\n\tif err := oprot.WriteString(string(p.Message)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.message (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:message: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *NewErr_) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"NewErr_(%+v)\", *p)\n}\n\nfunc (p *NewErr_) Error() string {\n\treturn p.String()\n}\n\n// Attributes:\n//   - Ok\n//   - Message\ntype HealthStatus struct {\n\tOk      bool    `thrift:\"ok,1,required\" db:\"ok\" json:\"ok\"`\n\tMessage *string `thrift:\"message,2\" db:\"message\" json:\"message,omitempty\"`\n}\n\nfunc NewHealthStatus() *HealthStatus {\n\treturn &HealthStatus{}\n}\n\nfunc (p *HealthStatus) GetOk() bool {\n\treturn p.Ok\n}\n\nvar HealthStatus_Message_DEFAULT string\n\nfunc (p *HealthStatus) GetMessage() string {\n\tif !p.IsSetMessage() {\n\t\treturn HealthStatus_Message_DEFAULT\n\t}\n\treturn *p.Message\n}\nfunc (p *HealthStatus) IsSetMessage() bool {\n\treturn p.Message != nil\n}\n\nfunc (p *HealthStatus) Read(iprot thrift.TProtocol) error {\n\tif _, err := iprot.ReadStructBegin(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)\n\t}\n\n\tvar issetOk bool = false\n\n\tfor {\n\t\t_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()\n\t\tif err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T field %d read error: \", p, fieldId), err)\n\t\t}\n\t\tif fieldTypeId == thrift.STOP {\n\t\t\tbreak\n\t\t}\n\t\tswitch fieldId {\n\t\tcase 1:\n\t\t\tif err := p.ReadField1(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tissetOk = true\n\t\tcase 2:\n\t\t\tif err := p.ReadField2(iprot); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := iprot.Skip(fieldTypeId); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := iprot.ReadFieldEnd(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iprot.ReadStructEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T read struct end error: \", p), err)\n\t}\n\tif !issetOk {\n\t\treturn thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf(\"Required field Ok is not set\"))\n\t}\n\treturn nil\n}\n\nfunc (p *HealthStatus) ReadField1(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadBool(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 1: \", err)\n\t} else {\n\t\tp.Ok = v\n\t}\n\treturn nil\n}\n\nfunc (p *HealthStatus) ReadField2(iprot thrift.TProtocol) error {\n\tif v, err := iprot.ReadString(); err != nil {\n\t\treturn thrift.PrependError(\"error reading field 2: \", err)\n\t} else {\n\t\tp.Message = &v\n\t}\n\treturn nil\n}\n\nfunc (p *HealthStatus) Write(oprot thrift.TProtocol) error {\n\tif err := oprot.WriteStructBegin(\"HealthStatus\"); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write struct begin error: \", p), err)\n\t}\n\tif err := p.writeField1(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := p.writeField2(oprot); err != nil {\n\t\treturn err\n\t}\n\tif err := oprot.WriteFieldStop(); err != nil {\n\t\treturn thrift.PrependError(\"write field stop error: \", err)\n\t}\n\tif err := oprot.WriteStructEnd(); err != nil {\n\t\treturn thrift.PrependError(\"write struct stop error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (p *HealthStatus) writeField1(oprot thrift.TProtocol) (err error) {\n\tif err := oprot.WriteFieldBegin(\"ok\", thrift.BOOL, 1); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 1:ok: \", p), err)\n\t}\n\tif err := oprot.WriteBool(bool(p.Ok)); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.ok (1) field write error: \", p), err)\n\t}\n\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 1:ok: \", p), err)\n\t}\n\treturn err\n}\n\nfunc (p *HealthStatus) writeField2(oprot thrift.TProtocol) (err error) {\n\tif p.IsSetMessage() {\n\t\tif err := oprot.WriteFieldBegin(\"message\", thrift.STRING, 2); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field begin error 2:message: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteString(string(*p.Message)); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T.message (2) field write error: \", p), err)\n\t\t}\n\t\tif err := oprot.WriteFieldEnd(); err != nil {\n\t\t\treturn thrift.PrependError(fmt.Sprintf(\"%T write field end error 2:message: \", p), err)\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (p *HealthStatus) String() string {\n\tif p == nil {\n\t\treturn \"<nil>\"\n\t}\n\treturn fmt.Sprintf(\"HealthStatus(%+v)\", *p)\n}\n"
  },
  {
    "path": "thrift/headers.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n)\n\n// WriteHeaders writes the given key-value pairs using the following encoding:\n// len~2 (k~4 v~4)~len\nfunc WriteHeaders(w io.Writer, headers map[string]string) error {\n\t// TODO(prashant): Since we are not writing length-prefixed data here,\n\t// we can write out to the buffer, and if it fills up, flush it.\n\t// Right now, we calculate the size of the required buffer and write it out.\n\n\t// Calculate the size of the buffer that we need.\n\tsize := 2\n\tfor k, v := range headers {\n\t\tsize += 4 /* size of key/value lengths */\n\t\tsize += len(k) + len(v)\n\t}\n\n\tbuf := make([]byte, size)\n\twriteBuffer := typed.NewWriteBuffer(buf)\n\twriteBuffer.WriteUint16(uint16(len(headers)))\n\tfor k, v := range headers {\n\t\twriteBuffer.WriteLen16String(k)\n\t\twriteBuffer.WriteLen16String(v)\n\t}\n\n\tif err := writeBuffer.Err(); err != nil {\n\t\treturn err\n\t}\n\n\t// Safety check to ensure the bytes written calculation is correct.\n\tif writeBuffer.BytesWritten() != size {\n\t\treturn fmt.Errorf(\n\t\t\t\"writeHeaders size calculation wrong, expected to write %v bytes, only wrote %v bytes\",\n\t\t\tsize, writeBuffer.BytesWritten())\n\t}\n\n\t_, err := writeBuffer.FlushTo(w)\n\treturn err\n}\n\nfunc readHeaders(reader *typed.Reader) (map[string]string, error) {\n\tnumHeaders := reader.ReadUint16()\n\tif numHeaders == 0 {\n\t\treturn nil, reader.Err()\n\t}\n\n\theaders := make(map[string]string, numHeaders)\n\tfor i := 0; i < int(numHeaders) && reader.Err() == nil; i++ {\n\t\tk := reader.ReadLen16String()\n\t\tv := reader.ReadLen16String()\n\t\theaders[k] = v\n\t}\n\n\treturn headers, reader.Err()\n}\n\n// ReadHeaders reads key-value pairs encoded using WriteHeaders.\nfunc ReadHeaders(r io.Reader) (map[string]string, error) {\n\treader := typed.NewReader(r)\n\tm, err := readHeaders(reader)\n\treader.Release()\n\n\treturn m, err\n}\n"
  },
  {
    "path": "thrift/headers_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"bytes\"\n\t\"io/ioutil\"\n\t\"testing\"\n\t\"testing/iotest\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar headers = map[string]string{\n\t\"header1\": \"value1\",\n\t\"header2\": \"value2\",\n\t\"header3\": \"value1\",\n\t\"header4\": \"value2\",\n\t\"header5\": \"value1\",\n\t\"header6\": \"value2\",\n\t\"header7\": \"value1\",\n\t\"header8\": \"value2\",\n\t\"header9\": \"value1\",\n\t\"header0\": \"value2\",\n}\n\nvar headerTests = []struct {\n\tm         map[string]string\n\tencoding  []byte\n\tencoding2 []byte\n}{\n\t{\n\t\tm:        nil,\n\t\tencoding: []byte{0, 0},\n\t},\n\t{\n\t\tm:        make(map[string]string),\n\t\tencoding: []byte{0, 0},\n\t},\n\t{\n\t\tm: map[string]string{\n\t\t\t\"k\": \"v\",\n\t\t},\n\t\tencoding: []byte{\n\t\t\t0, 1, /* number of headers */\n\t\t\t0, 1, /* length of key */\n\t\t\t'k',\n\t\t\t0, 1, /* length of value */\n\t\t\t'v',\n\t\t},\n\t},\n\t{\n\t\tm: map[string]string{\n\t\t\t\"\": \"\",\n\t\t},\n\t\tencoding: []byte{\n\t\t\t0, 1, /* number of headers */\n\t\t\t0, 0,\n\t\t\t0, 0,\n\t\t},\n\t},\n\t{\n\t\tm: map[string]string{\n\t\t\t\"k1\": \"v12\",\n\t\t\t\"k2\": \"v34\",\n\t\t},\n\t\tencoding: []byte{\n\t\t\t0, 2, /* number of headers */\n\t\t\t0, 2, /* length of key */\n\t\t\t'k', '2',\n\t\t\t0, 3, /* length of value */\n\t\t\t'v', '3', '4',\n\t\t\t0, 2, /* length of key */\n\t\t\t'k', '1',\n\t\t\t0, 3, /* length of value */\n\t\t\t'v', '1', '2',\n\t\t},\n\t\tencoding2: []byte{\n\t\t\t0, 2, /* number of headers */\n\t\t\t0, 2, /* length of key */\n\t\t\t'k', '1',\n\t\t\t0, 3, /* length of value */\n\t\t\t'v', '1', '2',\n\t\t\t0, 2, /* length of key */\n\t\t\t'k', '2',\n\t\t\t0, 3, /* length of value */\n\t\t\t'v', '3', '4',\n\t\t},\n\t},\n}\n\nfunc TestWriteHeadersSuccessful(t *testing.T) {\n\tfor _, tt := range headerTests {\n\t\tbuf := &bytes.Buffer{}\n\t\terr := WriteHeaders(buf, tt.m)\n\t\tassert.NoError(t, err, \"WriteHeaders failed\")\n\n\t\t// Writes iterate over the map in an undefined order, so we might get\n\t\t// encoding or encoding2. If it's not encoding, assert that it's encoding2.\n\t\tif !bytes.Equal(tt.encoding, buf.Bytes()) {\n\t\t\tassert.Equal(t, tt.encoding2, buf.Bytes(), \"Unexpected bytes\")\n\t\t}\n\t}\n}\n\nfunc TestReadHeadersSuccessful(t *testing.T) {\n\tfor _, tt := range headerTests {\n\t\t// when the bytes are {0, 0}, we always return nil.\n\t\tif tt.m != nil && len(tt.m) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\treader := iotest.OneByteReader(bytes.NewReader(tt.encoding))\n\t\tgot, err := ReadHeaders(reader)\n\t\tassert.NoError(t, err, \"ReadHeaders failed\")\n\t\tassert.Equal(t, tt.m, got, \"Map mismatch\")\n\n\t\tif tt.encoding2 != nil {\n\t\t\treader := iotest.OneByteReader(bytes.NewReader(tt.encoding2))\n\t\t\tgot, err := ReadHeaders(reader)\n\t\t\tassert.NoError(t, err, \"ReadHeaders failed\")\n\t\t\tassert.Equal(t, tt.m, got, \"Map mismatch\")\n\t\t}\n\t}\n}\n\nfunc TestReadHeadersLeftoverBytes(t *testing.T) {\n\tbuf := []byte{0, 0, 1, 2, 3}\n\tr := bytes.NewReader(buf)\n\theaders, err := ReadHeaders(r)\n\tassert.NoError(t, err, \"ReadHeaders failed\")\n\tassert.Equal(t, map[string]string(nil), headers, \"Headers mismatch\")\n\n\tleftover, err := ioutil.ReadAll(r)\n\tassert.NoError(t, err, \"ReadAll failed\")\n\tassert.Equal(t, []byte{1, 2, 3}, leftover, \"Reader consumed leftover bytes\")\n}\n\nfunc BenchmarkWriteHeaders(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tWriteHeaders(ioutil.Discard, headers)\n\t}\n}\n\nfunc BenchmarkReadHeaders(b *testing.B) {\n\tbuf := &bytes.Buffer{}\n\tassert.NoError(b, WriteHeaders(buf, headers))\n\tbs := buf.Bytes()\n\treader := bytes.NewReader(bs)\n\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\treader.Seek(0, 0)\n\t\tReadHeaders(reader)\n\t}\n}\n"
  },
  {
    "path": "thrift/interfaces.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport athrift \"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n\n// This file defines interfaces that are used or exposed by thrift-gen generated code.\n// TChanClient is used by the generated code to make outgoing requests.\n// TChanServer is exposed by the generated code, and is called on incoming requests.\n\n// TChanClient abstracts calling a Thrift endpoint, and is used by the generated client code.\ntype TChanClient interface {\n\t// Call should be passed the method to call and the request/response Thrift structs.\n\tCall(ctx Context, serviceName, methodName string, req, resp athrift.TStruct) (success bool, err error)\n}\n\n// TChanServer abstracts handling of an RPC that is implemented by the generated server code.\ntype TChanServer interface {\n\t// Handle should read the request from the given reqReader, and return the response struct.\n\t// The arguments returned are success, result struct, unexpected error\n\tHandle(ctx Context, methodName string, protocol athrift.TProtocol) (success bool, resp athrift.TStruct, err error)\n\n\t// Service returns the service name.\n\tService() string\n\n\t// Methods returns the method names handled by this server.\n\tMethods() []string\n}\n"
  },
  {
    "path": "thrift/meta.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"errors\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/thrift/gen-go/meta\"\n)\n\n// HealthFunc is the interface for custom health endpoints.\n// ok is whether the service health is OK, and message is optional additional information for the health result.\ntype HealthFunc func(ctx Context) (ok bool, message string)\n\n// HealthRequestType is the type of health check.\ntype HealthRequestType int\n\nconst (\n\t// Process health checks are used to check whether the process is up\n\t// and should almost always return true immediately.\n\tProcess HealthRequestType = iota\n\n\t// Traffic health checks are used to check whether the process should\n\t// receive traffic. This can be used to keep a process running, but\n\t// not receiving health checks (e.g., during process warm-up).\n\tTraffic\n)\n\n// HealthRequest is optional parametres for a health request.\ntype HealthRequest struct {\n\t// Type is the type of health check being requested.\n\tType HealthRequestType\n}\n\n// HealthRequestFunc is a health check function that includes parameters\n// about the health check.\ntype HealthRequestFunc func(Context, HealthRequest) (ok bool, message string)\n\n// healthHandler implements the default health check enpoint.\ntype metaHandler struct {\n\thealthFn HealthRequestFunc\n}\n\n// newMetaHandler return a new HealthHandler instance.\nfunc newMetaHandler() *metaHandler {\n\treturn &metaHandler{healthFn: defaultHealth}\n}\n\n// Health returns true as default Health endpoint.\nfunc (h *metaHandler) Health(ctx Context, req *meta.HealthRequest) (*meta.HealthStatus, error) {\n\tok, message := h.healthFn(ctx, metaReqToReq(req))\n\tif message == \"\" {\n\t\treturn &meta.HealthStatus{Ok: ok}, nil\n\t}\n\treturn &meta.HealthStatus{Ok: ok, Message: &message}, nil\n}\n\nfunc (h *metaHandler) ThriftIDL(ctx Context) (*meta.ThriftIDLs, error) {\n\t// TODO(prashant): Add thriftIDL to the generated code.\n\treturn nil, errors.New(\"unimplemented\")\n}\n\nfunc (h *metaHandler) VersionInfo(ctx Context) (*meta.VersionInfo, error) {\n\treturn &meta.VersionInfo{\n\t\tLanguage:        \"go\",\n\t\tLanguageVersion: strings.TrimPrefix(runtime.Version(), \"go\"),\n\t\tVersion:         tchannel.VersionInfo,\n\t}, nil\n}\n\nfunc defaultHealth(ctx Context, r HealthRequest) (bool, string) {\n\treturn true, \"\"\n}\n\nfunc (h *metaHandler) setHandler(f HealthRequestFunc) {\n\th.healthFn = f\n}\n\nfunc metaReqToReq(r *meta.HealthRequest) HealthRequest {\n\tif r == nil {\n\t\treturn HealthRequest{}\n\t}\n\n\treturn HealthRequest{\n\t\tType: HealthRequestType(r.GetType()),\n\t}\n}\n"
  },
  {
    "path": "thrift/meta.thrift",
    "content": "// The HealthState provides additional information when the\n// health endpoint returns !ok.\nenum HealthState {\n    REFUSING = 0,\n    ACCEPTING = 1,\n    STOPPING = 2,\n    STOPPED = 3,\n}\n\n// The HealthRequestType is the type of health check, as a process may want to\n// return that it's running, but not ready for traffic.\nenum HealthRequestType {\n    // PROCESS indicates that the health check is for checking that\n    // the process is up. Handlers should always return \"ok\".\n    PROCESS = 0,\n\n    // TRAFFIC indicates that the health check is for checking whether\n    // the process wants to receive traffic. The process may want to reject\n    // traffic due to warmup, or before shutdown to avoid in-flight requests\n    // when the process exits.\n    TRAFFIC = 1,\n}\n\nstruct HealthRequest {\n    1: optional HealthRequestType type\n}\n\nstruct HealthStatus {\n    1: required bool ok\n    2: optional string message\n    3: optional HealthState state\n}\n\ntypedef string filename\n\nstruct ThriftIDLs {\n    // map: filename -> contents\n    1: required map<filename, string> idls\n    // the entry IDL that imports others\n    2: required filename entryPoint\n}\n\nstruct VersionInfo {\n  // short string naming the implementation language\n  1: required string language\n  // language-specific version string representing runtime or build chain\n  2: required string language_version\n  // semver version indicating the version of the tchannel library\n  3: required string version\n}\n\nservice Meta {\n    // All arguments are optional. The default is a PROCESS health request.\n    HealthStatus health(1: HealthRequest hr)\n\n    ThriftIDLs thriftIDL()\n    VersionInfo versionInfo()\n}\n"
  },
  {
    "path": "thrift/meta_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\t\"github.com/uber/tchannel-go/thrift/gen-go/meta\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestThriftIDL(t *testing.T) {\n\twithMetaSetup(t, func(ctx Context, c tchanMeta, server *Server) {\n\t\t_, err := c.ThriftIDL(ctx)\n\t\tassert.Error(t, err, \"Health endpoint failed\")\n\t\tassert.Contains(t, err.Error(), \"unimplemented\")\n\t})\n}\n\nfunc TestVersionInfo(t *testing.T) {\n\twithMetaSetup(t, func(ctx Context, c tchanMeta, server *Server) {\n\t\tret, err := c.VersionInfo(ctx)\n\t\tif assert.NoError(t, err, \"VersionInfo endpoint failed\") {\n\t\t\texpected := &meta.VersionInfo{\n\t\t\t\tLanguage:        \"go\",\n\t\t\t\tLanguageVersion: strings.TrimPrefix(runtime.Version(), \"go\"),\n\t\t\t\tVersion:         tchannel.VersionInfo,\n\t\t\t}\n\t\t\tassert.Equal(t, expected, ret, \"Unexpected version info\")\n\t\t}\n\t})\n}\n\nfunc TestHealth(t *testing.T) {\n\ttests := []struct {\n\t\tmsg           string\n\t\thealthFunc    HealthFunc\n\t\thealthReqFunc HealthRequestFunc\n\t\treq           *meta.HealthRequest\n\t\twantOK        bool\n\t\twantMessage   *string\n\t}{\n\t\t{\n\t\t\tmsg:    \"default health func\",\n\t\t\twantOK: true,\n\t\t},\n\t\t{\n\t\t\tmsg: \"healthFunc returning unhealthy, no message\",\n\t\t\thealthFunc: func(Context) (bool, string) {\n\t\t\t\treturn false, \"\"\n\t\t\t},\n\t\t\twantOK: false,\n\t\t},\n\t\t{\n\t\t\tmsg: \"healthFunc returning healthy, with message\",\n\t\t\thealthFunc: func(Context) (bool, string) {\n\t\t\t\treturn true, \"ok\"\n\t\t\t},\n\t\t\twantOK:      true,\n\t\t\twantMessage: stringPtr(\"ok\"),\n\t\t},\n\t\t{\n\t\t\tmsg: \"healthReqFunc returning unhealthy for traffic, default check\",\n\t\t\thealthReqFunc: func(_ Context, r HealthRequest) (bool, string) {\n\t\t\t\treturn r.Type != Traffic, \"\"\n\t\t\t},\n\t\t\twantOK: true,\n\t\t},\n\t\t{\n\t\t\tmsg: \"healthReqFunc returning unhealthy for traffic, traffic check\",\n\t\t\thealthReqFunc: func(_ Context, r HealthRequest) (bool, string) {\n\t\t\t\treturn r.Type != Traffic, \"\"\n\t\t\t},\n\t\t\treq:    &meta.HealthRequest{Type: meta.HealthRequestTypePtr(meta.HealthRequestType_TRAFFIC)},\n\t\t\twantOK: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\twithMetaSetup(t, func(ctx Context, c tchanMeta, server *Server) {\n\t\t\t\tif tt.healthFunc != nil {\n\t\t\t\t\tserver.RegisterHealthHandler(tt.healthFunc)\n\t\t\t\t}\n\t\t\t\tif tt.healthReqFunc != nil {\n\t\t\t\t\tserver.RegisterHealthRequestHandler(tt.healthReqFunc)\n\t\t\t\t}\n\n\t\t\t\treq := tt.req\n\t\t\t\tif req == nil {\n\t\t\t\t\treq = &meta.HealthRequest{}\n\t\t\t\t}\n\n\t\t\t\tret, err := c.Health(ctx, req)\n\t\t\t\trequire.NoError(t, err, \"Health endpoint failed\")\n\n\t\t\t\tassert.Equal(t, tt.wantOK, ret.Ok, \"Health status mismatch\")\n\t\t\t\tassert.Equal(t, tt.wantMessage, ret.Message, \"Health message mismatch\")\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestMetaReqToReq(t *testing.T) {\n\ttests := []struct {\n\t\tmsg  string\n\t\tr    *meta.HealthRequest\n\t\twant HealthRequest\n\t}{\n\t\t{\n\t\t\tmsg:  \"nil\",\n\t\t\tr:    nil,\n\t\t\twant: HealthRequest{},\n\t\t},\n\t\t{\n\t\t\tmsg:  \"default\",\n\t\t\tr:    &meta.HealthRequest{},\n\t\t\twant: HealthRequest{},\n\t\t},\n\t\t{\n\t\t\tmsg: \"explcit process check\",\n\t\t\tr: &meta.HealthRequest{\n\t\t\t\tType: meta.HealthRequestTypePtr(meta.HealthRequestType_PROCESS),\n\t\t\t},\n\t\t\twant: HealthRequest{\n\t\t\t\tType: Process,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"explcit traffic check\",\n\t\t\tr: &meta.HealthRequest{\n\t\t\t\tType: meta.HealthRequestTypePtr(meta.HealthRequestType_TRAFFIC),\n\t\t\t},\n\t\t\twant: HealthRequest{\n\t\t\t\tType: Traffic,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\tassert.Equal(t, tt.want, metaReqToReq(tt.r))\n\t\t})\n\t}\n}\n\nfunc withMetaSetup(t *testing.T, f func(ctx Context, c tchanMeta, server *Server)) {\n\tctx, cancel := NewContext(time.Second * 10)\n\tdefer cancel()\n\n\t// Start server\n\ttchan, server := setupMetaServer(t)\n\tdefer tchan.Close()\n\n\t// Get client1\n\tc := getMetaClient(t, tchan.PeerInfo().HostPort)\n\tf(ctx, c, server)\n}\n\nfunc setupMetaServer(t *testing.T) (*tchannel.Channel, *Server) {\n\ttchan := testutils.NewServer(t, testutils.NewOpts().SetServiceName(\"meta\"))\n\tserver := NewServer(tchan)\n\treturn tchan, server\n}\n\nfunc getMetaClient(t *testing.T, dst string) tchanMeta {\n\ttchan := testutils.NewClient(t, nil)\n\ttchan.Peers().Add(dst)\n\tthriftClient := NewClient(tchan, \"meta\", nil)\n\treturn newTChanMetaClient(thriftClient)\n}\n\nfunc stringPtr(s string) *string {\n\treturn &s\n}\n"
  },
  {
    "path": "thrift/mocks/TChanMeta.go",
    "content": "package mocks\n\nimport \"github.com/uber/tchannel-go/thrift/gen-go/meta\"\nimport \"github.com/stretchr/testify/mock\"\n\nimport \"github.com/uber/tchannel-go/thrift\"\n\ntype TChanMeta struct {\n\tmock.Mock\n}\n\nfunc (_m *TChanMeta) Health(ctx thrift.Context) (*meta.HealthStatus, error) {\n\tret := _m.Called(ctx)\n\n\tvar r0 *meta.HealthStatus\n\tif rf, ok := ret.Get(0).(func(thrift.Context) *meta.HealthStatus); ok {\n\t\tr0 = rf(ctx)\n\t} else {\n\t\tif ret.Get(0) != nil {\n\t\t\tr0 = ret.Get(0).(*meta.HealthStatus)\n\t\t}\n\t}\n\n\tvar r1 error\n\tif rf, ok := ret.Get(1).(func(thrift.Context) error); ok {\n\t\tr1 = rf(ctx)\n\t} else {\n\t\tr1 = ret.Error(1)\n\t}\n\n\treturn r0, r1\n}\n"
  },
  {
    "path": "thrift/mocks/TChanSecondService.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage mocks\n\nimport \"github.com/stretchr/testify/mock\"\n\nimport \"github.com/uber/tchannel-go/thrift\"\n\ntype TChanSecondService struct {\n\tmock.Mock\n}\n\nfunc (_m *TChanSecondService) Echo(_ctx thrift.Context, _arg string) (string, error) {\n\tret := _m.Called(_ctx, _arg)\n\n\tvar r0 string\n\tif rf, ok := ret.Get(0).(func(thrift.Context, string) string); ok {\n\t\tr0 = rf(_ctx, _arg)\n\t} else {\n\t\tr0 = ret.Get(0).(string)\n\t}\n\n\tvar r1 error\n\tif rf, ok := ret.Get(1).(func(thrift.Context, string) error); ok {\n\t\tr1 = rf(_ctx, _arg)\n\t} else {\n\t\tr1 = ret.Error(1)\n\t}\n\n\treturn r0, r1\n}\n"
  },
  {
    "path": "thrift/mocks/TChanSimpleService.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage mocks\n\nimport \"github.com/uber/tchannel-go/thrift/gen-go/test\"\nimport \"github.com/stretchr/testify/mock\"\n\nimport \"github.com/uber/tchannel-go/thrift\"\n\ntype TChanSimpleService struct {\n\tmock.Mock\n}\n\nfunc (_m *TChanSimpleService) Call(_ctx thrift.Context, _arg *test.Data) (*test.Data, error) {\n\tret := _m.Called(_ctx, _arg)\n\n\tvar r0 *test.Data\n\tif rf, ok := ret.Get(0).(func(thrift.Context, *test.Data) *test.Data); ok {\n\t\tr0 = rf(_ctx, _arg)\n\t} else {\n\t\tif ret.Get(0) != nil {\n\t\t\tr0 = ret.Get(0).(*test.Data)\n\t\t}\n\t}\n\n\tvar r1 error\n\tif rf, ok := ret.Get(1).(func(thrift.Context, *test.Data) error); ok {\n\t\tr1 = rf(_ctx, _arg)\n\t} else {\n\t\tr1 = ret.Error(1)\n\t}\n\n\treturn r0, r1\n}\nfunc (_m *TChanSimpleService) Simple(_ctx thrift.Context) error {\n\tret := _m.Called(_ctx)\n\n\tvar r0 error\n\tif rf, ok := ret.Get(0).(func(thrift.Context) error); ok {\n\t\tr0 = rf(_ctx)\n\t} else {\n\t\tr0 = ret.Error(0)\n\t}\n\n\treturn r0\n}\nfunc (_m *TChanSimpleService) SimpleFuture(_ctx thrift.Context) error {\n\tret := _m.Called(_ctx)\n\n\tvar r0 error\n\tif rf, ok := ret.Get(0).(func(thrift.Context) error); ok {\n\t\tr0 = rf(_ctx)\n\t} else {\n\t\tr0 = ret.Error(0)\n\t}\n\n\treturn r0\n}\n"
  },
  {
    "path": "thrift/options.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n\t\"golang.org/x/net/context\"\n)\n\n// RegisterOption is the interface for options to Register.\ntype RegisterOption interface {\n\tApply(h *handler)\n}\n\n// PostResponseCB registers a callback that is run after a response has been\n// compeltely processed (e.g. written to the channel).\n// This gives the server a chance to clean up resources from the response object\ntype PostResponseCB func(ctx context.Context, method string, response thrift.TStruct)\n\ntype optPostResponse PostResponseCB\n\n// OptPostResponse registers a PostResponseCB.\nfunc OptPostResponse(cb PostResponseCB) RegisterOption {\n\treturn optPostResponse(cb)\n}\n\nfunc (o optPostResponse) Apply(h *handler) {\n\th.postResponseCB = PostResponseCB(o)\n}\n"
  },
  {
    "path": "thrift/server.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"log\"\n\t\"strings\"\n\t\"sync\"\n\n\ttchannel \"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/internal/argreader\"\n\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n\t\"golang.org/x/net/context\"\n)\n\ntype handler struct {\n\tserver         TChanServer\n\tpostResponseCB PostResponseCB\n}\n\n// Server handles incoming TChannel calls and forwards them to the matching TChanServer.\ntype Server struct {\n\tsync.RWMutex\n\tch          tchannel.Registrar\n\tlog         tchannel.Logger\n\thandlers    map[string]handler\n\tmetaHandler *metaHandler\n\tctxFn       func(ctx context.Context, method string, headers map[string]string) Context\n}\n\n// NewServer returns a server that can serve thrift services over TChannel.\nfunc NewServer(registrar tchannel.Registrar) *Server {\n\tmetaHandler := newMetaHandler()\n\tserver := &Server{\n\t\tch:          registrar,\n\t\tlog:         registrar.Logger(),\n\t\thandlers:    make(map[string]handler),\n\t\tmetaHandler: metaHandler,\n\t\tctxFn:       defaultContextFn,\n\t}\n\tserver.Register(newTChanMetaServer(metaHandler))\n\tif ch, ok := registrar.(*tchannel.Channel); ok {\n\t\t// Register the meta endpoints on the \"tchannel\" service name.\n\t\tNewServer(ch.GetSubChannel(\"tchannel\"))\n\t}\n\treturn server\n}\n\n// Register registers the given TChanServer to be called on any incoming call for its' services.\n// TODO(prashant): Replace Register call with this call.\nfunc (s *Server) Register(svr TChanServer, opts ...RegisterOption) {\n\tservice := svr.Service()\n\thandler := &handler{server: svr}\n\tfor _, opt := range opts {\n\t\topt.Apply(handler)\n\t}\n\n\ts.Lock()\n\ts.handlers[service] = *handler\n\ts.Unlock()\n\n\tfor _, m := range svr.Methods() {\n\t\ts.ch.Register(s, service+\"::\"+m)\n\t}\n}\n\n// RegisterHealthHandler uses the user-specified function f for the Health endpoint.\nfunc (s *Server) RegisterHealthHandler(f HealthFunc) {\n\twrapped := func(ctx Context, r HealthRequest) (bool, string) {\n\t\treturn f(ctx)\n\t}\n\ts.metaHandler.setHandler(wrapped)\n}\n\n// RegisterHealthRequestHandler uses the user-specified function for the\n// Health endpoint. The function receives the health request which includes\n// information about the type of the request being performed.\nfunc (s *Server) RegisterHealthRequestHandler(f HealthRequestFunc) {\n\ts.metaHandler.setHandler(f)\n}\n\n// SetContextFn sets the function used to convert a context.Context to a thrift.Context.\n// Note: This API may change and is only intended to bridge different contexts.\nfunc (s *Server) SetContextFn(f func(ctx context.Context, method string, headers map[string]string) Context) {\n\ts.ctxFn = f\n}\n\nfunc (s *Server) onError(call *tchannel.InboundCall, err error) {\n\t// TODO(prashant): Expose incoming call errors through options for NewServer.\n\tremotePeer := call.RemotePeer()\n\tlogger := s.log.WithFields(\n\t\ttchannel.ErrField(err),\n\t\ttchannel.LogField{Key: \"method\", Value: call.MethodString()},\n\t\ttchannel.LogField{Key: \"callerName\", Value: call.CallerName()},\n\n\t\t// TODO: These are very similar to the connection fields, but we don't\n\t\t// have access to the connection's logger. Consider exposing the\n\t\t// connection through CurrentCall.\n\t\ttchannel.LogField{Key: \"localAddr\", Value: call.LocalPeer().HostPort},\n\t\ttchannel.LogField{Key: \"remoteHostPort\", Value: remotePeer.HostPort},\n\t\ttchannel.LogField{Key: \"remoteIsEphemeral\", Value: remotePeer.IsEphemeral},\n\t\ttchannel.LogField{Key: \"remoteProcess\", Value: remotePeer.ProcessName},\n\t)\n\n\tif tchannel.GetSystemErrorCode(err) == tchannel.ErrCodeTimeout {\n\t\tlogger.Debug(\"Thrift server timeout.\")\n\t} else {\n\t\tlogger.Error(\"Thrift server error.\")\n\t}\n}\n\nfunc defaultContextFn(ctx context.Context, method string, headers map[string]string) Context {\n\treturn WithHeaders(ctx, headers)\n}\n\nfunc (s *Server) handle(origCtx context.Context, handler handler, method string, call *tchannel.InboundCall) error {\n\treader, err := call.Arg2Reader()\n\tif err != nil {\n\t\treturn err\n\t}\n\theaders, err := ReadHeaders(reader)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := argreader.EnsureEmpty(reader, \"reading request headers\"); err != nil {\n\t\treturn err\n\t}\n\n\tif err := reader.Close(); err != nil {\n\t\treturn err\n\t}\n\n\treader, err = call.Arg3Reader()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttracer := tchannel.TracerFromRegistrar(s.ch)\n\torigCtx = tchannel.ExtractInboundSpan(origCtx, call, headers, tracer)\n\tctx := s.ctxFn(origCtx, method, headers)\n\n\twp := getProtocolReader(reader)\n\tsuccess, resp, err := handler.server.Handle(ctx, method, wp.protocol)\n\tthriftProtocolPool.Put(wp)\n\n\tif handler.postResponseCB != nil {\n\t\tdefer handler.postResponseCB(ctx, method, resp)\n\t}\n\n\tif err != nil {\n\t\tif _, ok := err.(thrift.TProtocolException); ok {\n\t\t\t// We failed to parse the Thrift generated code, so convert the error to bad request.\n\t\t\terr = tchannel.NewSystemError(tchannel.ErrCodeBadRequest, err.Error())\n\t\t}\n\n\t\treader.Close()\n\t\tcall.Response().SendSystemError(err)\n\t\treturn nil\n\t}\n\n\tif err := argreader.EnsureEmpty(reader, \"reading request body\"); err != nil {\n\t\treturn err\n\t}\n\tif err := reader.Close(); err != nil {\n\t\treturn err\n\t}\n\n\tif !success {\n\t\tcall.Response().SetApplicationError()\n\t}\n\n\twriter, err := call.Response().Arg2Writer()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := WriteHeaders(writer, ctx.ResponseHeaders()); err != nil {\n\t\treturn err\n\t}\n\tif err := writer.Close(); err != nil {\n\t\treturn err\n\t}\n\n\twriter, err = call.Response().Arg3Writer()\n\n\twp = getProtocolWriter(writer)\n\tdefer thriftProtocolPool.Put(wp)\n\n\tif err := resp.Write(wp.protocol); err != nil {\n\t\tcall.Response().SendSystemError(err)\n\t\treturn err\n\t}\n\n\treturn writer.Close()\n}\n\nfunc getServiceMethod(method string) (string, string, bool) {\n\ts := string(method)\n\tsep := strings.Index(s, \"::\")\n\tif sep == -1 {\n\t\treturn \"\", \"\", false\n\t}\n\treturn s[:sep], s[sep+2:], true\n}\n\n// Handle handles an incoming TChannel call and forwards it to the correct handler.\nfunc (s *Server) Handle(ctx context.Context, call *tchannel.InboundCall) {\n\top := call.MethodString()\n\tservice, method, ok := getServiceMethod(op)\n\tif !ok {\n\t\tlog.Fatalf(\"Handle got call for %s which does not match the expected call format\", op)\n\t}\n\n\ts.RLock()\n\thandler, ok := s.handlers[service]\n\ts.RUnlock()\n\tif !ok {\n\t\tlog.Fatalf(\"Handle got call for service %v which is not registered\", service)\n\t}\n\n\tif err := s.handle(ctx, handler, method, call); err != nil {\n\t\ts.onError(call, err)\n\t}\n}\n"
  },
  {
    "path": "thrift/server_test.go",
    "content": "package thrift\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\tathrift \"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\nvar errIO = errors.New(\"IO Error\")\n\n// badTStruct implements TStruct that always fails with the provided error.\ntype badTStruct struct {\n\t// If specified, runs the specified function before failing the Write.\n\tPreWrite func(athrift.TProtocol)\n\n\tErr error\n}\n\nfunc (t *badTStruct) Write(p athrift.TProtocol) error {\n\tif t.PreWrite != nil {\n\t\tt.PreWrite(p)\n\t}\n\treturn t.Err\n}\n\nfunc (t *badTStruct) Read(p athrift.TProtocol) error {\n\treturn t.Err\n}\n\n// nullTStruct implements TStruct that does nothing at all with no errors.\ntype nullTStruct struct{}\n\nfunc (*nullTStruct) Write(p athrift.TProtocol) error {\n\treturn nil\n}\n\nfunc (*nullTStruct) Read(p athrift.TProtocol) error {\n\treturn nil\n}\n\n// thriftStruction is a TChannel service that implements the following\n// methods:\n//\n//\tdestruct\n//\t  Returns a TStruct that fails without writing anything.\n//\tpartialDestruct\n//\t  Returns a TStruct that fails after writing partial output.\ntype thriftStruction struct{}\n\nfunc (ts *thriftStruction) Handle(\n\tctx Context,\n\tmethodName string,\n\tprotocol athrift.TProtocol,\n) (success bool, resp athrift.TStruct, err error) {\n\tvar preWrite func(athrift.TProtocol)\n\tif methodName == \"partialDestruct\" {\n\t\tpreWrite = func(p athrift.TProtocol) {\n\t\t\tp.WriteStructBegin(\"foo\")\n\t\t\tp.WriteFieldBegin(\"bar\", athrift.STRING, 42)\n\t\t\tp.WriteString(\"baz\")\n\t\t}\n\t}\n\n\t// successful call with a TStruct that fails while writing.\n\treturn true, &badTStruct{Err: errIO, PreWrite: preWrite}, nil\n}\n\nfunc (ts *thriftStruction) Service() string { return \"destruct\" }\n\nfunc (ts *thriftStruction) Methods() []string {\n\treturn []string{\"destruct\", \"partialDestruct\"}\n}\n\nfunc TestHandleTStructError(t *testing.T) {\n\tserverOpts := testutils.NewOpts().\n\t\tAddLogFilter(\n\t\t\t\"Thrift server error.\", 1,\n\t\t\t\"error\", \"IO Error\",\n\t\t\t\"method\", \"destruct::destruct\").\n\t\tAddLogFilter(\n\t\t\t\"Thrift server error.\", 1,\n\t\t\t\"error\", \"IO Error\",\n\t\t\t\"method\", \"destruct::partialDestruct\")\n\tserver := testutils.NewTestServer(t, serverOpts)\n\tdefer server.CloseAndVerify()\n\n\t// Create a thrift server with a handler that returns success with\n\t// TStructs that refuse to do I/O.\n\ttchan := server.Server()\n\tNewServer(tchan).Register(&thriftStruction{})\n\n\tclient := NewClient(\n\t\tserver.NewClient(testutils.NewOpts()),\n\t\ttchan.ServiceName(),\n\t\t&ClientOptions{HostPort: server.HostPort()},\n\t)\n\n\tt.Run(\"failing response\", func(t *testing.T) {\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\t_, err := client.Call(ctx, \"destruct\", \"destruct\", &nullTStruct{}, &nullTStruct{})\n\t\tassert.Error(t, err)\n\t\tassert.IsType(t, tchannel.SystemError{}, err)\n\t\tassert.Equal(t, tchannel.ErrCodeUnexpected, tchannel.GetSystemErrorCode(err))\n\t\tassert.Equal(t, \"IO Error\", tchannel.GetSystemErrorMessage(err))\n\t})\n\n\tt.Run(\"failing response with partial write\", func(t *testing.T) {\n\t\tctx, cancel := NewContext(time.Second)\n\t\tdefer cancel()\n\n\t\t_, err := client.Call(ctx, \"destruct\", \"partialDestruct\", &nullTStruct{}, &nullTStruct{})\n\t\tassert.Error(t, err)\n\t\tassert.IsType(t, tchannel.SystemError{}, err)\n\t\tassert.Equal(t, tchannel.ErrCodeUnexpected, tchannel.GetSystemErrorCode(err))\n\t\tassert.Equal(t, \"IO Error\", tchannel.GetSystemErrorMessage(err))\n\t})\n}\n"
  },
  {
    "path": "thrift/struct.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"io\"\n\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// WriteStruct writes the given Thrift struct to a writer. It pools TProtocols.\nfunc WriteStruct(writer io.Writer, s thrift.TStruct) error {\n\twp := getProtocolWriter(writer)\n\terr := s.Write(wp.protocol)\n\tthriftProtocolPool.Put(wp)\n\treturn err\n}\n\n// ReadStruct reads the given Thrift struct. It pools TProtocols.\nfunc ReadStruct(reader io.Reader, s thrift.TStruct) error {\n\twp := getProtocolReader(reader)\n\terr := s.Read(wp.protocol)\n\tthriftProtocolPool.Put(wp)\n\treturn err\n}\n"
  },
  {
    "path": "thrift/struct_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift_test\n\nimport (\n\t\"bytes\"\n\t\"io/ioutil\"\n\t\"sync\"\n\t\"testing\"\n\n\t. \"github.com/uber/tchannel-go/thrift\"\n\n\t\"github.com/uber/tchannel-go/testutils/testreader\"\n\t\"github.com/uber/tchannel-go/testutils/testwriter\"\n\t\"github.com/uber/tchannel-go/thrift/gen-go/test\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\nvar structTest = struct {\n\ts       thrift.TStruct\n\tencoded []byte\n}{\n\ts: &test.Data{\n\t\tB1: true,\n\t\tS2: \"S2\",\n\t\tI3: 3,\n\t},\n\tencoded: []byte{\n\t\t0x2,      // bool\n\t\t0x0, 0x1, // field 1\n\t\t0x1,      // true\n\t\t0xb,      // string\n\t\t0x0, 0x2, // field 2\n\t\t0x0, 0x0, 0x0, 0x2, // length of string \"S2\"\n\t\t'S', '2', // string \"S2\"\n\t\t0x8,      // i32\n\t\t0x0, 0x3, // field 3\n\t\t0x0, 0x0, 0x0, 0x3, // i32 3\n\t\t0x0, // end of struct\n\t},\n}\n\nfunc TestReadStruct(t *testing.T) {\n\tappendBytes := func(bs []byte, append []byte) []byte {\n\t\tb := make([]byte, len(bs)+len(append))\n\t\tn := copy(b, bs)\n\t\tcopy(b[n:], append)\n\t\treturn b\n\t}\n\n\ttests := []struct {\n\t\ts        thrift.TStruct\n\t\tencoded  []byte\n\t\twantErr  bool\n\t\tleftover []byte\n\t}{\n\t\t{\n\t\t\ts:       structTest.s,\n\t\t\tencoded: structTest.encoded,\n\t\t},\n\t\t{\n\t\t\ts: &test.Data{\n\t\t\t\tB1: true,\n\t\t\t\tS2: \"S2\",\n\t\t\t},\n\t\t\t// Missing field 3.\n\t\t\tencoded: structTest.encoded[:len(structTest.encoded)-8],\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\ts:        structTest.s,\n\t\t\tencoded:  appendBytes(structTest.encoded, []byte{1, 2, 3, 4}),\n\t\t\tleftover: []byte{1, 2, 3, 4},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\treader := bytes.NewReader(tt.encoded)\n\t\tvar s thrift.TStruct = &test.Data{}\n\t\terr := ReadStruct(reader, s)\n\t\tassert.Equal(t, tt.wantErr, err != nil, \"Unexpected error: %v\", err)\n\n\t\t// Even if there's an error, the struct will be partially filled.\n\t\tassert.Equal(t, tt.s, s, \"Unexpected struct\")\n\n\t\tleftover, err := ioutil.ReadAll(reader)\n\t\tif assert.NoError(t, err, \"Read leftover bytes failed\") {\n\t\t\t// ReadAll always returns a non-nil byte slice.\n\t\t\tif tt.leftover == nil {\n\t\t\t\ttt.leftover = make([]byte, 0)\n\t\t\t}\n\t\t\tassert.Equal(t, tt.leftover, leftover, \"Leftover bytes mismatch\")\n\t\t}\n\t}\n}\n\nfunc TestReadStructErr(t *testing.T) {\n\twriter, reader := testreader.ChunkReader()\n\twriter <- structTest.encoded[:10]\n\twriter <- nil\n\tclose(writer)\n\n\ts := &test.Data{}\n\terr := ReadStruct(reader, s)\n\tif assert.Error(t, err, \"ReadStruct should fail\") {\n\t\t// Apache Thrift just prepends the error message, and doesn't give us access\n\t\t// to the underlying error, so we can't check the underlying error exactly.\n\t\tassert.Contains(t, err.Error(), testreader.ErrUser.Error(), \"Underlying error missing\")\n\t}\n}\n\nfunc TestWriteStruct(t *testing.T) {\n\ttests := []struct {\n\t\ts       thrift.TStruct\n\t\tencoded []byte\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\ts:       structTest.s,\n\t\t\tencoded: structTest.encoded,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tbuf := &bytes.Buffer{}\n\t\terr := WriteStruct(buf, tt.s)\n\t\tassert.Equal(t, tt.wantErr, err != nil, \"Unexpected err: %v\", err)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tassert.Equal(t, tt.encoded, buf.Bytes(), \"Encoded data mismatch\")\n\t}\n}\n\nfunc TestWriteStructErr(t *testing.T) {\n\twriter := testwriter.Limited(10)\n\terr := WriteStruct(writer, structTest.s)\n\tif assert.Error(t, err, \"WriteStruct should fail\") {\n\t\t// Apache Thrift just prepends the error message, and doesn't give us access\n\t\t// to the underlying error, so we can't check the underlying error exactly.\n\t\tassert.Contains(t, err.Error(), testwriter.ErrOutOfSpace.Error(), \"Underlying error missing\")\n\t}\n}\n\nfunc TestParallelReadWrites(t *testing.T) {\n\tvar wg sync.WaitGroup\n\ttestBG := func(f func(t *testing.T)) {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tf(t)\n\t\t\twg.Done()\n\t\t}()\n\t}\n\tfor i := 0; i < 50; i++ {\n\t\ttestBG(TestReadStruct)\n\t\ttestBG(TestWriteStruct)\n\t}\n\twg.Wait()\n}\n\nfunc BenchmarkWriteStruct(b *testing.B) {\n\tbuf := &bytes.Buffer{}\n\tfor i := 0; i < b.N; i++ {\n\t\tbuf.Reset()\n\t\tWriteStruct(buf, structTest.s)\n\t}\n}\n\nfunc BenchmarkReadStruct(b *testing.B) {\n\tbuf := bytes.NewReader(structTest.encoded)\n\tvar d test.Data\n\n\tbuf.Seek(0, 0)\n\tassert.NoError(b, ReadStruct(buf, &d))\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tbuf.Seek(0, 0)\n\t\tReadStruct(buf, &d)\n\t}\n}\n"
  },
  {
    "path": "thrift/tchan-meta.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"fmt\"\n\n\tathrift \"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n\tgen \"github.com/uber/tchannel-go/thrift/gen-go/meta\"\n)\n\n// Interfaces for the service and client for the services defined in the IDL.\n\n// tchanMeta is the interface that defines the server handler and client interface.\ntype tchanMeta interface {\n\tHealth(ctx Context, req *gen.HealthRequest) (*gen.HealthStatus, error)\n\tThriftIDL(ctx Context) (*gen.ThriftIDLs, error)\n\tVersionInfo(ctx Context) (*gen.VersionInfo, error)\n}\n\n// Implementation of a client and service handler.\n\ntype tchanMetaClient struct {\n\tthriftService string\n\tclient        TChanClient\n}\n\nfunc newTChanMetaClient(client TChanClient) tchanMeta {\n\treturn &tchanMetaClient{\n\t\t\"Meta\",\n\t\tclient,\n\t}\n}\n\nfunc (c *tchanMetaClient) Health(ctx Context, req *gen.HealthRequest) (*gen.HealthStatus, error) {\n\tvar resp gen.MetaHealthResult\n\targs := gen.MetaHealthArgs{\n\t\tHr: req,\n\t}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"health\", &args, &resp)\n\tif err == nil && !success {\n\t}\n\n\treturn resp.GetSuccess(), err\n}\n\nfunc (c *tchanMetaClient) ThriftIDL(ctx Context) (*gen.ThriftIDLs, error) {\n\tvar resp gen.MetaThriftIDLResult\n\targs := gen.MetaThriftIDLArgs{}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"thriftIDL\", &args, &resp)\n\tif err == nil && !success {\n\t}\n\n\treturn resp.GetSuccess(), err\n}\n\nfunc (c *tchanMetaClient) VersionInfo(ctx Context) (*gen.VersionInfo, error) {\n\tvar resp gen.MetaVersionInfoResult\n\targs := gen.MetaVersionInfoArgs{}\n\tsuccess, err := c.client.Call(ctx, c.thriftService, \"versionInfo\", &args, &resp)\n\tif err == nil && !success {\n\t}\n\n\treturn resp.GetSuccess(), err\n}\n\ntype tchanMetaServer struct {\n\thandler tchanMeta\n}\n\nfunc newTChanMetaServer(handler tchanMeta) TChanServer {\n\treturn &tchanMetaServer{\n\t\thandler,\n\t}\n}\n\nfunc (s *tchanMetaServer) Service() string {\n\treturn \"Meta\"\n}\n\nfunc (s *tchanMetaServer) Methods() []string {\n\treturn []string{\n\t\t\"health\",\n\t\t\"thriftIDL\",\n\t\t\"versionInfo\",\n\t}\n}\n\nfunc (s *tchanMetaServer) Handle(ctx Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\tcase \"health\":\n\t\treturn s.handleHealth(ctx, protocol)\n\tcase \"thriftIDL\":\n\t\treturn s.handleThriftIDL(ctx, protocol)\n\tcase \"versionInfo\":\n\t\treturn s.handleVersionInfo(ctx, protocol)\n\n\tdefault:\n\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\nfunc (s *tchanMetaServer) handleHealth(ctx Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req gen.MetaHealthArgs\n\tvar res gen.MetaHealthResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tr, err :=\n\t\ts.handler.Health(ctx, req.Hr)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t}\n\tres.Success = r\n\n\treturn err == nil, &res, nil\n}\n\nfunc (s *tchanMetaServer) handleThriftIDL(ctx Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req gen.MetaThriftIDLArgs\n\tvar res gen.MetaThriftIDLResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tr, err :=\n\t\ts.handler.ThriftIDL(ctx)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t}\n\tres.Success = r\n\n\treturn err == nil, &res, nil\n}\n\nfunc (s *tchanMetaServer) handleVersionInfo(ctx Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tvar req gen.MetaVersionInfoArgs\n\tvar res gen.MetaVersionInfoResult\n\n\tif err := req.Read(protocol); err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tr, err :=\n\t\ts.handler.VersionInfo(ctx)\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t}\n\tres.Success = r\n\n\treturn err == nil, &res, nil\n}\n"
  },
  {
    "path": "thrift/test.thrift",
    "content": "struct Data {\n  1: required bool b1,\n  2: required string s2,\n  3: required i32 i3\n}\n\nexception SimpleErr {\n  1: string message\n}\n\nexception NewErr {\n  1: string message\n}\n\nservice SimpleService {\n  Data Call(1: Data arg)\n  void Simple() throws (1: SimpleErr simpleErr)\n  void SimpleFuture() throws (1: SimpleErr simpleErr, 2: NewErr newErr)\n}\n\nservice SecondService {\n  string Echo(1: string arg)\n}\n\nstruct HealthStatus {\n    1: required bool ok\n    2: optional string message\n}\n\n// Meta contains the old health endpoint without arguments.\nservice Meta {\n    HealthStatus health()\n}"
  },
  {
    "path": "thrift/thrift-gen/compile_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/uber/tchannel-go/testutils\"\n)\n\n// These tests ensure that the code generator generates valid code that can be built\n// in combination with Thrift's autogenerated code.\n\nconst _tchannelPackage = \"github.com/uber/tchannel-go\"\n\nvar (\n\t_testGoPath     string\n\t_testGoPathOnce sync.Once\n)\n\nfunc TestMain(m *testing.M) {\n\texitCode := m.Run()\n\n\t// If we created a fake GOPATH, we should clean it up on success.\n\tif _testGoPath != \"\" && exitCode == 0 {\n\t\tos.RemoveAll(_testGoPath)\n\t}\n\n\tos.Exit(exitCode)\n}\n\nfunc getTChannelDir(goPath string) string {\n\treturn filepath.Join(goPath, \"src\", _tchannelPackage)\n}\n\nfunc getCurrentTChannelPath(t *testing.T) string {\n\twd, err := os.Getwd()\n\trequire.NoError(t, err, \"Failed to get working directory\")\n\n\t// Walk up \"wd\" till we find \"tchannel-go\".\n\tfor filepath.Base(wd) != filepath.Base(_tchannelPackage) {\n\t\twd = filepath.Dir(wd)\n\t\tif wd == \"\" {\n\t\t\tt.Fatalf(\"Failed to find tchannel-go in parents of current directory\")\n\t\t}\n\t}\n\n\treturn wd\n}\n\nfunc createGoPath(t *testing.T) {\n\tgoPath, err := ioutil.TempDir(\"\", \"thrift-gen\")\n\trequire.NoError(t, err, \"TempDir failed\")\n\n\t// Create $GOPATH/src/github.com/uber/tchannel-go and symlink everything.\n\t// And then create a dummy directory for all the test output.\n\ttchannelDir := getTChannelDir(goPath)\n\trequire.NoError(t, os.MkdirAll(tchannelDir, 0755), \"MkDirAll failed\")\n\n\t// Symlink the contents of tchannel-go into the temp directory.\n\trealTChannelDir := getCurrentTChannelPath(t)\n\trealDirContents, err := ioutil.ReadDir(realTChannelDir)\n\trequire.NoError(t, err, \"Failed to read real tchannel-go dir\")\n\n\tfor _, f := range realDirContents {\n\t\trealPath := filepath.Join(realTChannelDir, f.Name())\n\t\terr := os.Symlink(realPath, filepath.Join(tchannelDir, filepath.Base(f.Name())))\n\t\trequire.NoError(t, err, \"Failed to symlink %v\", f.Name())\n\t}\n\n\t_testGoPath = goPath\n\n\t// None of the other tests in this package should use GOPATH, so we don't\n\t// restore this.\n\tos.Setenv(\"GOPATH\", goPath)\n}\n\nfunc getOutputDir(t *testing.T) (dir, pkg string) {\n\t_testGoPathOnce.Do(func() { createGoPath(t) })\n\n\t// Create a random directory inside of the GOPATH in tmp\n\trandStr := testutils.RandString(10)\n\trandDir := filepath.Join(getTChannelDir(_testGoPath), randStr)\n\t// In case it's not empty.\n\tos.RemoveAll(randDir)\n\n\treturn randDir, filepath.Join(_tchannelPackage, randStr)\n}\n\nfunc TestAllThrift(t *testing.T) {\n\tfiles, err := ioutil.ReadDir(\"test_files\")\n\trequire.NoError(t, err, \"Cannot read test_files directory: %v\", err)\n\n\tfor _, f := range files {\n\t\tfname := f.Name()\n\t\tif f.IsDir() || filepath.Ext(fname) != \".thrift\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := runBuildTest(t, filepath.Join(\"test_files\", fname)); err != nil {\n\t\t\tt.Errorf(\"Thrift file %v failed: %v\", fname, err)\n\t\t}\n\t}\n}\n\nfunc TestIncludeThrift(t *testing.T) {\n\tdirs, err := ioutil.ReadDir(\"test_files/include_test\")\n\trequire.NoError(t, err, \"Cannot read test_files/include_test directory: %v\", err)\n\n\tfor _, d := range dirs {\n\t\tdname := d.Name()\n\t\tif !d.IsDir() {\n\t\t\tcontinue\n\t\t}\n\n\t\tthriftFile := filepath.Join(dname, path.Base(dname)+\".thrift\")\n\t\tif err := runBuildTest(t, filepath.Join(\"test_files/include_test/\", thriftFile)); err != nil {\n\t\t\tt.Errorf(\"Thrift test %v failed: %v\", dname, err)\n\t\t}\n\t}\n}\n\nfunc TestMultipleFiles(t *testing.T) {\n\tif err := runBuildTest(t, filepath.Join(\"test_files\", \"multi_test\", \"file1.thrift\")); err != nil {\n\t\tt.Errorf(\"Multiple file test failed: %v\", err)\n\t}\n}\n\nfunc TestExternalTemplate(t *testing.T) {\n\ttemplate1 := `package {{ .Package }}\n\n{{ range .AST.Services }}\n// Service {{ .Name }} has {{ len .Methods }} methods.\n{{ range .Methods }}\n// func {{ .Name | goPublicName }} ({{ range .Arguments }}{{ .Type | goType }}, {{ end }}) ({{ if .ReturnType }}{{ .ReturnType | goType }}{{ end }}){{ end }}\n{{ end }}\n\t`\n\ttemplateFile := writeTempFile(t, template1)\n\tdefer os.Remove(templateFile)\n\n\texpected := `package service_extend\n\n// Service S1 has 1 methods.\n\n// func M1 ([]byte, ) ([]byte)\n\n// Service S2 has 1 methods.\n\n// func M2 (*S, int32, ) (*S)\n\n// Service S3 has 1 methods.\n\n// func M3 () ()\n`\n\n\topts := processOptions{\n\t\tInputFile:     \"test_files/service_extend.thrift\",\n\t\tTemplateFiles: []string{templateFile},\n\t}\n\tchecks := func(dir string) error {\n\t\tdir = filepath.Join(dir, \"service_extend\")\n\t\tif err := checkDirectoryFiles(dir, 6); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Verify the contents of the extra file.\n\t\toutFile := filepath.Join(dir, defaultPackageName(templateFile)+\"-service_extend.go\")\n\t\treturn verifyFileContents(outFile, expected)\n\t}\n\tif err := runTest(t, opts, checks); err != nil {\n\t\tt.Errorf(\"Failed to run test: %v\", err)\n\t}\n}\n\nfunc writeTempFile(t *testing.T, contents string) string {\n\ttempFile, err := ioutil.TempFile(\"\", \"temp\")\n\trequire.NoError(t, err, \"Failed to create temp file\")\n\ttempFile.Close()\n\trequire.NoError(t, ioutil.WriteFile(tempFile.Name(), []byte(contents), 0666),\n\t\t\"Write temp file failed\")\n\treturn tempFile.Name()\n}\n\nfunc verifyFileContents(filename, expected string) error {\n\tbytes, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbytesStr := string(bytes)\n\tif bytesStr != expected {\n\t\treturn fmt.Errorf(\"file contents mismatch. got:\\n%vexpected:\\n%v\", bytesStr, expected)\n\t}\n\n\treturn nil\n}\n\nfunc copyFile(src, dst string) error {\n\tf, err := os.Open(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\twriteF, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer writeF.Close()\n\n\t_, err = io.Copy(writeF, f)\n\treturn err\n}\n\n// setupDirectory creates a temporary directory.\nfunc setupDirectory(thriftFile string) (string, error) {\n\ttempDir, err := ioutil.TempDir(\"\", \"thrift-gen\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn tempDir, nil\n}\n\nfunc createAdditionalTestFile(thriftFile, tempDir string) error {\n\tf, err := os.Open(thriftFile)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar writer io.Writer\n\trdr := bufio.NewReader(f)\n\tfor {\n\t\tline, err := rdr.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\tif strings.HasPrefix(line, \"//Go code:\") {\n\t\t\tfileName := strings.TrimSpace(strings.TrimPrefix(line, \"//Go code:\"))\n\t\t\toutFile := filepath.Join(tempDir, fileName)\n\t\t\tf, err := os.OpenFile(outFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer f.Close()\n\t\t\twriter = f\n\t\t} else if writer != nil {\n\t\t\tif strings.HasPrefix(line, \"//\") {\n\t\t\t\twriter.Write([]byte(strings.TrimPrefix(line, \"//\")))\n\t\t\t} else {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc checkDirectoryFiles(dir string, n int) error {\n\tdirContents, err := ioutil.ReadDir(dir)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(dirContents) < n {\n\t\treturn fmt.Errorf(\"expected to generate at least %v files, but found: %v\", n, len(dirContents))\n\t}\n\n\treturn nil\n}\n\nfunc runBuildTest(t *testing.T, thriftFile string) error {\n\textraChecks := func(dir string) error {\n\t\treturn checkDirectoryFiles(filepath.Join(dir, defaultPackageName(thriftFile)), 4)\n\t}\n\n\topts := processOptions{InputFile: thriftFile}\n\treturn runTest(t, opts, extraChecks)\n}\n\nfunc runTest(t *testing.T, opts processOptions, extraChecks func(string) error) error {\n\ttempDir, outputPkg := getOutputDir(t)\n\n\t// Generate code from the Thrift file.\n\t*packagePrefix = outputPkg + \"/\"\n\topts.GenerateThrift = true\n\topts.OutputDir = tempDir\n\tif err := processFile(opts); err != nil {\n\t\treturn fmt.Errorf(\"processFile(%s) in %q failed: %v\", opts.InputFile, tempDir, err)\n\t}\n\n\t// Create any extra Go files as specified in the Thrift file.\n\tif err := createAdditionalTestFile(opts.InputFile, tempDir); err != nil {\n\t\treturn fmt.Errorf(\"failed creating additional test files for %s in %q: %v\", opts.InputFile, tempDir, err)\n\t}\n\n\t// Run go build to ensure that the generated code builds.\n\tcmd := exec.Command(\"go\", \"build\", \"./...\")\n\tcmd.Dir = tempDir\n\t// NOTE: we check output, since go build ./... returns 0 status code on failure:\n\t// https://github.com/golang/go/issues/11407\n\tvar (\n\t\toutput, err = cmd.CombinedOutput()\n\t\toutputLines []string\n\t)\n\tfor _, s := range strings.Split(string(output), \"\\n\") {\n\t\t// Exclude expected output like vendor package downloads and formatting lines\n\t\tif strings.HasPrefix(s, \"go: downloading\") || strings.TrimSpace(s) == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\toutputLines = append(outputLines, s)\n\t}\n\tif err != nil || len(outputLines) > 0 {\n\t\treturn fmt.Errorf(\"build in %q failed.\\nError: %v Output:\\n%v\", tempDir, err, string(output))\n\t}\n\n\t// Run any extra checks the user may want.\n\tif err := extraChecks(tempDir); err != nil {\n\t\treturn err\n\t}\n\n\t// Only delete the temp directory on success.\n\tos.RemoveAll(tempDir)\n\treturn nil\n}\n"
  },
  {
    "path": "thrift/thrift-gen/extends.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n)\n\n// setExtends will set the ExtendsService for all services.\n// It is done after all files are parsed, as services may extend those\n// found in an included file.\nfunc setExtends(state map[string]parseState) error {\n\tfor _, v := range state {\n\t\tfor _, s := range v.services {\n\t\t\tif s.Extends == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tvar searchServices []*Service\n\t\t\tvar searchFor string\n\t\t\tparts := strings.SplitN(s.Extends, \".\", 2)\n\t\t\t// If it's not imported, then look at the current file's services.\n\t\t\tif len(parts) < 2 {\n\t\t\t\tsearchServices = v.services\n\t\t\t\tsearchFor = s.Extends\n\t\t\t} else {\n\t\t\t\tinclude := v.global.includes[parts[0]]\n\t\t\t\ts.ExtendsPrefix = include.pkg + \".\"\n\t\t\t\tsearchServices = state[include.file].services\n\t\t\t\tsearchFor = parts[1]\n\t\t\t}\n\n\t\t\tfoundService := sort.Search(len(searchServices), func(i int) bool {\n\t\t\t\treturn searchServices[i].Name >= searchFor\n\t\t\t})\n\t\t\tif foundService == len(searchServices) {\n\t\t\t\treturn fmt.Errorf(\"failed to find base service %q for %q\", s.Extends, s.Name)\n\t\t\t}\n\t\t\ts.ExtendsService = searchServices[foundService]\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "thrift/thrift-gen/generate.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nvar (\n\tthriftBinary       = flag.String(\"thriftBinary\", \"thrift\", \"Command to use for the Apache Thrift binary\")\n\tapacheThriftImport = flag.String(\"thriftImport\", \"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\", \"Go package to use for the Thrift import\")\n\tpackagePrefix      = flag.String(\"packagePrefix\", \"\", \"The package prefix (will be used similar to how Apache Thrift uses it)\")\n)\n\nfunc execCmd(name string, args ...string) error {\n\tcmd := exec.Command(name, args...)\n\tcmd.Stdout = os.Stdout\n\tcmd.Stderr = os.Stderr\n\treturn cmd.Run()\n}\n\nfunc execThrift(args ...string) error {\n\treturn execCmd(*thriftBinary, args...)\n}\n\nfunc deleteRemote(dir string) error {\n\treturn filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif !info.IsDir() || !strings.HasSuffix(path, \"-remote\") {\n\t\t\treturn nil\n\t\t}\n\n\t\tif err := os.RemoveAll(path); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// Once the directory is deleted, we can skip the rest of it.\n\t\treturn filepath.SkipDir\n\t})\n}\n\nfunc runThrift(inFile string, outDir string) error {\n\tinFile, err := filepath.Abs(inFile)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Delete any existing generated code for this Thrift file.\n\tgenDir := filepath.Join(outDir, defaultPackageName(inFile))\n\tif err := execCmd(\"rm\", \"-rf\", genDir); err != nil {\n\t\treturn fmt.Errorf(\"failed to delete directory %s: %v\", genDir, err)\n\t}\n\n\t// Generate the Apache Thrift generated code.\n\tgoArgs := fmt.Sprintf(\"go:thrift_import=%s,package_prefix=%s\", *apacheThriftImport, *packagePrefix)\n\tif err := execThrift(\"-r\", \"--gen\", goArgs, \"-out\", outDir, inFile); err != nil {\n\t\treturn fmt.Errorf(\"thrift compile failed: %v\", err)\n\t}\n\n\t// Delete the -remote folders.\n\tif err := deleteRemote(outDir); err != nil {\n\t\treturn fmt.Errorf(\"failed to delete -remote folders: %v\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "thrift/thrift-gen/gopath.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\n// ResolveWithGoPath will resolve the filename relative to GOPATH and returns\n// the first file that exists, or an error otherwise.\nfunc ResolveWithGoPath(filename string) (string, error) {\n\tfor _, file := range goPathCandidates(filename) {\n\t\tif _, err := os.Stat(file); !os.IsNotExist(err) {\n\t\t\treturn file, nil\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"file not found on GOPATH: %q\", filename)\n}\n\nfunc goPathCandidates(filename string) []string {\n\tcandidates := []string{filename}\n\tpaths := filepath.SplitList(os.Getenv(\"GOPATH\"))\n\tfor _, path := range paths {\n\t\tresolvedFilename := filepath.Join(path, \"src\", filename)\n\t\tcandidates = append(candidates, resolvedFilename)\n\t}\n\n\treturn candidates\n}\n"
  },
  {
    "path": "thrift/thrift-gen/gopath_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc getFakeFS(t *testing.T) string {\n\tfiles := []string{\n\t\t\"src/pkg1/sub/ringpop.thriftgen\",\n\t\t\"src/pkg2/sub/ringpop.thriftgen\",\n\t}\n\n\ttempDir, err := ioutil.TempDir(\"\", \"thriftgen\")\n\trequire.NoError(t, err, \"TempDir failed\")\n\n\tfor _, f := range files {\n\t\trequire.NoError(t, os.MkdirAll(filepath.Join(tempDir, filepath.Dir(f)), 0770),\n\t\t\t\"Failed to create directory structure for %v\", f)\n\t\trequire.NoError(t, ioutil.WriteFile(filepath.Join(tempDir, f), nil, 0660),\n\t\t\t\"Failed to create dummy file\")\n\t}\n\treturn tempDir\n}\n\nfunc TestGoPathCandidates(t *testing.T) {\n\ttests := []struct {\n\t\tgoPath             string\n\t\tfilename           string\n\t\texpectedCandidates []string\n\t}{\n\t\t{\n\t\t\tgoPath:   \"onepath\",\n\t\t\tfilename: \"github.com/uber/tchannel-go/tchan.thrift-gen\",\n\t\t\texpectedCandidates: []string{\n\t\t\t\t\"github.com/uber/tchannel-go/tchan.thrift-gen\",\n\t\t\t\t\"onepath/src/github.com/uber/tchannel-go/tchan.thrift-gen\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tgoPath:   \"onepath:secondpath\",\n\t\t\tfilename: \"github.com/uber/tchannel-go/tchan.thrift-gen\",\n\t\t\texpectedCandidates: []string{\n\t\t\t\t\"github.com/uber/tchannel-go/tchan.thrift-gen\",\n\t\t\t\t\"onepath/src/github.com/uber/tchannel-go/tchan.thrift-gen\",\n\t\t\t\t\"secondpath/src/github.com/uber/tchannel-go/tchan.thrift-gen\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tos.Setenv(\"GOPATH\", tt.goPath)\n\t\tcandidates := goPathCandidates(tt.filename)\n\t\tif !reflect.DeepEqual(candidates, tt.expectedCandidates) {\n\t\t\tt.Errorf(\"GOPATH=%s FileCandidatesWithGopath(%s) = %q, want %q\",\n\t\t\t\ttt.goPath, tt.filename, candidates, tt.expectedCandidates)\n\t\t}\n\t}\n}\n\nfunc TestResolveWithGoPath(t *testing.T) {\n\tgoPath1 := getFakeFS(t)\n\tgoPath2 := getFakeFS(t)\n\tos.Setenv(\"GOPATH\", goPath1+string(filepath.ListSeparator)+goPath2)\n\tdefer os.RemoveAll(goPath1)\n\tdefer os.RemoveAll(goPath2)\n\n\ttests := []struct {\n\t\tfilename string\n\t\twant     string\n\t\twantErr  bool\n\t}{\n\t\t{\n\t\t\tfilename: \"pkg1/sub/ringpop.thriftgen\",\n\t\t\twant:     filepath.Join(goPath1, \"src/pkg1/sub/ringpop.thriftgen\"),\n\t\t},\n\t\t{\n\t\t\tfilename: \"pkg2/sub/ringpop.thriftgen\",\n\t\t\twant:     filepath.Join(goPath1, \"src/pkg2/sub/ringpop.thriftgen\"),\n\t\t},\n\t\t{\n\t\t\tfilename: filepath.Join(goPath2, \"src/pkg2/sub/ringpop.thriftgen\"),\n\t\t\twant:     filepath.Join(goPath2, \"src/pkg2/sub/ringpop.thriftgen\"),\n\t\t},\n\t\t{\n\t\t\tfilename: \"pkg3/sub/ringpop.thriftgen\",\n\t\t\twantErr:  true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tfile, err := ResolveWithGoPath(tt.filename)\n\t\tgotErr := err != nil\n\t\tassert.Equal(t, tt.wantErr, gotErr, \"%v expected error: %v got: %v\", tt.filename, tt.wantErr, err)\n\t\tassert.Equal(t, tt.want, file, \"%v expected to resolve to %v, got %v\", tt.filename, tt.want, file)\n\t}\n}\n"
  },
  {
    "path": "thrift/thrift-gen/include.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport \"github.com/samuel/go-thrift/parser\"\n\n// Include represents a single include statement in the Thrift file.\ntype Include struct {\n\tkey  string\n\tfile string\n\tpkg  string\n}\n\n// Import returns the go import to use for this package.\nfunc (i *Include) Import() string {\n\t// TODO(prashant): Rename imports so they don't clash with standard imports.\n\t// This is not high priority since Apache thrift clashes already with \"bytes\" and \"fmt\".\n\t// which are the same imports we would clash with.\n\treturn *packagePrefix + i.Package()\n}\n\n// Package returns the package selector for this package.\nfunc (i *Include) Package() string {\n\treturn i.pkg\n}\n\nfunc createIncludes(parsed *parser.Thrift, all map[string]parseState) map[string]*Include {\n\tincludes := make(map[string]*Include)\n\tfor k, v := range parsed.Includes {\n\t\tincluded := all[v]\n\t\tincludes[k] = &Include{\n\t\t\tkey:  k,\n\t\t\tfile: v,\n\t\t\tpkg:  included.namespace,\n\t\t}\n\t}\n\treturn includes\n}\n"
  },
  {
    "path": "thrift/thrift-gen/main.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// thrift-gen generates code for Thrift services that can be used with the\n// uber/tchannel/thrift package. thrift-gen generated code relies on the\n// Apache Thrift generated code for serialization/deserialization, and should\n// be a part of the generated code's package.\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\t\"text/template\"\n\n\t\"github.com/samuel/go-thrift/parser\"\n)\n\nconst tchannelThriftImport = \"github.com/uber/tchannel-go/thrift\"\n\nvar (\n\tgenerateThrift = flag.Bool(\"generateThrift\", false, \"Whether to generate all Thrift go code\")\n\tinputFile      = flag.String(\"inputFile\", \"\", \"The .thrift file to generate a client for\")\n\toutputDir      = flag.String(\"outputDir\", \"gen-go\", \"The output directory to generate go code to.\")\n\tskipTChannel   = flag.Bool(\"skipTChannel\", false, \"Whether to skip the TChannel template\")\n\ttemplateFiles  = NewStringSliceFlag(\"template\", \"Template file to compile code from\")\n\n\tnlSpaceNL = regexp.MustCompile(`\\n[ \\t]+\\n`)\n)\n\n// TemplateData is the data passed to the template that generates code.\ntype TemplateData struct {\n\tPackage  string\n\tAST      *parser.Thrift\n\tServices []*Service\n\tIncludes map[string]*Include\n\tImports  imports\n\n\t// global should not be directly exported to the template, but functions on\n\t// global can be exposed to templates.\n\tglobal *State\n}\n\ntype imports struct {\n\tThrift   string\n\tTChannel string\n}\n\nfunc main() {\n\tflag.Parse()\n\tif *inputFile == \"\" {\n\t\tlog.Fatalf(\"Please specify an inputFile\")\n\t}\n\n\topts := processOptions{\n\t\tInputFile:      *inputFile,\n\t\tGenerateThrift: *generateThrift,\n\t\tOutputDir:      *outputDir,\n\t\tSkipTChannel:   *skipTChannel,\n\t\tTemplateFiles:  *templateFiles,\n\t}\n\tif err := processFile(opts); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\ntype processOptions struct {\n\tInputFile      string\n\tGenerateThrift bool\n\tOutputDir      string\n\tSkipTChannel   bool\n\tTemplateFiles  []string\n}\n\nfunc processFile(opts processOptions) error {\n\tif err := os.MkdirAll(opts.OutputDir, 0770); err != nil {\n\t\treturn fmt.Errorf(\"failed to create output directory %q: %v\", opts.OutputDir, err)\n\t}\n\n\tif opts.GenerateThrift {\n\t\tif err := runThrift(opts.InputFile, opts.OutputDir); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to run thrift for file %q: %v\", opts.InputFile, err)\n\t\t}\n\t}\n\n\tallParsed, err := parseFile(opts.InputFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse file %q: %v\", opts.InputFile, err)\n\t}\n\n\tallTemplates, err := parseTemplates(opts.SkipTChannel, opts.TemplateFiles)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse templates: %v\", err)\n\t}\n\n\tfor filename, v := range allParsed {\n\t\tpkg := getNamespace(filename, v.ast)\n\n\t\tfor _, template := range allTemplates {\n\t\t\toutputFile := filepath.Join(opts.OutputDir, pkg, template.outputFile(pkg))\n\t\t\tif err := generateCode(outputFile, template, pkg, v); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype parseState struct {\n\tast       *parser.Thrift\n\tnamespace string\n\tglobal    *State\n\tservices  []*Service\n}\n\n// parseTemplates returns a list of Templates that must be rendered given the template files.\nfunc parseTemplates(skipTChannel bool, templateFiles []string) ([]*Template, error) {\n\tvar templates []*Template\n\n\tif !skipTChannel {\n\t\ttemplates = append(templates, &Template{\n\t\t\tname:     \"tchan\",\n\t\t\ttemplate: template.Must(parseTemplate(tchannelTmpl)),\n\t\t})\n\t}\n\n\tfor _, f := range templateFiles {\n\t\tt, err := parseTemplateFile(f)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\ttemplates = append(templates, t)\n\t}\n\n\treturn templates, nil\n}\n\nfunc parseFile(inputFile string) (map[string]parseState, error) {\n\tparser := &parser.Parser{}\n\tparsed, _, err := parser.ParseFile(inputFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tallParsed := make(map[string]parseState)\n\tfor filename, v := range parsed {\n\t\tstate := newState(v, allParsed)\n\t\tservices, err := wrapServices(v, state)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"wrap services failed: %v\", err)\n\t\t}\n\n\t\tnamespace := getNamespace(filename, v)\n\t\tallParsed[filename] = parseState{v, namespace, state, services}\n\t}\n\tsetIncludes(allParsed)\n\treturn allParsed, setExtends(allParsed)\n}\n\nfunc defaultPackageName(fullPath string) string {\n\tfilename := filepath.Base(fullPath)\n\tfile := strings.TrimSuffix(filename, filepath.Ext(filename))\n\treturn strings.ToLower(file)\n}\n\nfunc getNamespace(filename string, v *parser.Thrift) string {\n\tif ns, ok := v.Namespaces[\"go\"]; ok {\n\t\treturn ns\n\t}\n\n\t// TODO(prashant): Remove any characters that are not valid in Go package names.\n\treturn defaultPackageName(filename)\n}\n\nfunc generateCode(outputFile string, template *Template, pkg string, state parseState) error {\n\tif outputFile == \"\" {\n\t\treturn fmt.Errorf(\"must speciy an output file\")\n\t}\n\tif len(state.services) == 0 {\n\t\treturn nil\n\t}\n\n\ttd := TemplateData{\n\t\tPackage:  pkg,\n\t\tAST:      state.ast,\n\t\tIncludes: state.global.includes,\n\t\tServices: state.services,\n\t\tglobal:   state.global,\n\t\tImports: imports{\n\t\t\tThrift:   *apacheThriftImport,\n\t\t\tTChannel: tchannelThriftImport,\n\t\t},\n\t}\n\treturn template.execute(outputFile, td)\n}\n\ntype stringSliceFlag []string\n\nfunc (s *stringSliceFlag) String() string {\n\treturn strings.Join(*s, \", \")\n}\n\nfunc (s *stringSliceFlag) Set(in string) error {\n\t*s = append(*s, in)\n\treturn nil\n}\n\n// NewStringSliceFlag creates a new string slice flag. The default value is always nil.\nfunc NewStringSliceFlag(name string, usage string) *[]string {\n\tvar ss stringSliceFlag\n\tflag.Var(&ss, name, usage)\n\treturn (*[]string)(&ss)\n}\n"
  },
  {
    "path": "thrift/thrift-gen/names.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\n// This file implements go name generation for thrift identifiers.\n// It has to match the Apache Thrift generated names.\n\nimport \"strings\"\n\n// goKeywords taken from https://golang.org/ref/spec#Keywords (and added error).\nvar goKeywords = map[string]bool{\n\t\"error\":       true,\n\t\"break\":       true,\n\t\"default\":     true,\n\t\"func\":        true,\n\t\"interface\":   true,\n\t\"select\":      true,\n\t\"case\":        true,\n\t\"defer\":       true,\n\t\"go\":          true,\n\t\"map\":         true,\n\t\"struct\":      true,\n\t\"chan\":        true,\n\t\"else\":        true,\n\t\"goto\":        true,\n\t\"package\":     true,\n\t\"switch\":      true,\n\t\"const\":       true,\n\t\"fallthrough\": true,\n\t\"if\":          true,\n\t\"range\":       true,\n\t\"type\":        true,\n\t\"continue\":    true,\n\t\"for\":         true,\n\t\"import\":      true,\n\t\"return\":      true,\n\t\"var\":         true,\n}\n\n// This set is taken from https://github.com/golang/lint/blob/master/lint.go#L692\nvar commonInitialisms = map[string]bool{\n\t\"API\":   true,\n\t\"ASCII\": true,\n\t\"CPU\":   true,\n\t\"CSS\":   true,\n\t\"DNS\":   true,\n\t\"EOF\":   true,\n\t\"GUID\":  true,\n\t\"HTML\":  true,\n\t\"HTTP\":  true,\n\t\"HTTPS\": true,\n\t\"ID\":    true,\n\t\"IP\":    true,\n\t\"JSON\":  true,\n\t\"LHS\":   true,\n\t\"QPS\":   true,\n\t\"RAM\":   true,\n\t\"RHS\":   true,\n\t\"RPC\":   true,\n\t\"SLA\":   true,\n\t\"SMTP\":  true,\n\t\"SQL\":   true,\n\t\"SSH\":   true,\n\t\"TCP\":   true,\n\t\"TLS\":   true,\n\t\"TTL\":   true,\n\t\"UDP\":   true,\n\t\"UI\":    true,\n\t\"UID\":   true,\n\t\"UUID\":  true,\n\t\"URI\":   true,\n\t\"URL\":   true,\n\t\"UTF8\":  true,\n\t\"VM\":    true,\n\t\"XML\":   true,\n\t\"XSRF\":  true,\n\t\"XSS\":   true,\n}\n\nfunc goName(name string) string {\n\t// Thrift Identifier from IDL: ( Letter | '_' ) ( Letter | Digit | '.' | '_' )*\n\t// Go identifier from spec: letter { letter | unicode_digit } .\n\t// Go letter allows underscore, so the only difference is period. However, periods cannot\n\t// actaully be used - this seems to be a bug in the IDL.\n\tif _, ok := goKeywords[name]; ok {\n\t\t// The thrift compiler appends _a1 for any clashes with go keywords.\n\t\tname += \"_a1\"\n\t}\n\n\tname = camelCase(name, false /* publicName */)\n\treturn name\n}\n\n// camelCase takes a name with underscores such as my_arg and returns camelCase (e.g. myArg).\n// if publicName is true, then it returns UpperCamelCase.\n// This method will also fix common initialisms (e.g. ID, API, etc).\nfunc camelCase(name string, publicName bool) string {\n\tparts := strings.Split(name, \"_\")\n\tstartAt := 1\n\tif publicName {\n\t\tstartAt = 0\n\t}\n\tfor i := startAt; i < len(parts); i++ {\n\t\tname := parts[i]\n\t\tif name == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\t// For all words except the first, if the first letter of the word is\n\t\t// uppercase, Thrift keeps the underscore.\n\t\tif i > 0 && strings.ToUpper(name[0:1]) == name[0:1] {\n\t\t\tname = \"_\" + name\n\t\t} else {\n\t\t\tname = strings.ToUpper(name[0:1]) + name[1:]\n\t\t}\n\n\t\tif isInitialism := commonInitialisms[strings.ToUpper(name)]; isInitialism {\n\t\t\tname = strings.ToUpper(name)\n\t\t}\n\n\t\tparts[i] = name\n\t}\n\treturn strings.Join(parts, \"\")\n}\n\nfunc avoidThriftClash(name string) string {\n\tif strings.HasSuffix(name, \"Result\") ||\n\t\tstrings.HasSuffix(name, \"Args\") ||\n\t\tstrings.HasPrefix(name, \"New\") {\n\t\treturn name + \"_\"\n\t}\n\treturn name\n}\n\n// goPublicName returns a go identifier that is exported.\nfunc goPublicName(name string) string {\n\treturn camelCase(name, true /* publicName */)\n}\n\n// goPublicFieldName returns the name of the field as used in a struct.\nfunc goPublicFieldName(name string) string {\n\treturn avoidThriftClash(goPublicName(name))\n}\n\nvar thriftToGo = map[string]string{\n\t\"bool\":   \"bool\",\n\t\"byte\":   \"int8\",\n\t\"i16\":    \"int16\",\n\t\"i32\":    \"int32\",\n\t\"i64\":    \"int64\",\n\t\"double\": \"float64\",\n\t\"string\": \"string\",\n}\n"
  },
  {
    "path": "thrift/thrift-gen/tchannel-template.go",
    "content": "package main\n\nvar tchannelTmpl = `\n// @generated Code generated by thrift-gen. Do not modify.\n\n// Package {{ .Package }} is generated code used to make or handle TChannel calls using Thrift.\npackage {{ .Package }}\n\nimport (\n\"fmt\"\n\nathrift \"{{ .Imports.Thrift }}\"\n\"{{ .Imports.TChannel }}\"\n\n{{ range .Includes }}\n\t\"{{ .Import }}\"\n{{ end }}\n)\n\n\n{{ range .Includes }}\n\tvar _ = {{ .Package }}.GoUnusedProtection__\n{{ end }}\n\n\n// Interfaces for the service and client for the services defined in the IDL.\n\n{{ range .Services }}\n// {{ .Interface }} is the interface that defines the server handler and client interface.\ntype {{ .Interface }} interface {\n\t{{ if .HasExtends }}\n\t\t{{ .ExtendsServicePrefix }}{{ .ExtendsService.Interface }}\n\n\t{{ end }}\n\t{{ range .Methods }}\n\t\t{{ .Name }}({{ .ArgList }}) {{ .RetType }}\n\t{{ end }}\n}\n{{ end }}\n\n// Implementation of a client and service handler.\n\n{{/* Generate client and service implementations for the above interfaces. */}}\n{{ range $svc := .Services }}\ntype {{ .ClientStruct }} struct {\n\t{{ if .HasExtends }}\n\t\t{{ .ExtendsServicePrefix }}{{ .ExtendsService.Interface }}\n\n\t{{ end }}\n\tthriftService string\n\tclient        thrift.TChanClient\n}\n\n\nfunc {{ .InheritedClientConstructor }}(thriftService string, client thrift.TChanClient) *{{ .ClientStruct }} {\n\treturn &{{ .ClientStruct }}{\n\t\t{{ if .HasExtends }}\n\t\t\t{{ .ExtendsServicePrefix }}{{ .ExtendsService.InheritedClientConstructor }}(thriftService, client),\n\t\t{{ end }}\n\t\tthriftService,\n\t\tclient,\n\t}\n}\n\n// {{ .ClientConstructor }} creates a client that can be used to make remote calls.\nfunc {{ .ClientConstructor }}(client thrift.TChanClient) {{ .Interface }} {\n\treturn {{ .InheritedClientConstructor }}(\"{{ .ThriftName }}\", client)\n}\n\n{{ range .Methods }}\n\tfunc (c *{{ $svc.ClientStruct }}) {{ .Name }}({{ .ArgList }}) {{ .RetType }} {\n\t\tvar resp {{ .ResultType }}\n\t\targs := {{ .ArgsType }}{\n\t\t\t{{ range .Arguments }}\n\t\t\t\t{{ .ArgStructName }}: {{ .Name }},\n\t\t\t{{ end }}\n\t\t}\n\t\tsuccess, err := c.client.Call(ctx, c.thriftService, \"{{ .ThriftName }}\", &args, &resp)\n\t\tif err == nil && !success {\n\t\t\tswitch {\n\t\t\t{{ range .Exceptions }}\n\t\t\tcase resp.{{ .ArgStructName }} != nil:\n\t\t\t\terr = resp.{{ .ArgStructName }}\n\t\t\t{{ end }}\n\t\t\tdefault:\n\t\t\t\terr = fmt.Errorf(\"received no result or unknown exception for {{ .ThriftName }}\")\n\t\t\t}\n\t\t}\n\n\t\t{{ if .HasReturn }}\n\t\t\treturn resp.GetSuccess(), err\n\t\t{{ else }}\n\t\t\treturn err\n\t\t{{ end }}\n\t}\n{{ end }}\n\ntype {{ .ServerStruct }} struct {\n\t{{ if .HasExtends }}\n\t\tthrift.TChanServer\n\n\t{{ end }}\n\thandler {{ .Interface }}\n}\n\n// {{ .ServerConstructor }} wraps a handler for {{ .Interface }} so it can be\n// registered with a thrift.Server.\nfunc {{ .ServerConstructor }}(handler {{ .Interface }}) thrift.TChanServer {\n\treturn &{{ .ServerStruct }}{\n\t\t{{ if .HasExtends }}\n\t\t\t{{ .ExtendsServicePrefix }}{{ .ExtendsService.ServerConstructor }}(handler),\n\t\t{{ end }}\n\t\thandler,\n\t}\n}\n\nfunc (s *{{ .ServerStruct }}) Service() string {\n\treturn \"{{ .ThriftName }}\"\n}\n\nfunc (s *{{ .ServerStruct }}) Methods() []string {\n\treturn []string{\n\t\t{{ range .Methods }}\n\t\t\t\"{{ .ThriftName }}\",\n\t\t{{ end }}\n\t\t{{ range .InheritedMethods }}\n\t\t\t\"{{ . }}\",\n\t\t{{ end }}\n\t}\n}\n\nfunc (s *{{ .ServerStruct }}) Handle(ctx {{ contextType }}, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\tswitch methodName {\n\t\t{{ range .Methods }}\n\t\t\tcase \"{{ .ThriftName }}\":\n\t\t\t\treturn s.{{ .HandleFunc }}(ctx, protocol)\n\t\t{{ end }}\n\t\t{{ range .InheritedMethods }}\n\t\t\tcase \"{{ . }}\":\n\t\t\t\treturn s.TChanServer.Handle(ctx, methodName, protocol)\n\t\t{{ end }}\n\t\tdefault:\n\t\t\treturn false, nil, fmt.Errorf(\"method %v not found in service %v\", methodName, s.Service())\n\t}\n}\n\n{{ range .Methods }}\n\tfunc (s *{{ $svc.ServerStruct }}) {{ .HandleFunc }}(ctx {{ contextType }}, protocol athrift.TProtocol) (bool, athrift.TStruct, error) {\n\t\tvar req {{ .ArgsType }}\n\t\tvar res {{ .ResultType }}\n\n\t\tif err := req.Read(protocol); err != nil {\n\t\t\treturn false, nil, err\n\t\t}\n\n\t\t{{ if .HasReturn }}\n\t\t\tr, err :=\n\t\t{{ else }}\n\t\t\terr :=\n\t\t{{ end }}\n\t\t\t\ts.handler.{{ .Name }}({{ .CallList \"req\" }})\n\n\t\tif err != nil {\n\t\t\t{{ if .HasExceptions }}\n\t\t\tswitch v := err.(type) {\n\t\t\t\t{{ range .Exceptions }}\n\t\t\t\t\tcase {{ .ArgType }}:\n\t\t\t\t\t\tif v == nil {\n\t\t\t\t\t\t\treturn false, nil, fmt.Errorf(\"Handler for {{ .Name }} returned non-nil error type {{ .ArgType }} but nil value\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tres.{{ .ArgStructName }} = v\n\t\t\t\t{{ end }}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false, nil, err\n\t\t\t}\n\t\t\t{{ else }}\n\t\t\t\treturn false, nil, err\n\t\t\t{{ end }}\n\t\t} else {\n    {{ if .HasReturn }}\n\t\t  res.Success = {{ .WrapResult \"r\" }}\n\t\t{{ end }}\n    }\n\n\t\treturn err == nil, &res, nil\n\t}\n\n{{ end }}\n\n{{ end }}\n`\n"
  },
  {
    "path": "thrift/thrift-gen/template.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// thrift-gen generates code for Thrift services that can be used with the\n// uber/tchannel/thrift package. thrift-gen generated code relies on the\n// Apache Thrift generated code for serialization/deserialization, and should\n// be a part of the generated code's package.\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os/exec\"\n\t\"text/template\"\n)\n\n// Template represents a single thrift-gen template that will be used to generate code.\ntype Template struct {\n\tname     string\n\ttemplate *template.Template\n}\n\n// dummyGoType is a function to be passed to the test/template package as the named\n// function 'goType'. This named function is overwritten by an actual implementation\n// specific to the thrift file being rendered at the time of rendering.\nfunc dummyGoType() string {\n\treturn \"\"\n}\n\nfunc parseTemplate(contents string) (*template.Template, error) {\n\tfuncs := map[string]interface{}{\n\t\t\"contextType\":   contextType,\n\t\t\"goPrivateName\": goName,\n\t\t\"goPublicName\":  goPublicName,\n\t\t\"goType\":        dummyGoType,\n\t}\n\treturn template.New(\"thrift-gen\").Funcs(funcs).Parse(contents)\n}\n\nfunc parseTemplateFile(file string) (*Template, error) {\n\tfile, err := ResolveWithGoPath(file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tbytes, err := ioutil.ReadFile(file)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read file %q: %v\", file, err)\n\t}\n\n\tt, err := parseTemplate(string(bytes))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse template in file %q: %v\", file, err)\n\t}\n\n\treturn &Template{defaultPackageName(file), t}, nil\n}\n\nfunc contextType() string {\n\treturn \"thrift.Context\"\n}\n\nfunc cleanGeneratedCode(generated []byte) []byte {\n\tgenerated = nlSpaceNL.ReplaceAll(generated, []byte(\"\\n\"))\n\treturn generated\n}\n\n// withStateFuncs adds functions to the template that are dependent upon state.\nfunc (t *Template) withStateFuncs(td TemplateData) *template.Template {\n\treturn t.template.Funcs(map[string]interface{}{\n\t\t\"goType\": td.global.goType,\n\t})\n}\n\nfunc (t *Template) execute(outputFile string, td TemplateData) error {\n\tbuf := &bytes.Buffer{}\n\tif err := t.withStateFuncs(td).Execute(buf, td); err != nil {\n\t\treturn fmt.Errorf(\"failed to execute template: %v\", err)\n\t}\n\n\tgenerated := cleanGeneratedCode(buf.Bytes())\n\tif err := ioutil.WriteFile(outputFile, generated, 0660); err != nil {\n\t\treturn fmt.Errorf(\"cannot write output file %q: %v\", outputFile, err)\n\t}\n\n\t// Run gofmt on the file (ignore any errors)\n\texec.Command(\"gofmt\", \"-w\", outputFile).Run()\n\treturn nil\n}\n\nfunc (t *Template) outputFile(pkg string) string {\n\treturn fmt.Sprintf(\"%v-%v.go\", t.name, pkg)\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/binary.thrift",
    "content": "typedef binary Z\n\nstruct S {\n  1: binary s1\n  2: Z s2\n}\n\nservice Test {\n  binary M1(1: binary arg1)\n  S M2(1: binary arg1, 2: S arg2)\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/byte.thrift",
    "content": "typedef byte Z\n\nstruct S {\n  1: byte s1\n  2: Z s2\n}\n\nservice Test {\n  byte M1(1: byte arg1)\n  S M2(1: byte arg1, 2: S arg2)\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/gokeywords.thrift",
    "content": "// Test to make sure that reserved names are handled correctly.\n\nexception Exception {\n  1: required string message\n}\n\nstruct Result {\n  1: required string error\n  2: required i32 func\n  3: required i32 chan\n  4: required i32 result\n  5: required i64 newRole\n}\n\nservice func {\n  string func1()\n  void func(1: i32 func)\n  Result chan(1: i32 func, 2: i32 result) throws (1: Exception error)\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/include_test/namespace/a/shared.thrift",
    "content": "namespace go a_shared\n\ninclude \"../b/shared.thrift\"\n\ntypedef shared.b_string a_string\n\nservice AShared extends shared.BShared {\n\tbool healthA()\n}"
  },
  {
    "path": "thrift/thrift-gen/test_files/include_test/namespace/b/shared.thrift",
    "content": "namespace go b_shared\n\ntypedef string b_string\n\nservice BShared {\n\tbool healthB()\n}"
  },
  {
    "path": "thrift/thrift-gen/test_files/include_test/namespace/namespace.thrift",
    "content": "include \"a/shared.thrift\"\n\nservice Foo extends shared.AShared {\n void Foo(1: shared.a_string str)\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/include_test/simple/shared.thrift",
    "content": "include \"shared2.thrift\"\n\ntypedef string a_shared_string\n\ntypedef shared2.a_shared2_string a_shared_string2\n\ntypedef shared2.a_shared2_mystruct a_shared_mystruct2\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/include_test/simple/shared2.thrift",
    "content": "typedef string a_shared2_string\n\nstruct MyStruct {\n  1: string name\n}\n\ntypedef MyStruct a_shared2_mystruct \n"
  },
  {
    "path": "thrift/thrift-gen/test_files/include_test/simple/simple.thrift",
    "content": "include \"shared.thrift\"\ninclude \"shared2.thrift\"\n\nservice Foo {\n  void Foo(1: shared.a_shared_string str, 2: shared.a_shared_string2 str2, 3: shared2.MyStruct str3)\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/include_test/svc_extend/shared.thrift",
    "content": "typedef string UUID\n\n\nstruct Health {\n  1: bool ok\n}\n\nservice FooBase {\n  UUID getUUID()\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/include_test/svc_extend/svc_extend.thrift",
    "content": "include \"shared.thrift\"\n\nservice Foo extends shared.FooBase {\n  shared.UUID getMyUUID(1: shared.UUID uuid, 2: shared.Health health)\n  shared.Health health(1: shared.UUID uuid, 2: shared.Health health)\n}\n\n//Go code: svc_extend/test.go\n// package svc_extend\n// var _ = TChanFoo(nil).GetMyUUID\n// var _ = TChanFoo(nil).Health\n// var _ = TChanFoo(nil).GetUUID\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/multi_test/file1.thrift",
    "content": "include \"file2.thrift\"\n\nservice Foo1 {\n  void M()\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/multi_test/file2.thrift",
    "content": "\nservice Foo2 {\n  void M()\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/service_extend.thrift",
    "content": "struct S {\n  1: binary s1\n}\n\nservice S1 {\n  binary M1(1: binary bits)\n}\n\nservice S2 extends S1 {\n  S M2(1: optional S s, 2: optional i32 i)\n}\n\nservice S3 extends S2 {\n  void M3()\n}\n\n//Go code: service_extend/test.go\n// package service_extend\n// var _ = TChanS3(nil).M1\n// var _ = TChanS3(nil).M2\n// var _ = TChanS3(nil).M3\n// var _ int32 = S2M2Args{}.I\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/sets.thrift",
    "content": "\nservice Test {\n  list<i32> getInts(1: list<i32> nums)\n  set<i32> getIntSet(1: set<i32> nums)\n  map<i32, string> getIntMap(1: map<i32, string> nums)\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/test1.thrift",
    "content": "\nstruct FakeStruct {\n 1: i64 id\n 2: i64 user_id\n}\n\nservice Fake {\n  // Test initialisms in the method name (as well as name clashes).\n  void id_get()\n  void id()\n  void get_id()\n  void get_Id()\n  void get_ID()\n\n  // Test initialisms in parameter names.\n  void initialisms_in_args1(1: string LoL_http_TEST_Name)\n  void initialisms_in_args2(1: string user_id)\n  void initialisms_in_args3(1: string id)\n\n\n  // Test casing for method names\n  void fAkE(1: i32 func, 2: i32 pkg, 3: FakeStruct fakeStruct)\n\n  void MyArgs()\n  void MyResult()\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/typedefs.thrift",
    "content": "\ntypedef i64 X\ntypedef X Z\ntypedef X Y\ntypedef i64 i\ntypedef i64 func\n\nstruct S {\n  1: X x\n  2: Y y\n  3: Z z\n}\n\ntypedef S ST\n\nenum Operator\n{\n  ADD = 1,\n  SUBTRACT = 2\n}\n\nservice Test {\n  Y M1(1: X arg1, 2: i arg2)\n  X M2(1: Y arg1)\n  Z M3(1: X arg1)\n  S M4(1: S arg1, 2: Operator op)\n\n  // Thrift compiler is broken on this case.\n  // ST M5(1: ST arg1, 2: S arg2)\n}\n"
  },
  {
    "path": "thrift/thrift-gen/test_files/union.thrift",
    "content": "union Constraint {\n  1: i32 intV\n  2: string stringV\n}\n\n// thrift-gen generated code assumes there is a service.\nservice Test {}\n\n"
  },
  {
    "path": "thrift/thrift-gen/typestate.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"strings\"\n\n\t\"github.com/samuel/go-thrift/parser\"\n)\n\n// State is global Thrift state for a file with type information.\ntype State struct {\n\t// typedefs is a map from a typedef name to the underlying type.\n\ttypedefs map[string]*parser.Type\n\n\t// includes is a map from Thrift base name to the include.\n\tincludes map[string]*Include\n\n\t// all is used for includes.\n\tall map[string]parseState\n}\n\n// newState parses the type information for a parsed Thrift file and returns the state.\nfunc newState(v *parser.Thrift, all map[string]parseState) *State {\n\ttypedefs := make(map[string]*parser.Type)\n\tfor k, v := range v.Typedefs {\n\t\ttypedefs[k] = v.Type\n\t}\n\n\t// Enums are typedefs to an int64.\n\ti64Type := &parser.Type{Name: \"i64\"}\n\tfor k := range v.Enums {\n\t\ttypedefs[k] = i64Type\n\t}\n\n\treturn &State{typedefs, nil, all}\n}\n\nfunc setIncludes(all map[string]parseState) {\n\tfor _, v := range all {\n\t\tv.global.includes = createIncludes(v.ast, all)\n\t}\n}\n\nfunc (s *State) isBasicType(thriftType string) bool {\n\t_, ok := thriftToGo[thriftType]\n\treturn ok\n}\n\n// rootType recurses through typedefs and returns the underlying type.\nfunc (s *State) rootType(thriftType *parser.Type) *parser.Type {\n\tif state, newType, include := s.checkInclude(thriftType); include != nil {\n\t\treturn state.rootType(newType)\n\t}\n\n\tif v, ok := s.typedefs[thriftType.Name]; ok {\n\t\treturn s.rootType(v)\n\t}\n\treturn thriftType\n}\n\n// checkInclude will check if the type is an included type, and if so, return the\n// state and type from the state for that file.\nfunc (s *State) checkInclude(thriftType *parser.Type) (*State, *parser.Type, *Include) {\n\tparts := strings.SplitN(thriftType.Name, \".\", 2)\n\tif len(parts) < 2 {\n\t\treturn nil, nil, nil\n\t}\n\n\tnewType := *thriftType\n\tnewType.Name = parts[1]\n\n\tinclude := s.includes[parts[0]]\n\tstate := s.all[include.file]\n\treturn state.global, &newType, include\n}\n\n// isResultPointer returns whether the result for this method is a pointer.\nfunc (s *State) isResultPointer(thriftType *parser.Type) bool {\n\t_, basicGoType := thriftToGo[s.rootType(thriftType).Name]\n\treturn !basicGoType\n}\n\n// goType returns the Go type name for the given thrift type.\nfunc (s *State) goType(thriftType *parser.Type) string {\n\treturn s.goTypePrefix(\"\", thriftType)\n}\n\n// goTypePrefix returns the Go type name for the given thrift type with the prefix.\nfunc (s *State) goTypePrefix(prefix string, thriftType *parser.Type) string {\n\tswitch thriftType.Name {\n\tcase \"binary\":\n\t\treturn \"[]byte\"\n\tcase \"list\":\n\t\treturn \"[]\" + s.goType(thriftType.ValueType)\n\tcase \"set\":\n\t\treturn \"map[\" + s.goType(thriftType.ValueType) + \"]bool\"\n\tcase \"map\":\n\t\treturn \"map[\" + s.goType(thriftType.KeyType) + \"]\" + s.goType(thriftType.ValueType)\n\t}\n\n\t// If the type is imported, then ignore the package.\n\tif state, newType, include := s.checkInclude(thriftType); include != nil {\n\t\treturn state.goTypePrefix(include.Package()+\".\", newType)\n\t}\n\n\t// If the type is a direct Go type, use that.\n\tif goType, ok := thriftToGo[thriftType.Name]; ok {\n\t\treturn goType\n\t}\n\n\tgoThriftName := goPublicFieldName(thriftType.Name)\n\tgoThriftName = prefix + goThriftName\n\n\t// Check if the type has a typedef to the direct Go type.\n\trootType := s.rootType(thriftType)\n\tif _, ok := thriftToGo[rootType.Name]; ok {\n\t\treturn goThriftName\n\t}\n\tif rootType.Name == \"list\" ||\n\t\trootType.Name == \"set\" ||\n\t\trootType.Name == \"map\" {\n\t\treturn goThriftName\n\t}\n\n\t// If it's a typedef to another struct, then the typedef is defined as a pointer\n\t// so we do not want the pointer type here.\n\tif rootType != thriftType {\n\t\treturn goThriftName\n\t}\n\n\t// If it's not a typedef for a basic type, we use a pointer.\n\treturn \"*\" + goThriftName\n}\n"
  },
  {
    "path": "thrift/thrift-gen/validate.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/samuel/go-thrift/parser\"\n)\n\n// Validate validates that the given spec is supported by thrift-gen.\nfunc Validate(svc *parser.Service) error {\n\tfor _, m := range svc.Methods {\n\t\tif err := validateMethod(svc, m); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc validateMethod(svc *parser.Service, m *parser.Method) error {\n\tif m.Oneway {\n\t\treturn fmt.Errorf(\"oneway methods are not supported: %s.%v\", svc.Name, m.Name)\n\t}\n\tfor _, arg := range m.Arguments {\n\t\tif arg.Optional {\n\t\t\t// Go treats argument structs as \"Required\" in the generated code interface.\n\t\t\targ.Optional = false\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "thrift/thrift-gen/wrap.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/samuel/go-thrift/parser\"\n)\n\ntype byServiceName []*Service\n\nfunc (l byServiceName) Len() int           { return len(l) }\nfunc (l byServiceName) Less(i, j int) bool { return l[i].Service.Name < l[j].Service.Name }\nfunc (l byServiceName) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }\n\nfunc wrapServices(v *parser.Thrift, state *State) ([]*Service, error) {\n\tvar services []*Service\n\tfor _, s := range v.Services {\n\t\tif err := Validate(s); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tservices = append(services, &Service{\n\t\t\tService: s,\n\t\t\tstate:   state,\n\t\t})\n\t}\n\n\t// Have a stable ordering for services so code generation is consistent.\n\tsort.Sort(byServiceName(services))\n\treturn services, nil\n}\n\n// Service is a wrapper for parser.Service.\ntype Service struct {\n\t*parser.Service\n\tstate *State\n\n\t// ExtendsService and ExtendsPrefix are set in `setExtends`.\n\tExtendsService *Service\n\tExtendsPrefix  string\n\n\t// methods is a cache of all methods.\n\tmethods []*Method\n\t// inheritedMethods is a list of inherited method names.\n\tinheritedMethods []string\n}\n\n// ThriftName returns the thrift identifier for this service.\nfunc (s *Service) ThriftName() string {\n\treturn s.Service.Name\n}\n\n// Interface returns the name of the interface representing the service.\nfunc (s *Service) Interface() string {\n\treturn \"TChan\" + goPublicName(s.Name)\n}\n\n// ClientStruct returns the name of the unexported struct that satisfies the interface as a client.\nfunc (s *Service) ClientStruct() string {\n\treturn \"tchan\" + goPublicName(s.Name) + \"Client\"\n}\n\n// ClientConstructor returns the name of the constructor used to create a client.\nfunc (s *Service) ClientConstructor() string {\n\treturn \"NewTChan\" + goPublicName(s.Name) + \"Client\"\n}\n\n// InheritedClientConstructor returns the name of the constructor used by the generated code\n// for inherited services. This allows the parent service to set the service name that should\n// be used.\nfunc (s *Service) InheritedClientConstructor() string {\n\treturn \"NewTChan\" + goPublicName(s.Name) + \"InheritedClient\"\n}\n\n// ServerStruct returns the name of the unexported struct that satisfies TChanServer.\nfunc (s *Service) ServerStruct() string {\n\treturn \"tchan\" + goPublicName(s.Name) + \"Server\"\n}\n\n// ServerConstructor returns the name of the constructor used to create the TChanServer interface.\nfunc (s *Service) ServerConstructor() string {\n\treturn \"NewTChan\" + goPublicName(s.Name) + \"Server\"\n}\n\n// HasExtends returns whether this service extends another service.\nfunc (s *Service) HasExtends() bool {\n\treturn s.ExtendsService != nil\n}\n\n// ExtendsServicePrefix returns a package selector (if any) for the extended service.\nfunc (s *Service) ExtendsServicePrefix() string {\n\tif dotIndex := strings.Index(s.Extends, \".\"); dotIndex > 0 {\n\t\treturn s.ExtendsPrefix\n\t}\n\treturn \"\"\n}\n\ntype byMethodName []*Method\n\nfunc (l byMethodName) Len() int           { return len(l) }\nfunc (l byMethodName) Less(i, j int) bool { return l[i].Method.Name < l[j].Method.Name }\nfunc (l byMethodName) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }\n\n// Methods returns the methods on this service, not including methods from inherited services.\nfunc (s *Service) Methods() []*Method {\n\tif s.methods != nil {\n\t\treturn s.methods\n\t}\n\n\tfor _, m := range s.Service.Methods {\n\t\ts.methods = append(s.methods, &Method{m, s, s.state})\n\t}\n\tsort.Sort(byMethodName(s.methods))\n\treturn s.methods\n}\n\n// InheritedMethods returns names for inherited methods on this service.\nfunc (s *Service) InheritedMethods() []string {\n\tif s.inheritedMethods != nil {\n\t\treturn s.inheritedMethods\n\t}\n\n\tfor svc := s.ExtendsService; svc != nil; svc = svc.ExtendsService {\n\t\tfor m := range svc.Service.Methods {\n\t\t\ts.inheritedMethods = append(s.inheritedMethods, m)\n\t\t}\n\t}\n\tsort.Strings(s.inheritedMethods)\n\n\treturn s.inheritedMethods\n}\n\n// Method is a wrapper for parser.Method.\ntype Method struct {\n\t*parser.Method\n\n\tservice *Service\n\tstate   *State\n}\n\n// ThriftName returns the thrift identifier for this function.\nfunc (m *Method) ThriftName() string {\n\treturn m.Method.Name\n}\n\n// Name returns the go method name.\nfunc (m *Method) Name() string {\n\treturn goPublicName(m.Method.Name)\n}\n\n// HandleFunc is the go method name for the handle function which decodes the payload.\nfunc (m *Method) HandleFunc() string {\n\treturn \"handle\" + goPublicName(m.Method.Name)\n}\n\n// Arguments returns the argument declarations for this method.\nfunc (m *Method) Arguments() []*Field {\n\tvar args []*Field\n\tfor _, f := range m.Method.Arguments {\n\t\targs = append(args, &Field{f, m.state})\n\t}\n\treturn args\n}\n\n// Exceptions returns the exceptions that this method may return.\nfunc (m *Method) Exceptions() []*Field {\n\tvar args []*Field\n\tfor _, f := range m.Method.Exceptions {\n\t\targs = append(args, &Field{f, m.state})\n\t}\n\treturn args\n}\n\n// HasReturn returns false if this method is declared as void in the Thrift file.\nfunc (m *Method) HasReturn() bool {\n\treturn m.Method.ReturnType != nil\n}\n\n// HasExceptions returns true if this method has\nfunc (m *Method) HasExceptions() bool {\n\treturn len(m.Method.Exceptions) > 0\n}\n\nfunc (m *Method) argResPrefix() string {\n\treturn goPublicName(m.service.Name) + m.Name()\n}\n\n// ArgsType returns the Go name for the struct used to encode the method's arguments.\nfunc (m *Method) ArgsType() string {\n\treturn m.argResPrefix() + \"Args\"\n}\n\n// ResultType returns the Go name for the struct used to encode the method's result.\nfunc (m *Method) ResultType() string {\n\treturn m.argResPrefix() + \"Result\"\n}\n\n// ArgList returns the argument list for the function.\nfunc (m *Method) ArgList() string {\n\targs := []string{\"ctx \" + contextType()}\n\tfor _, arg := range m.Arguments() {\n\t\targs = append(args, arg.Declaration())\n\t}\n\treturn strings.Join(args, \", \")\n}\n\n// CallList creates the call to a function satisfying Interface from an Args struct.\nfunc (m *Method) CallList(reqStruct string) string {\n\targs := []string{\"ctx\"}\n\tfor _, arg := range m.Arguments() {\n\t\targs = append(args, reqStruct+\".\"+arg.ArgStructName())\n\t}\n\treturn strings.Join(args, \", \")\n}\n\n// RetType returns the go return type of the method.\nfunc (m *Method) RetType() string {\n\tif !m.HasReturn() {\n\t\treturn \"error\"\n\t}\n\treturn fmt.Sprintf(\"(%v, %v)\", m.state.goType(m.Method.ReturnType), \"error\")\n}\n\n// WrapResult wraps the result variable before being used in the result struct.\nfunc (m *Method) WrapResult(respVar string) string {\n\tif !m.HasReturn() {\n\t\tpanic(\"cannot wrap a return when there is no return mode\")\n\t}\n\n\tif m.state.isResultPointer(m.ReturnType) {\n\t\treturn respVar\n\t}\n\treturn \"&\" + respVar\n}\n\n// ReturnWith takes the result name and the error name, and generates the return expression.\nfunc (m *Method) ReturnWith(respName string, errName string) string {\n\tif !m.HasReturn() {\n\t\treturn errName\n\t}\n\treturn fmt.Sprintf(\"%v, %v\", respName, errName)\n}\n\n// Field is a wrapper for parser.Field.\ntype Field struct {\n\t*parser.Field\n\n\tstate *State\n}\n\n// Declaration returns the declaration for this field.\nfunc (a *Field) Declaration() string {\n\treturn fmt.Sprintf(\"%s %s\", a.Name(), a.ArgType())\n}\n\n// Name returns the field name.\nfunc (a *Field) Name() string {\n\treturn goName(a.Field.Name)\n}\n\n// ArgType returns the Go type for the given field.\nfunc (a *Field) ArgType() string {\n\treturn a.state.goType(a.Type)\n}\n\n// ArgStructName returns the name of this field in the Args struct generated by thrift.\nfunc (a *Field) ArgStructName() string {\n\treturn goPublicFieldName(a.Field.Name)\n}\n"
  },
  {
    "path": "thrift/thrift_bench_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift_test\n\nimport (\n\t\"flag\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/benchmark\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/atomic\"\n)\n\nconst callBatch = 100\n\nvar (\n\tuseHyperbahn   = flag.Bool(\"useHyperbahn\", false, \"Whether to advertise and route requests through Hyperbahn\")\n\thyperbahnNodes = flag.String(\"hyperbahn-nodes\", \"127.0.0.1:21300,127.0.0.1:21301\", \"Comma-separated list of Hyperbahn nodes\")\n\trequestSize    = flag.Int(\"request-size\", 4, \"Call payload size\")\n\ttimeout        = flag.Duration(\"call-timeout\", time.Second, \"Timeout for each call\")\n)\n\nfunc init() {\n\tbenchmark.BenchmarkDir = \"../benchmark/\"\n}\n\nfunc BenchmarkBothSerial(b *testing.B) {\n\tserver := benchmark.NewServer()\n\tclient := benchmark.NewClient(\n\t\t[]string{server.HostPort()},\n\t\tbenchmark.WithTimeout(*timeout),\n\t\tbenchmark.WithRequestSize(*requestSize),\n\t)\n\n\tb.ResetTimer()\n\n\tfor _, calls := range testutils.Batch(b.N, callBatch) {\n\t\tif _, err := client.ThriftCall(calls); err != nil {\n\t\t\tb.Errorf(\"Call failed: %v\", err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkInboundSerial(b *testing.B) {\n\tserver := benchmark.NewServer()\n\tclient := benchmark.NewClient(\n\t\t[]string{server.HostPort()},\n\t\tbenchmark.WithTimeout(*timeout),\n\t\tbenchmark.WithExternalProcess(),\n\t\tbenchmark.WithRequestSize(*requestSize),\n\t)\n\tdefer client.Close()\n\trequire.NoError(b, client.Warmup(), \"Warmup failed\")\n\n\tb.ResetTimer()\n\tfor _, calls := range testutils.Batch(b.N, callBatch) {\n\t\tif _, err := client.ThriftCall(calls); err != nil {\n\t\t\tb.Errorf(\"Call failed: %v\", err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkInboundParallel(b *testing.B) {\n\tserver := benchmark.NewServer()\n\n\tvar reqCounter atomic.Int32\n\tstarted := time.Now()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tclient := benchmark.NewClient(\n\t\t\t[]string{server.HostPort()},\n\t\t\tbenchmark.WithTimeout(*timeout),\n\t\t\tbenchmark.WithExternalProcess(),\n\t\t\tbenchmark.WithRequestSize(*requestSize),\n\t\t)\n\t\tdefer client.Close()\n\t\trequire.NoError(b, client.Warmup(), \"Warmup failed\")\n\n\t\tfor pb.Next() {\n\t\t\tif _, err := client.ThriftCall(100); err != nil {\n\t\t\t\tb.Errorf(\"Call failed: %v\", err)\n\t\t\t}\n\t\t\treqCounter.Add(100)\n\t\t}\n\t})\n\n\tduration := time.Since(started)\n\treqs := reqCounter.Load()\n\tb.Logf(\"Requests: %v   RPS: %v\", reqs, float64(reqs)/duration.Seconds())\n}\n"
  },
  {
    "path": "thrift/thrift_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"golang.org/x/net/context\"\n\n\t// Test is in a separate package to avoid circular dependencies.\n\t. \"github.com/uber/tchannel-go/thrift\"\n\n\ttchannel \"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\tgen \"github.com/uber/tchannel-go/thrift/gen-go/test\"\n\t\"github.com/uber/tchannel-go/thrift/mocks\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// Generate the service mocks using go generate.\n//go:generate mockery -dir ./gen-go/test -name TChanSimpleService\n//go:generate mockery -dir ./gen-go/test -name TChanSecondService\n\ntype testArgs struct {\n\tserver *Server\n\ts1     *mocks.TChanSimpleService\n\ts2     *mocks.TChanSecondService\n\tc1     gen.TChanSimpleService\n\tc2     gen.TChanSecondService\n\n\tserverCh *tchannel.Channel\n\tclientCh *tchannel.Channel\n}\n\nfunc ctxArg() mock.AnythingOfTypeArgument {\n\treturn mock.AnythingOfType(\"tchannel.headerCtx\")\n}\n\nfunc TestThriftArgs(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\targ := &gen.Data{\n\t\t\tB1: true,\n\t\t\tS2: \"str\",\n\t\t\tI3: 102,\n\t\t}\n\t\tret := &gen.Data{\n\t\t\tB1: false,\n\t\t\tS2: \"return-str\",\n\t\t\tI3: 105,\n\t\t}\n\n\t\targs.s1.On(\"Call\", ctxArg(), arg).Return(ret, nil)\n\t\tgot, err := args.c1.Call(ctx, arg)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, ret, got)\n\t})\n}\n\nfunc TestRequest(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\targs.s1.On(\"Simple\", ctxArg()).Return(nil)\n\t\trequire.NoError(t, args.c1.Simple(ctx))\n\t})\n}\n\nfunc TestRetryRequest(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\tcount := 0\n\t\targs.s1.On(\"Simple\", ctxArg()).Return(tchannel.ErrServerBusy).\n\t\t\tRun(func(args mock.Arguments) {\n\t\t\t\tcount++\n\t\t\t})\n\t\trequire.Error(t, args.c1.Simple(ctx), \"Simple expected to fail\")\n\t\tassert.Equal(t, 5, count, \"Expected Simple to be retried 5 times\")\n\t})\n}\n\nfunc TestRequestSubChannel(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\ttchan := testutils.NewServer(t, testutils.NewOpts().SetServiceName(\"svc1\"))\n\tdefer tchan.Close()\n\n\tclientCh := testutils.NewClient(t, nil)\n\tdefer clientCh.Close()\n\tclientCh.Peers().Add(tchan.PeerInfo().HostPort)\n\n\ttests := []tchannel.Registrar{tchan, tchan.GetSubChannel(\"svc2\"), tchan.GetSubChannel(\"svc3\")}\n\tfor _, ch := range tests {\n\t\tmockHandler := new(mocks.TChanSecondService)\n\t\tserver := NewServer(ch)\n\t\tserver.Register(gen.NewTChanSecondServiceServer(mockHandler))\n\n\t\tclient := NewClient(clientCh, ch.ServiceName(), nil)\n\t\tsecondClient := gen.NewTChanSecondServiceClient(client)\n\n\t\techoArg := ch.ServiceName()\n\t\techoRes := echoArg + \"-echo\"\n\t\tmockHandler.On(\"Echo\", ctxArg(), echoArg).Return(echoRes, nil)\n\t\tres, err := secondClient.Echo(ctx, echoArg)\n\t\tassert.NoError(t, err, \"Echo failed\")\n\t\tassert.Equal(t, echoRes, res)\n\t}\n}\n\nfunc TestLargeRequest(t *testing.T) {\n\targ := testutils.RandString(100000)\n\tres := strings.ToLower(arg)\n\n\tfmt.Println(len(arg))\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\targs.s2.On(\"Echo\", ctxArg(), arg).Return(res, nil)\n\n\t\tgot, err := args.c2.Echo(ctx, arg)\n\t\tif assert.NoError(t, err, \"Echo got error\") {\n\t\t\tassert.Equal(t, res, got, \"Echo got unexpected response\")\n\t\t}\n\t})\n}\n\nfunc TestThriftError(t *testing.T) {\n\tthriftErr := &gen.SimpleErr{\n\t\tMessage: \"this is the error\",\n\t}\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\targs.s1.On(\"Simple\", ctxArg()).Return(thriftErr)\n\t\tgot := args.c1.Simple(ctx)\n\t\trequire.Error(t, got)\n\t\trequire.Equal(t, thriftErr, got)\n\t})\n}\n\nfunc TestThriftUnknownError(t *testing.T) {\n\tthriftErr := &gen.NewErr_{\n\t\tMessage: \"new error\",\n\t}\n\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\t// When \"Simple\" is called, actually call a separate similar looking method\n\t\t// SimpleFuture which has a new exception that the client side of Simple\n\t\t// does not know how to handle.\n\t\targs.s1.On(\"SimpleFuture\", ctxArg()).Return(thriftErr)\n\t\ttClient := NewClient(args.clientCh, args.serverCh.ServiceName(), nil)\n\t\trewriteMethodClient := rewriteMethodClient{tClient, \"SimpleFuture\"}\n\t\tsimpleClient := gen.NewTChanSimpleServiceClient(rewriteMethodClient)\n\n\t\tgot := simpleClient.Simple(ctx)\n\t\trequire.Error(t, got)\n\t\tassert.Contains(t, got.Error(), \"no result or unknown exception\")\n\t})\n}\n\nfunc TestThriftNilErr(t *testing.T) {\n\tvar thriftErr *gen.SimpleErr\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\targs.s1.On(\"Simple\", ctxArg()).Return(thriftErr)\n\t\tgot := args.c1.Simple(ctx)\n\t\trequire.Error(t, got)\n\t\trequire.Contains(t, got.Error(), \"non-nil error type\")\n\t\trequire.Contains(t, got.Error(), \"nil value\")\n\t})\n}\n\nfunc TestThriftDecodeEmptyFrameServer(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\targs.s1.On(\"Simple\", ctxArg()).Return(nil)\n\n\t\tcall, err := args.clientCh.BeginCall(ctx, args.serverCh.PeerInfo().HostPort, args.serverCh.ServiceName(), \"SimpleService::Simple\", nil)\n\t\trequire.NoError(t, err, \"Failed to BeginCall\")\n\n\t\twithWriter(t, call.Arg2Writer, func(w tchannel.ArgWriter) error {\n\t\t\tif err := WriteHeaders(w, nil); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\treturn w.Flush()\n\t\t})\n\n\t\twithWriter(t, call.Arg3Writer, func(w tchannel.ArgWriter) error {\n\t\t\tif err := WriteStruct(w, &gen.SimpleServiceSimpleArgs{}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\treturn w.Flush()\n\t\t})\n\n\t\tresponse := call.Response()\n\t\twithReader(t, response.Arg2Reader, func(r tchannel.ArgReader) error {\n\t\t\t_, err := ReadHeaders(r)\n\t\t\treturn err\n\t\t})\n\n\t\tvar res gen.SimpleServiceSimpleResult\n\t\twithReader(t, response.Arg3Reader, func(r tchannel.ArgReader) error {\n\t\t\treturn ReadStruct(r, &res)\n\t\t})\n\n\t\tassert.False(t, res.IsSetSimpleErr(), \"Expected no error\")\n\t})\n}\n\nfunc TestThriftDecodeEmptyFrameClient(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\thandler := func(ctx context.Context, call *tchannel.InboundCall) {\n\t\t\twithReader(t, call.Arg2Reader, func(r tchannel.ArgReader) error {\n\t\t\t\t_, err := ReadHeaders(r)\n\t\t\t\treturn err\n\t\t\t})\n\n\t\t\twithReader(t, call.Arg3Reader, func(r tchannel.ArgReader) error {\n\t\t\t\treq := &gen.SimpleServiceSimpleArgs{}\n\t\t\t\treturn ReadStruct(r, req)\n\t\t\t})\n\n\t\t\tresponse := call.Response()\n\t\t\twithWriter(t, response.Arg2Writer, func(w tchannel.ArgWriter) error {\n\t\t\t\tif err := WriteHeaders(w, nil); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\treturn w.Flush()\n\t\t\t})\n\n\t\t\twithWriter(t, response.Arg3Writer, func(w tchannel.ArgWriter) error {\n\t\t\t\tif err := WriteStruct(w, &gen.SimpleServiceSimpleResult{}); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\treturn w.Flush()\n\t\t\t})\n\t\t}\n\n\t\targs.serverCh.Register(tchannel.HandlerFunc(handler), \"SimpleService::Simple\")\n\t\trequire.NoError(t, args.c1.Simple(ctx))\n\t})\n}\n\nfunc TestUnknownError(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\targs.s1.On(\"Simple\", ctxArg()).Return(errors.New(\"unexpected err\"))\n\t\tgot := args.c1.Simple(ctx)\n\t\trequire.Error(t, got)\n\t\trequire.Equal(t, tchannel.NewSystemError(tchannel.ErrCodeUnexpected, \"unexpected err\"), got)\n\t})\n}\n\nfunc TestMultiple(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\targs.s1.On(\"Simple\", ctxArg()).Return(nil)\n\t\targs.s2.On(\"Echo\", ctxArg(), \"test1\").Return(\"test2\", nil)\n\n\t\trequire.NoError(t, args.c1.Simple(ctx))\n\t\tres, err := args.c2.Echo(ctx, \"test1\")\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"test2\", res)\n\t})\n}\n\nfunc TestHeaders(t *testing.T) {\n\treqHeaders := map[string]string{\"header1\": \"value1\", \"header2\": \"value2\"}\n\trespHeaders := map[string]string{\"resp1\": \"value1-resp\", \"resp2\": \"value2-resp\"}\n\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\targs.s1.On(\"Simple\", ctxArg()).Return(nil).Run(func(args mock.Arguments) {\n\t\t\tctx := args.Get(0).(Context)\n\t\t\tassert.Equal(t, reqHeaders, ctx.Headers(), \"request headers mismatch\")\n\t\t\tctx.SetResponseHeaders(respHeaders)\n\t\t})\n\n\t\tctx = WithHeaders(ctx, reqHeaders)\n\t\trequire.NoError(t, args.c1.Simple(ctx))\n\t\tassert.Equal(t, respHeaders, ctx.ResponseHeaders(), \"response headers mismatch\")\n\t})\n}\n\nfunc TestClientHostPort(t *testing.T) {\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\ts1ch := testutils.NewServer(t, nil)\n\ts2ch := testutils.NewServer(t, nil)\n\tdefer s1ch.Close()\n\tdefer s2ch.Close()\n\n\ts1ch.Peers().Add(s2ch.PeerInfo().HostPort)\n\ts2ch.Peers().Add(s1ch.PeerInfo().HostPort)\n\n\tmock1, mock2 := new(mocks.TChanSecondService), new(mocks.TChanSecondService)\n\tNewServer(s1ch).Register(gen.NewTChanSecondServiceServer(mock1))\n\tNewServer(s2ch).Register(gen.NewTChanSecondServiceServer(mock2))\n\n\t// When we call using a normal client, it can only call the other server (only peer).\n\tc1 := gen.NewTChanSecondServiceClient(NewClient(s1ch, s2ch.PeerInfo().ServiceName, nil))\n\tmock2.On(\"Echo\", ctxArg(), \"call1\").Return(\"call1\", nil)\n\tres, err := c1.Echo(ctx, \"call1\")\n\tassert.NoError(t, err, \"call1 failed\")\n\tassert.Equal(t, \"call1\", res)\n\n\t// When we call using a client that specifies host:port, it should call that server.\n\tc2 := gen.NewTChanSecondServiceClient(NewClient(s1ch, s1ch.PeerInfo().ServiceName, &ClientOptions{\n\t\tHostPort: s1ch.PeerInfo().HostPort,\n\t}))\n\tmock1.On(\"Echo\", ctxArg(), \"call2\").Return(\"call2\", nil)\n\tres, err = c2.Echo(ctx, \"call2\")\n\tassert.NoError(t, err, \"call2 failed\")\n\tassert.Equal(t, \"call2\", res)\n}\n\nfunc TestRegisterPostResponseCB(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\tvar createdCtx Context\n\t\tctxKey := \"key\"\n\t\tctxValue := \"value\"\n\n\t\targs.server.SetContextFn(func(ctx context.Context, method string, headers map[string]string) Context {\n\t\t\tcreatedCtx = WithHeaders(context.WithValue(ctx, ctxKey, ctxValue), headers)\n\t\t\treturn createdCtx\n\t\t})\n\n\t\targ := &gen.Data{\n\t\t\tB1: true,\n\t\t\tS2: \"str\",\n\t\t\tI3: 102,\n\t\t}\n\t\tret := &gen.Data{\n\t\t\tB1: false,\n\t\t\tS2: \"return-str\",\n\t\t\tI3: 105,\n\t\t}\n\n\t\tcalled := make(chan struct{})\n\t\tcb := func(reqCtx context.Context, method string, response thrift.TStruct) {\n\t\t\tassert.Equal(t, \"Call\", method)\n\t\t\tassert.Equal(t, createdCtx, reqCtx)\n\t\t\tassert.Equal(t, ctxValue, reqCtx.Value(ctxKey))\n\t\t\tres, ok := response.(*gen.SimpleServiceCallResult)\n\t\t\tif assert.True(t, ok, \"response type should be Result struct\") {\n\t\t\t\tassert.Equal(t, ret, res.GetSuccess(), \"result should be returned value\")\n\t\t\t}\n\t\t\tclose(called)\n\t\t}\n\t\targs.server.Register(gen.NewTChanSimpleServiceServer(args.s1), OptPostResponse(cb))\n\n\t\targs.s1.On(\"Call\", ctxArg(), arg).Return(ret, nil)\n\t\tres, err := args.c1.Call(ctx, arg)\n\t\trequire.NoError(t, err, \"Call failed\")\n\t\tassert.Equal(t, res, ret, \"Call return value wrong\")\n\t\tselect {\n\t\tcase <-time.After(time.Second):\n\t\t\tt.Errorf(\"post-response callback not called\")\n\t\tcase <-called:\n\t\t}\n\t})\n}\n\nfunc TestRegisterPostResponseCBCalledOnError(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\tvar createdCtx Context\n\t\tctxKey := \"key\"\n\t\tctxValue := \"value\"\n\n\t\targs.server.SetContextFn(func(ctx context.Context, method string, headers map[string]string) Context {\n\t\t\tcreatedCtx = WithHeaders(context.WithValue(ctx, ctxKey, ctxValue), headers)\n\t\t\treturn createdCtx\n\t\t})\n\n\t\targ := &gen.Data{\n\t\t\tB1: true,\n\t\t\tS2: \"str\",\n\t\t\tI3: 102,\n\t\t}\n\n\t\tretErr := thrift.NewTProtocolException(fmt.Errorf(\"expected error\"))\n\n\t\tcalled := make(chan struct{})\n\t\tcb := func(reqCtx context.Context, method string, response thrift.TStruct) {\n\t\t\tassert.Equal(t, \"Call\", method)\n\t\t\tassert.Equal(t, createdCtx, reqCtx)\n\t\t\tassert.Equal(t, ctxValue, reqCtx.Value(ctxKey))\n\t\t\tassert.Nil(t, response)\n\t\t\tclose(called)\n\t\t}\n\t\targs.server.Register(gen.NewTChanSimpleServiceServer(args.s1), OptPostResponse(cb))\n\n\t\targs.s1.On(\"Call\", ctxArg(), arg).Return(nil, retErr)\n\t\tres, err := args.c1.Call(ctx, arg)\n\t\trequire.Error(t, err, \"Call succeeded instead of failed\")\n\t\trequire.Nil(t, res, \"Call returned value and an error\")\n\t\tsysErr, ok := err.(tchannel.SystemError)\n\t\trequire.True(t, ok, \"Call return error not a system error\")\n\t\tassert.Equal(t, tchannel.ErrCodeBadRequest, sysErr.Code(), \"Call return error value wrong\")\n\t\tassert.Equal(t, retErr.Error(), sysErr.Message(), \"Call return error value wrong\")\n\t\tselect {\n\t\tcase <-time.After(time.Second):\n\t\t\tt.Errorf(\"post-response callback not called\")\n\t\tcase <-called:\n\t\t}\n\t})\n}\n\nfunc TestThriftTimeout(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\thandler := make(chan struct{})\n\n\t\targs.s2.On(\"Echo\", ctxArg(), \"asd\").Return(\"asd\", nil).Run(func(args mock.Arguments) {\n\t\t\ttime.Sleep(testutils.Timeout(150 * time.Millisecond))\n\t\t\tclose(handler)\n\t\t})\n\n\t\tctx, cancel := NewContext(testutils.Timeout(100 * time.Millisecond))\n\t\tdefer cancel()\n\n\t\t_, err := args.c2.Echo(ctx, \"asd\")\n\t\tassert.Equal(t, err, tchannel.ErrTimeout, \"Expect call to time out\")\n\n\t\t// Wait for the handler to return, otherwise the test ends before the Server gets an error.\n\t\tselect {\n\t\tcase <-handler:\n\t\tcase <-time.After(time.Second):\n\t\t\tt.Errorf(\"Echo handler did not run\")\n\t\t}\n\t})\n}\n\nfunc TestThriftContextFn(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\targs.server.SetContextFn(func(ctx context.Context, method string, headers map[string]string) Context {\n\t\t\treturn WithHeaders(ctx, map[string]string{\"custom\": \"headers\"})\n\t\t})\n\n\t\targs.s2.On(\"Echo\", ctxArg(), \"test\").Return(\"test\", nil).Run(func(args mock.Arguments) {\n\t\t\tctx := args.Get(0).(Context)\n\t\t\tassert.Equal(t, \"headers\", ctx.Headers()[\"custom\"], \"Custom header is missing\")\n\t\t})\n\t\t_, err := args.c2.Echo(ctx, \"test\")\n\t\tassert.NoError(t, err, \"Echo failed\")\n\t})\n}\n\nfunc TestThriftMetaHealthNoArgs(t *testing.T) {\n\twithSetup(t, func(ctx Context, args testArgs) {\n\t\tc := gen.NewTChanMetaClient(NewClient(args.clientCh, args.serverCh.ServiceName(), nil /* options */))\n\t\tres, err := c.Health(ctx)\n\t\trequire.NoError(t, err)\n\t\tassert.True(t, res.Ok, \"Health without args failed\")\n\t})\n}\n\nfunc withSetup(t *testing.T, f func(ctx Context, args testArgs)) {\n\targs := testArgs{\n\t\ts1: new(mocks.TChanSimpleService),\n\t\ts2: new(mocks.TChanSecondService),\n\t}\n\n\tctx, cancel := NewContext(time.Second)\n\tdefer cancel()\n\n\t// Start server\n\targs.serverCh, args.server = setupServer(t, args.s1, args.s2)\n\tdefer args.serverCh.Close()\n\n\targs.clientCh, args.c1, args.c2 = getClients(t, args.serverCh.PeerInfo(), args.serverCh.ServiceName(), args.clientCh)\n\n\tf(ctx, args)\n\n\targs.s1.AssertExpectations(t)\n\targs.s2.AssertExpectations(t)\n}\n\nfunc setupServer(t *testing.T, h *mocks.TChanSimpleService, sh *mocks.TChanSecondService) (*tchannel.Channel, *Server) {\n\tch := testutils.NewServer(t, nil)\n\tserver := NewServer(ch)\n\tserver.Register(gen.NewTChanSimpleServiceServer(h))\n\tserver.Register(gen.NewTChanSecondServiceServer(sh))\n\treturn ch, server\n}\n\nfunc getClients(t *testing.T, serverInfo tchannel.LocalPeerInfo, svcName string, clientCh *tchannel.Channel) (*tchannel.Channel, gen.TChanSimpleService, gen.TChanSecondService) {\n\tch := testutils.NewClient(t, nil)\n\n\tch.Peers().Add(serverInfo.HostPort)\n\tclient := NewClient(ch, svcName, nil)\n\n\tsimpleClient := gen.NewTChanSimpleServiceClient(client)\n\tsecondClient := gen.NewTChanSecondServiceClient(client)\n\treturn ch, simpleClient, secondClient\n}\n\nfunc withReader(t *testing.T, readerFn func() (tchannel.ArgReader, error), f func(r tchannel.ArgReader) error) {\n\treader, err := readerFn()\n\trequire.NoError(t, err, \"Failed to get reader\")\n\n\terr = f(reader)\n\trequire.NoError(t, err, \"Failed to read contents\")\n\n\trequire.NoError(t, reader.Close(), \"Failed to close reader\")\n}\n\nfunc withWriter(t *testing.T, writerFn func() (tchannel.ArgWriter, error), f func(w tchannel.ArgWriter) error) {\n\twriter, err := writerFn()\n\trequire.NoError(t, err, \"Failed to get writer\")\n\n\tf(writer)\n\trequire.NoError(t, err, \"Failed to write contents\")\n\n\trequire.NoError(t, writer.Close(), \"Failed to close Writer\")\n}\n\ntype rewriteMethodClient struct {\n\tclient    TChanClient\n\trewriteTo string\n}\n\nfunc (c rewriteMethodClient) Call(ctx Context, serviceName, methodName string, req, resp thrift.TStruct) (success bool, err error) {\n\treturn c.client.Call(ctx, serviceName, c.rewriteTo, req, resp)\n}\n"
  },
  {
    "path": "thrift/tracing_test.go",
    "content": "package thrift_test\n\nimport (\n\tjson_encoding \"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/uber/tchannel-go\"\n\t. \"github.com/uber/tchannel-go/testutils/testtracing\"\n\t\"github.com/uber/tchannel-go/thrift\"\n\tgen \"github.com/uber/tchannel-go/thrift/gen-go/test\"\n\n\t\"golang.org/x/net/context\"\n)\n\n// ThriftHandler tests tracing over Thrift encoding\ntype ThriftHandler struct {\n\tgen.TChanSimpleService // leave nil so calls to unimplemented methods panic.\n\tTraceHandler\n\n\tthriftClient gen.TChanSimpleService\n\tt            *testing.T\n}\n\nfunc requestFromThrift(req *gen.Data) *TracingRequest {\n\tr := new(TracingRequest)\n\tr.ForwardCount = int(req.I3)\n\treturn r\n}\n\nfunc requestToThrift(r *TracingRequest) *gen.Data {\n\treturn &gen.Data{I3: int32(r.ForwardCount)}\n}\n\nfunc responseFromThrift(t *testing.T, res *gen.Data) (*TracingResponse, error) {\n\tvar r TracingResponse\n\tif err := json_encoding.Unmarshal([]byte(res.S2), &r); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &r, nil\n}\n\nfunc responseToThrift(t *testing.T, r *TracingResponse) (*gen.Data, error) {\n\tjsonBytes, err := json_encoding.Marshal(r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &gen.Data{S2: string(jsonBytes)}, nil\n}\n\nfunc (h *ThriftHandler) Call(ctx thrift.Context, arg *gen.Data) (*gen.Data, error) {\n\treq := requestFromThrift(arg)\n\tres, err := h.HandleCall(ctx, req,\n\t\tfunc(ctx context.Context, req *TracingRequest) (*TracingResponse, error) {\n\t\t\ttctx := ctx.(thrift.Context)\n\t\t\tres, err := h.thriftClient.Call(tctx, requestToThrift(req))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn responseFromThrift(h.t, res)\n\t\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn responseToThrift(h.t, res)\n}\n\nfunc (h *ThriftHandler) firstCall(ctx context.Context, req *TracingRequest) (*TracingResponse, error) {\n\ttctx := thrift.Wrap(ctx)\n\tres, err := h.thriftClient.Call(tctx, requestToThrift(req))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn responseFromThrift(h.t, res)\n}\n\nfunc TestThriftTracingPropagation(t *testing.T) {\n\tsuite := &PropagationTestSuite{\n\t\tEncoding: EncodingInfo{Format: tchannel.Thrift, HeadersSupported: true},\n\t\tRegister: func(t *testing.T, ch *tchannel.Channel) TracingCall {\n\t\t\topts := &thrift.ClientOptions{HostPort: ch.PeerInfo().HostPort}\n\t\t\tthriftClient := thrift.NewClient(ch, ch.PeerInfo().ServiceName, opts)\n\t\t\thandler := &ThriftHandler{\n\t\t\t\tTraceHandler: TraceHandler{Ch: ch},\n\t\t\t\tt:            t,\n\t\t\t\tthriftClient: gen.NewTChanSimpleServiceClient(thriftClient),\n\t\t\t}\n\n\t\t\t// Register Thrift handler\n\t\t\tserver := thrift.NewServer(ch)\n\t\t\tserver.Register(gen.NewTChanSimpleServiceServer(handler))\n\n\t\t\treturn handler.firstCall\n\t\t},\n\t\tTestCases: map[TracerType][]PropagationTestCase{\n\t\t\tNoop: {\n\t\t\t\t{ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: \"\", ExpectedSpanCount: 0},\n\t\t\t\t{ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: \"\", ExpectedSpanCount: 0},\n\t\t\t},\n\t\t\tMock: {\n\t\t\t\t{ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 0},\n\t\t\t\t{ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 6},\n\t\t\t},\n\t\t\tJaeger: {\n\t\t\t\t{ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 0},\n\t\t\t\t{ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 6},\n\t\t\t},\n\t\t},\n\t}\n\tsuite.Run(t)\n}\n"
  },
  {
    "path": "thrift/transport.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"sync\"\n\n\t\"github.com/uber/tchannel-go/thirdparty/github.com/apache/thrift/lib/go/thrift\"\n)\n\n// readerWriterTransport is a transport that reads and writes from the underlying Reader/Writer.\ntype readWriterTransport struct {\n\tio.Writer\n\tio.Reader\n\treadBuf  [1]byte\n\twriteBuf [1]byte\n\tstrBuf   []byte\n}\n\nvar errNoBytesRead = errors.New(\"no bytes read\")\n\nfunc (t *readWriterTransport) Open() error {\n\treturn nil\n}\n\nfunc (t *readWriterTransport) Flush() error {\n\treturn nil\n}\n\nfunc (t *readWriterTransport) IsOpen() bool {\n\treturn true\n}\n\nfunc (t *readWriterTransport) Close() error {\n\treturn nil\n}\n\nfunc (t *readWriterTransport) ReadByte() (byte, error) {\n\tv := t.readBuf[0:1]\n\n\tvar n int\n\tvar err error\n\n\tfor {\n\t\tn, err = t.Read(v)\n\t\tif n > 0 || err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif err == io.EOF && n > 0 {\n\t\terr = nil\n\t}\n\treturn v[0], err\n}\n\nfunc (t *readWriterTransport) WriteByte(b byte) error {\n\tv := t.writeBuf[:1]\n\n\tv[0] = b\n\t_, err := t.Write(v)\n\treturn err\n}\n\nfunc (t *readWriterTransport) WriteString(s string) (int, error) {\n\t// TODO switch to io.StringWriter once we don't need to support < 1.12\n\ttype stringWriter interface{ WriteString(string) (int, error) }\n\n\tif sw, ok := t.Writer.(stringWriter); ok {\n\t\treturn sw.WriteString(s)\n\t}\n\n\t// This path frequently taken since thrift.TBinaryProtocol calls\n\t// WriteString a lot, but fragmentingWriter does not implement WriteString;\n\t// furthermore it is difficult to add a dual WriteString path to\n\t// fragmentingWriter, since hash checksumming does not accept strings.\n\t//\n\t// Without this, io.WriteString ends up allocating every time.\n\tb := append(t.strBuf[:0], s...)\n\tt.strBuf = b[:0]\n\treturn t.Writer.Write(b)\n}\n\n// RemainingBytes returns the max number of bytes (same as Thrift's StreamTransport) as we\n// do not know how many bytes we have left.\nfunc (t *readWriterTransport) RemainingBytes() uint64 {\n\tconst maxSize = ^uint64(0)\n\treturn maxSize\n}\n\nvar _ thrift.TRichTransport = &readWriterTransport{}\n\ntype thriftProtocol struct {\n\ttransport *readWriterTransport\n\tprotocol  *thrift.TBinaryProtocol\n}\n\nvar thriftProtocolPool = sync.Pool{\n\tNew: func() interface{} {\n\t\ttransport := &readWriterTransport{}\n\t\tprotocol := thrift.NewTBinaryProtocolTransport(transport)\n\t\treturn &thriftProtocol{transport, protocol}\n\t},\n}\n\nfunc getProtocolWriter(writer io.Writer) *thriftProtocol {\n\twp := thriftProtocolPool.Get().(*thriftProtocol)\n\twp.transport.Reader = nil\n\twp.transport.Writer = writer\n\treturn wp\n}\n\nfunc getProtocolReader(reader io.Reader) *thriftProtocol {\n\twp := thriftProtocolPool.Get().(*thriftProtocol)\n\twp.transport.Reader = reader\n\twp.transport.Writer = nil\n\treturn wp\n}\n"
  },
  {
    "path": "thrift/transport_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage thrift\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/uber/tchannel-go/testutils/testreader\"\n\t\"github.com/uber/tchannel-go/testutils/testwriter\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc writeByte(writer io.Writer, b byte) error {\n\tprotocol := getProtocolWriter(writer)\n\treturn protocol.transport.WriteByte(b)\n}\n\nfunc TestWriteByteSuccess(t *testing.T) {\n\twriter := &bytes.Buffer{}\n\tassert.NoError(t, writeByte(writer, 'a'), \"WriteByte failed\")\n\tassert.NoError(t, writeByte(writer, 'b'), \"WriteByte failed\")\n\tassert.NoError(t, writeByte(writer, 'c'), \"WriteByte failed\")\n\tassert.Equal(t, []byte(\"abc\"), writer.Bytes(), \"Written bytes mismatch\")\n}\n\nfunc TestWriteByteFailed(t *testing.T) {\n\tbuf := &bytes.Buffer{}\n\twriter := io.MultiWriter(testwriter.Limited(2), buf)\n\tassert.NoError(t, writeByte(writer, 'a'), \"WriteByte failed\")\n\tassert.NoError(t, writeByte(writer, 'b'), \"WriteByte failed\")\n\tassert.Error(t, writeByte(writer, 'c'), \"WriteByte should fail due to lack of space\")\n\tassert.Equal(t, []byte(\"ab\"), buf.Bytes(), \"Written bytes mismatch\")\n}\n\nfunc TestReadByte0Byte(t *testing.T) {\n\tchunkWriter, chunkReader := testreader.ChunkReader()\n\treader := getProtocolReader(chunkReader)\n\n\tchunkWriter <- []byte{}\n\tchunkWriter <- []byte{}\n\tchunkWriter <- []byte{}\n\tchunkWriter <- []byte(\"abc\")\n\tclose(chunkWriter)\n\n\tb, err := reader.transport.ReadByte()\n\tassert.NoError(t, err, \"ReadByte should ignore 0 byte reads\")\n\tassert.EqualValues(t, 'a', b)\n\n\tb, err = reader.transport.ReadByte()\n\tassert.NoError(t, err, \"ReadByte failed\")\n\tassert.EqualValues(t, 'b', b)\n\n\tb, err = reader.transport.ReadByte()\n\tassert.NoError(t, err, \"ReadByte failed\")\n\tassert.EqualValues(t, 'c', b)\n\n\tb, err = reader.transport.ReadByte()\n\tassert.Equal(t, io.EOF, err, \"ReadByte should EOF\")\n}\n"
  },
  {
    "path": "tnet/listener.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tnet\n\nimport (\n\t\"net\"\n\t\"sync\"\n)\n\n// Wrap returns a new Listener around the provided net.Listener.\n// The returned Listener has a guarantee that when Close returns, it will no longer\n// accept any new connections.\n// See: https://github.com/uber/tchannel-go/issues/141\nfunc Wrap(l net.Listener) net.Listener {\n\treturn &listener{Listener: l, cond: sync.NewCond(&sync.Mutex{})}\n}\n\n// listener wraps a net.Listener and ensures that once Listener.Close returns,\n// the underlying socket has been closed.\n//\n// The default Listener returns from Close before the underlying socket has been closed\n// if another goroutine has an active reference (e.g. is in Accept).\n// The following can happen:\n// Goroutine 1 is running Accept, and is blocked, waiting for epoll\n// Goroutine 2 calls Close. It sees an extra reference, and so cannot destroy\n// the socket, but instead decrements a reference, marks the connection as closed\n// and unblocks epoll.\n//\n// Goroutine 2 returns to the caller, makes a new connection.\n// The new connection is sent to the socket (since it hasn't been destroyed)\n// Goroutine 1 returns from epoll, and accepts the new connection.\n//\n// To avoid accepting connections after Close, we block Goroutine 2 from returning from Close\n// till Accept returns an error to the user.\ntype listener struct {\n\tnet.Listener\n\n\t// cond is used signal Close when there are no references to the listener.\n\tcond *sync.Cond\n\trefs int\n}\n\nfunc (s *listener) incRef() {\n\ts.cond.L.Lock()\n\ts.refs++\n\ts.cond.L.Unlock()\n}\n\nfunc (s *listener) decRef() {\n\ts.cond.L.Lock()\n\ts.refs--\n\tnewRefs := s.refs\n\ts.cond.L.Unlock()\n\tif newRefs == 0 {\n\t\ts.cond.Broadcast()\n\t}\n}\n\n// Accept waits for and returns the next connection to the listener.\nfunc (s *listener) Accept() (net.Conn, error) {\n\ts.incRef()\n\tdefer s.decRef()\n\treturn s.Listener.Accept()\n}\n\n// Close closes the listener.\n// Any blocked Accept operations will be unblocked and return errors.\nfunc (s *listener) Close() error {\n\tif err := s.Listener.Close(); err != nil {\n\t\treturn err\n\t}\n\n\ts.cond.L.Lock()\n\tfor s.refs > 0 {\n\t\ts.cond.Wait()\n\t}\n\ts.cond.L.Unlock()\n\treturn nil\n}\n"
  },
  {
    "path": "tnet/listener_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tnet\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestListenerAcceptAfterClose(t *testing.T) {\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < 16; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tfor i := 0; i < 10; i++ {\n\t\t\t\trunTest(t)\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n}\n\nfunc runTest(t *testing.T) {\n\tconst connectionsBeforeClose = 1\n\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif !assert.NoError(t, err, \"Listen failed\") {\n\t\treturn\n\n\t}\n\tln = Wrap(ln)\n\n\taddr := ln.Addr().String()\n\twaitForListener := make(chan error)\n\tgo func() {\n\t\tdefer close(waitForListener)\n\n\t\tvar connCount int\n\t\tfor {\n\t\t\tconn, err := ln.Accept()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconnCount++\n\t\t\tif connCount > connectionsBeforeClose {\n\t\t\t\twaitForListener <- errors.New(\"got unexpected conn\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconn.Close()\n\t\t}\n\t}()\n\n\tfor i := 0; i < connectionsBeforeClose; i++ {\n\t\terr := connect(addr)\n\t\tif !assert.NoError(t, err, \"connect before listener is closed should succeed\") {\n\t\t\treturn\n\t\t}\n\t}\n\n\tln.Close()\n\tconnect(addr)\n\n\terr = <-waitForListener\n\tassert.NoError(t, err, \"got connection after listener was closed\")\n}\n\nfunc connect(addr string) error {\n\tconn, err := net.Dial(\"tcp\", addr)\n\tif err == nil {\n\t\tconn.Close()\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "tos/tos.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tos\n\n// ToS represents a const value DF, CS3 etc\n// Assured Forwarding (x=class, y=drop precedence) (RFC2597)\n// Class Selector (RFC 2474)\n// IP Precedence (Linux Socket Compat RFC 791\ntype ToS uint8\n\n// Assured Forwarding (x=class, y=drop precedence) (RFC2597)\n// Class Selector (RFC 2474)\n\nconst (\n\t// CS3 Class Selector 3\n\tCS3 ToS = 0x18\n\t// CS4 Class Selector 4\n\tCS4 ToS = 0x20\n\t// CS5 Class Selector 5\n\tCS5 ToS = 0x28\n\t// CS6 Class Selector 6\n\tCS6 ToS = 0x30\n\t// CS7 Class Selector 7\n\tCS7 ToS = 0x38\n\t// AF11 Assured Forward 11\n\tAF11 ToS = 0x0a\n\t// AF12 Assured Forward 11\n\tAF12 ToS = 0x0c\n\t// AF13 Assured Forward 12\n\tAF13 ToS = 0x0e\n\t// AF21 Assured Forward 13\n\tAF21 ToS = 0x12\n\t// AF22 Assured Forward 21\n\tAF22 ToS = 0x14\n\t// AF23 Assured Forward 22\n\tAF23 ToS = 0x16\n\t// AF31 Assured Forward 23\n\tAF31 ToS = 0x1a\n\t// AF32 Assured Forward 31\n\tAF32 ToS = 0x1c\n\t// AF33 Assured Forward 32\n\tAF33 ToS = 0x1e\n\t// AF41 Assured Forward 33\n\tAF41 ToS = 0x22\n\t// AF42 Assured Forward 41\n\tAF42 ToS = 0x24\n\t// AF43 Assured Forward 42\n\tAF43 ToS = 0x26\n\t// EF Expedited Forwarding (RFC 3246)\n\tEF ToS = 0x2e\n\t// Lowdelay 10\n\tLowdelay ToS = 0x10\n\t// Throughput 8\n\tThroughput ToS = 0x08\n\t// Reliability  4\n\tReliability ToS = 0x04\n\t// Lowcost 2\n\tLowcost ToS = 0x02\n)\n"
  },
  {
    "path": "tos/tos_string.go",
    "content": "package tos\n\nimport \"fmt\"\n\nvar (\n\t_tosNameToValue map[string]ToS\n\t_tosValueToName = map[ToS]string{\n\t\tCS3:         \"CS3\",\n\t\tCS4:         \"CS4\",\n\t\tCS5:         \"CS5\",\n\t\tCS6:         \"CS6\",\n\t\tCS7:         \"CS7\",\n\t\tAF11:        \"AF11\",\n\t\tAF12:        \"AF12\",\n\t\tAF13:        \"AF13\",\n\t\tAF21:        \"AF21\",\n\t\tAF22:        \"AF22\",\n\t\tAF23:        \"AF23\",\n\t\tAF31:        \"AF31\",\n\t\tAF32:        \"AF32\",\n\t\tAF33:        \"AF33\",\n\t\tAF41:        \"AF41\",\n\t\tAF42:        \"AF42\",\n\t\tAF43:        \"AF43\",\n\t\tEF:          \"EF\",\n\t\tLowdelay:    \"Lowdelay\",\n\t\tThroughput:  \"Throughput\",\n\t\tReliability: \"Reliability\",\n\t\tLowcost:     \"Lowcost\",\n\t}\n)\n\nfunc init() {\n\t_tosNameToValue = make(map[string]ToS, len(_tosValueToName))\n\tfor tos, tosString := range _tosValueToName {\n\t\t_tosNameToValue[tosString] = tos\n\t}\n}\n\n// MarshalText implements TextMarshaler from encoding\nfunc (r ToS) MarshalText() ([]byte, error) {\n\treturn []byte(_tosValueToName[r]), nil\n}\n\n// UnmarshalText implements TextUnMarshaler from encoding\nfunc (r *ToS) UnmarshalText(data []byte) error {\n\tif v, ok := _tosNameToValue[string(data)]; ok {\n\t\t*r = v\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"invalid ToS %q\", string(data))\n}\n"
  },
  {
    "path": "tos/tos_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tos\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMarshal(t *testing.T) {\n\tfor tos, wantMarshalled := range _tosValueToName {\n\t\tmarshalled, err := tos.MarshalText()\n\t\trequire.NoError(t, err, \"Failed to marshal %v\", tos)\n\t\tassert.Equal(t, wantMarshalled, string(marshalled))\n\n\t\tvar got ToS\n\t\terr = got.UnmarshalText(marshalled)\n\t\trequire.NoError(t, err, \"Failed to unmarshal %v\", string(marshalled))\n\t\tassert.Equal(t, tos, got)\n\t}\n}\n\nfunc TestUnmarshalUnknown(t *testing.T) {\n\tvar tos ToS\n\terr := tos.UnmarshalText([]byte(\"unknown\"))\n\trequire.Error(t, err, \"Should fail to unmarshal unknown value\")\n}\n"
  },
  {
    "path": "trace/doc.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n/*\nPackage trace used to contain TChannel's distributed tracing functionality.\nIt has since been replaced by integration with OpenTracing API.\n\nSee http://opentracing.io\n\nThis package is kept to alleviate problems with `glide update`, which tries\nto look for it during the dependencies upgrades.\n*/\npackage trace\n"
  },
  {
    "path": "tracing.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/uber/tchannel-go/trand\"\n\t\"github.com/uber/tchannel-go/typed\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/ext\"\n\t\"golang.org/x/net/context\"\n)\n\n// zipkinSpanFormat defines a name for OpenTracing carrier format that tracer may support.\n// It is used to extract zipkin-style trace/span IDs from the OpenTracing Span, which are\n// otherwise not exposed explicitly.\n// NB: the string value is what's actually shared between implementations\nconst zipkinSpanFormat = \"zipkin-span-format\"\n\nconst componentName = \"tchannel-go\"\n\n// Span is an internal representation of Zipkin-compatible OpenTracing Span.\n// It is used as OpenTracing inject/extract Carrier with ZipkinSpanFormat.\ntype Span struct {\n\ttraceID  uint64\n\tparentID uint64\n\tspanID   uint64\n\tflags    byte\n}\n\nvar (\n\t// traceRng is a thread-safe random number generator for generating trace IDs.\n\ttraceRng = trand.NewSeeded()\n\n\t// emptySpan is returned from CurrentSpan(ctx) when there is no OpenTracing\n\t// Span in ctx, to avoid returning nil.\n\temptySpan Span\n)\n\nfunc (s Span) String() string {\n\treturn fmt.Sprintf(\"TraceID=%x,ParentID=%x,SpanID=%x\", s.traceID, s.parentID, s.spanID)\n}\n\nfunc (s *Span) read(r *typed.ReadBuffer) error {\n\ts.spanID = r.ReadUint64()\n\ts.parentID = r.ReadUint64()\n\ts.traceID = r.ReadUint64()\n\ts.flags = r.ReadSingleByte()\n\treturn r.Err()\n}\n\nfunc (s *Span) write(w *typed.WriteBuffer) error {\n\tw.WriteUint64(s.spanID)\n\tw.WriteUint64(s.parentID)\n\tw.WriteUint64(s.traceID)\n\tw.WriteSingleByte(s.flags)\n\treturn w.Err()\n}\n\nfunc (s *Span) initRandom() {\n\ts.traceID = uint64(traceRng.Int63())\n\ts.spanID = s.traceID\n\ts.parentID = 0\n}\n\n// TraceID returns the trace id for the entire call graph of requests. Established\n// at the outermost edge service and propagated through all calls\nfunc (s Span) TraceID() uint64 { return s.traceID }\n\n// ParentID returns the id of the parent span in this call graph\nfunc (s Span) ParentID() uint64 { return s.parentID }\n\n// SpanID returns the id of this specific RPC\nfunc (s Span) SpanID() uint64 { return s.spanID }\n\n// Flags returns flags bitmap. Interpretation of the bits is up to the tracing system.\nfunc (s Span) Flags() byte { return s.flags }\n\ntype injectableSpan Span\n\n// SetTraceID sets traceID\nfunc (s *injectableSpan) SetTraceID(traceID uint64) { s.traceID = traceID }\n\n// SetSpanID sets spanID\nfunc (s *injectableSpan) SetSpanID(spanID uint64) { s.spanID = spanID }\n\n// SetParentID sets parentID\nfunc (s *injectableSpan) SetParentID(parentID uint64) { s.parentID = parentID }\n\n// SetFlags sets flags\nfunc (s *injectableSpan) SetFlags(flags byte) { s.flags = flags }\n\n// initFromOpenTracing initializes injectableSpan fields from an OpenTracing Span,\n// assuming the tracing implementation supports Zipkin-style span IDs.\nfunc (s *injectableSpan) initFromOpenTracing(span opentracing.Span) error {\n\treturn span.Tracer().Inject(span.Context(), zipkinSpanFormat, s)\n}\n\n// CurrentSpan extracts OpenTracing Span from the Context, and if found tries to\n// extract zipkin-style trace/span IDs from it using ZipkinSpanFormat carrier.\n// If there is no OpenTracing Span in the Context, an empty span is returned.\nfunc CurrentSpan(ctx context.Context) *Span {\n\tif sp := opentracing.SpanFromContext(ctx); sp != nil {\n\t\tvar injectable injectableSpan\n\t\tif err := injectable.initFromOpenTracing(sp); err == nil {\n\t\t\tspan := Span(injectable)\n\t\t\treturn &span\n\t\t}\n\t\t// return empty span on error, instead of possibly a partially filled one\n\t}\n\treturn &emptySpan\n}\n\n// startOutboundSpan creates a new tracing span to represent the outbound RPC call.\n// If the context already contains a span, it will be used as a parent, otherwise\n// a new root span is created.\n//\n// If the tracer supports Zipkin-style trace IDs, then call.callReq.Tracing is\n// initialized with those IDs. Otherwise it is assigned random values.\nfunc (c *Connection) startOutboundSpan(ctx context.Context, serviceName, methodName string, call *OutboundCall, startTime time.Time) opentracing.Span {\n\tvar parent opentracing.SpanContext // ok to be nil\n\tif s := opentracing.SpanFromContext(ctx); s != nil {\n\t\tparent = s.Context()\n\t}\n\tspan := c.Tracer().StartSpan(\n\t\tmethodName,\n\t\topentracing.ChildOf(parent),\n\t\topentracing.StartTime(startTime),\n\t)\n\tif isTracingDisabled(ctx) {\n\t\text.SamplingPriority.Set(span, 0)\n\t}\n\text.SpanKindRPCClient.Set(span)\n\text.PeerService.Set(span, serviceName)\n\text.Component.Set(span, componentName)\n\tc.setPeerHostPort(span)\n\tspan.SetTag(\"as\", call.callReq.Headers[ArgScheme])\n\tvar injectable injectableSpan\n\tif err := injectable.initFromOpenTracing(span); err == nil {\n\t\tcall.callReq.Tracing = Span(injectable)\n\t} else {\n\t\tcall.callReq.Tracing.initRandom()\n\t}\n\treturn span\n}\n\n// InjectOutboundSpan retrieves OpenTracing Span from `response`, where it is stored\n// when the outbound call is initiated. The tracing API is used to serialize the span\n// into the application `headers`, which will propagate tracing context to the server.\n// Returns modified headers containing serialized tracing context.\n//\n// Sometimes caller pass a shared instance of the `headers` map, so instead of modifying\n// it we clone it into the new map (assuming that Tracer actually injects some tracing keys).\nfunc InjectOutboundSpan(response *OutboundCallResponse, headers map[string]string) map[string]string {\n\tspan := response.span\n\tif span == nil {\n\t\treturn headers\n\t}\n\tnewHeaders := make(map[string]string)\n\tcarrier := tracingHeadersCarrier(newHeaders)\n\tif err := span.Tracer().Inject(span.Context(), opentracing.TextMap, carrier); err != nil {\n\t\t// Something had to go seriously wrong for Inject to fail, usually a setup problem.\n\t\t// A good Tracer implementation may also emit a metric.\n\t\tresponse.log.WithFields(ErrField(err)).Error(\"Failed to inject tracing span.\")\n\t}\n\tif len(newHeaders) == 0 {\n\t\treturn headers // Tracer did not add any tracing headers, so return the original map\n\t}\n\tfor k, v := range headers {\n\t\t// Some applications propagate all inbound application headers to outbound calls (issue #682).\n\t\t// If those headers include tracing headers we want to make sure to keep the new tracing headers.\n\t\tif _, ok := newHeaders[k]; !ok {\n\t\t\tnewHeaders[k] = v\n\t\t}\n\t}\n\treturn newHeaders\n}\n\n// extractInboundSpan attempts to create a new OpenTracing Span for inbound request\n// using only trace IDs stored in the frame's tracing field. It only works if the\n// tracer understand Zipkin-style trace IDs. If such attempt fails, another attempt\n// will be made from the higher level function ExtractInboundSpan() once the\n// application headers are read from the wire.\nfunc (c *Connection) extractInboundSpan(callReq *callReq) opentracing.Span {\n\tspanCtx, err := c.Tracer().Extract(zipkinSpanFormat, &callReq.Tracing)\n\tif err != nil {\n\t\tif err != opentracing.ErrUnsupportedFormat && err != opentracing.ErrSpanContextNotFound {\n\t\t\tc.log.WithFields(ErrField(err)).Error(\"Failed to extract Zipkin-style span.\")\n\t\t}\n\t\treturn nil\n\t}\n\tif spanCtx == nil {\n\t\treturn nil\n\t}\n\toperationName := \"\" // not known at this point, will be set later\n\tspan := c.Tracer().StartSpan(operationName, ext.RPCServerOption(spanCtx))\n\tspan.SetTag(\"as\", callReq.Headers[ArgScheme])\n\text.PeerService.Set(span, callReq.Headers[CallerName])\n\text.Component.Set(span, componentName)\n\tc.setPeerHostPort(span)\n\treturn span\n}\n\n// ExtractInboundSpan is a higher level version of extractInboundSpan().\n// If the lower-level attempt to create a span from incoming request was\n// successful (e.g. when then Tracer supports Zipkin-style trace IDs),\n// then the application headers are only used to read the Baggage and add\n// it to the existing span. Otherwise, the standard OpenTracing API supported\n// by all tracers is used to deserialize the tracing context from the\n// application headers and start a new server-side span.\n// Once the span is started, it is wrapped in a new Context, which is returned.\nfunc ExtractInboundSpan(ctx context.Context, call *InboundCall, headers map[string]string, tracer opentracing.Tracer) context.Context {\n\tvar span = call.Response().span\n\tif span != nil {\n\t\tif headers != nil {\n\t\t\t// extract SpanContext from headers, but do not start another span with it,\n\t\t\t// just get the baggage and copy to the already created span\n\t\t\tcarrier := tracingHeadersCarrier(headers)\n\t\t\tif sc, err := tracer.Extract(opentracing.TextMap, carrier); err == nil {\n\t\t\t\tsc.ForeachBaggageItem(func(k, v string) bool {\n\t\t\t\t\tspan.SetBaggageItem(k, v)\n\t\t\t\t\treturn true\n\t\t\t\t})\n\t\t\t}\n\t\t\tcarrier.RemoveTracingKeys()\n\t\t}\n\t} else {\n\t\tvar parent opentracing.SpanContext\n\t\tif headers != nil {\n\t\t\tcarrier := tracingHeadersCarrier(headers)\n\t\t\tif p, err := tracer.Extract(opentracing.TextMap, carrier); err == nil {\n\t\t\t\tparent = p\n\t\t\t}\n\t\t\tcarrier.RemoveTracingKeys()\n\t\t}\n\t\tspan = tracer.StartSpan(call.MethodString(), ext.RPCServerOption(parent))\n\t\text.PeerService.Set(span, call.CallerName())\n\t\text.Component.Set(span, componentName)\n\t\tspan.SetTag(\"as\", string(call.Format()))\n\t\tcall.conn.setPeerHostPort(span)\n\t\tcall.Response().span = span\n\t}\n\treturn opentracing.ContextWithSpan(ctx, span)\n}\n\nfunc (c *Connection) setPeerHostPort(span opentracing.Span) {\n\tif c.remotePeerAddress.ipv4 != 0 {\n\t\text.PeerHostIPv4.Set(span, c.remotePeerAddress.ipv4)\n\t}\n\tif c.remotePeerAddress.ipv6 != \"\" {\n\t\text.PeerHostIPv6.Set(span, c.remotePeerAddress.ipv6)\n\t}\n\tif c.remotePeerAddress.hostname != \"\" {\n\t\text.PeerHostname.Set(span, c.remotePeerAddress.hostname)\n\t}\n\tif c.remotePeerAddress.port != 0 {\n\t\text.PeerPort.Set(span, c.remotePeerAddress.port)\n\t}\n}\n\ntype tracerProvider interface {\n\tTracer() opentracing.Tracer\n}\n\n// TracerFromRegistrar returns an OpenTracing Tracer embedded in the Registrar,\n// assuming that Registrar has a Tracer() method. Otherwise it returns default Global Tracer.\nfunc TracerFromRegistrar(registrar Registrar) opentracing.Tracer {\n\tif tracerProvider, ok := registrar.(tracerProvider); ok {\n\t\treturn tracerProvider.Tracer()\n\t}\n\treturn opentracing.GlobalTracer()\n}\n"
  },
  {
    "path": "tracing_internal_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/uber/tchannel-go/typed\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/ext\"\n\t\"github.com/opentracing/opentracing-go/mocktracer\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\nfunc TestTracingSpanEncoding(t *testing.T) {\n\ts1 := Span{\n\t\ttraceID:  1,\n\t\tparentID: 2,\n\t\tspanID:   3,\n\t\tflags:    4,\n\t}\n\t// Encoding is: spanid:8 parentid:8 traceid:8 traceflags:1\n\t// http://tchannel.readthedocs.io/en/latest/protocol/#tracing\n\tencoded := []byte{\n\t\t0, 0, 0, 0, 0, 0, 0, 3, /* spanID */\n\t\t0, 0, 0, 0, 0, 0, 0, 2, /* parentID */\n\t\t0, 0, 0, 0, 0, 0, 0, 1, /* traceID */\n\t\t4, /* flags */\n\t}\n\n\tbuf := make([]byte, len(encoded))\n\twriter := typed.NewWriteBuffer(buf)\n\trequire.NoError(t, s1.write(writer), \"Failed to encode span\")\n\n\tassert.Equal(t, encoded, buf, \"Encoded span mismatch\")\n\n\tvar s2 Span\n\treader := typed.NewReadBuffer(buf)\n\trequire.NoError(t, s2.read(reader), \"Failed to decode span\")\n\n\tassert.Equal(t, s1, s2, \"Roundtrip of span failed\")\n}\n\nfunc TestTracingInjectorExtractor(t *testing.T) {\n\ttracer := mocktracer.New()\n\ttracer.RegisterInjector(zipkinSpanFormat, new(zipkinInjector))\n\ttracer.RegisterExtractor(zipkinSpanFormat, new(zipkinExtractor))\n\n\tsp := tracer.StartSpan(\"x\")\n\tvar injectable injectableSpan\n\terr := tracer.Inject(sp.Context(), zipkinSpanFormat, &injectable)\n\trequire.NoError(t, err)\n\n\ttsp := Span(injectable)\n\tassert.NotEqual(t, uint64(0), tsp.TraceID())\n\tassert.NotEqual(t, uint64(0), tsp.SpanID())\n\n\tsp2, err := tracer.Extract(zipkinSpanFormat, &tsp)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, sp2)\n}\n\nfunc TestSpanString(t *testing.T) {\n\tspan := Span{traceID: 15}\n\tassert.Equal(t, \"TraceID=f,ParentID=0,SpanID=0\", span.String())\n}\n\nfunc TestSetPeerHostPort(t *testing.T) {\n\ttracer := mocktracer.New()\n\n\tipv6 := []byte{1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 16}\n\tassert.Equal(t, net.IPv6len, len(ipv6))\n\tipv6hostPort := fmt.Sprintf(\"[%v]:789\", net.IP(ipv6))\n\n\ttests := []struct {\n\t\thostPort    string\n\t\twantHostTag string\n\t\twantHost    interface{}\n\t\twantPort    uint16\n\t}{\n\t\t{\"adhoc123:bad-port\", \"peer.hostname\", \"adhoc123\", 0},\n\t\t{\"adhoc123\", \"peer.hostname\", \"adhoc123\", 0},\n\t\t{\"ip123.uswest.aws.com:765\", \"peer.hostname\", \"ip123.uswest.aws.com\", 765},\n\t\t{\"localhost:123\", \"peer.ipv4\", uint32(127<<24 | 1), 123},\n\t\t{\"10.20.30.40:321\", \"peer.ipv4\", uint32(10<<24 | 20<<16 | 30<<8 | 40), 321},\n\t\t{ipv6hostPort, \"peer.ipv6\", \"102:300::f10\", 789},\n\t}\n\n\tfor i, test := range tests {\n\t\tspan := tracer.StartSpan(\"x\")\n\t\tpeerInfo, peerAddress, err := parseRemotePeer(initParams{\n\t\t\tInitParamHostPort:    test.hostPort,\n\t\t\tInitParamProcessName: \"test\",\n\t\t}, &net.IPAddr{IP: net.ParseIP(\"1.1.1.1\")})\n\t\trequire.NoError(t, err, \"Failed to parse remote peer info\")\n\n\t\tc := &Connection{\n\t\t\tchannelConnectionCommon: channelConnectionCommon{\n\t\t\t\tlog: NullLogger,\n\t\t\t},\n\t\t\tremotePeerInfo:    peerInfo,\n\t\t\tremotePeerAddress: peerAddress,\n\t\t}\n\t\tc.setPeerHostPort(span)\n\t\tspan.Finish()\n\t\trawSpan := tracer.FinishedSpans()[i]\n\t\tassert.Equal(t, test.wantHost, rawSpan.Tag(test.wantHostTag), \"test %+v\", test)\n\t\tif test.wantPort != 0 {\n\t\t\tassert.Equal(t, test.wantPort, rawSpan.Tag(string(ext.PeerPort)), \"test %+v\", test)\n\t\t} else {\n\t\t\tassert.Nil(t, rawSpan.Tag(string(ext.PeerPort)), \"test %+v\", test)\n\t\t}\n\t}\n}\n\nfunc TestExtractInboundSpanWithZipkinTracer(t *testing.T) {\n\ttracer := mocktracer.New()\n\tcallReq := new(callReq)\n\tcallReq.Tracing = Span{traceID: 1, spanID: 2, flags: 1}\n\tcallReq.Headers = transportHeaders{\n\t\tArgScheme:  string(JSON),\n\t\tCallerName: \"caller\",\n\t}\n\tpeerInfo, peerAddress, err := parseRemotePeer(initParams{\n\t\tInitParamHostPort:    \"host:123\",\n\t\tInitParamProcessName: \"test\",\n\t}, &net.IPAddr{IP: net.ParseIP(\"1.1.1.1\")})\n\trequire.NoError(t, err, \"Failed to parse remote peer info\")\n\tc := Connection{\n\t\tchannelConnectionCommon: channelConnectionCommon{\n\t\t\tlog:    NullLogger,\n\t\t\ttracer: tracer,\n\t\t},\n\t\tremotePeerInfo:    peerInfo,\n\t\tremotePeerAddress: peerAddress,\n\t}\n\n\t// fail to extract with zipkin format, as MockTracer does not support it\n\tassert.Nil(t, c.extractInboundSpan(callReq), \"zipkin format not available\")\n\n\t// add zipkin format extractor and try again\n\ttracer.RegisterExtractor(zipkinSpanFormat, new(zipkinExtractor))\n\tspan := c.extractInboundSpan(callReq)\n\trequire.NotNil(t, span, \"zipkin format available\")\n\n\t// validate the extracted span was correctly populated\n\ts1, ok := span.(*mocktracer.MockSpan)\n\trequire.True(t, ok)\n\tassert.Equal(t, 1, s1.SpanContext.TraceID)\n\tassert.Equal(t, 2, s1.ParentID)\n\tassert.True(t, s1.SpanContext.Sampled)\n\tassert.Equal(t, \"\", s1.OperationName, \"operation name unknown initially\")\n\tassert.Equal(t, string(JSON), s1.Tag(\"as\"))\n\tassert.Equal(t, \"caller\", s1.Tag(string(ext.PeerService)))\n\tassert.Equal(t, \"host\", s1.Tag(string(ext.PeerHostname)))\n\tassert.Equal(t, uint16(123), s1.Tag(string(ext.PeerPort)))\n\n\t// start a temporary span so that we can populate headers with baggage\n\ttempSpan := tracer.StartSpan(\"test\")\n\ttempSpan.SetBaggageItem(\"x\", \"y\")\n\theaders := make(map[string]string)\n\tcarrier := tracingHeadersCarrier(headers)\n\terr = tracer.Inject(tempSpan.Context(), opentracing.TextMap, carrier)\n\tassert.NoError(t, err)\n\n\t// run the public ExtractInboundSpan method with application headers\n\tinCall := &InboundCall{\n\t\tresponse: &InboundCallResponse{\n\t\t\tspan: span,\n\t\t},\n\t}\n\tctx := context.Background()\n\tctx2 := ExtractInboundSpan(ctx, inCall, headers, tracer)\n\tspan = opentracing.SpanFromContext(ctx2)\n\ts2, ok := span.(*mocktracer.MockSpan)\n\trequire.True(t, ok)\n\tassert.Equal(t, s1, s2, \"should be the same span started previously\")\n\tassert.Equal(t, \"y\", s2.BaggageItem(\"x\"), \"baggage should've been added\")\n}\n\ntype zipkinInjector struct{}\n\nfunc (z *zipkinInjector) Inject(sc mocktracer.MockSpanContext, carrier interface{}) error {\n\tspan, ok := carrier.(*injectableSpan)\n\tif !ok {\n\t\treturn opentracing.ErrInvalidCarrier\n\t}\n\tspan.SetTraceID(uint64(sc.TraceID))\n\tspan.SetSpanID(uint64(sc.SpanID))\n\tif sc.Sampled {\n\t\tspan.SetFlags(1)\n\t} else {\n\t\tspan.SetFlags(0)\n\t}\n\treturn nil\n}\n\ntype zipkinExtractor struct{}\n\nfunc (z *zipkinExtractor) Extract(carrier interface{}) (mocktracer.MockSpanContext, error) {\n\tspan, ok := carrier.(*Span)\n\tif !ok {\n\t\treturn mocktracer.MockSpanContext{}, opentracing.ErrInvalidCarrier\n\t}\n\treturn mocktracer.MockSpanContext{\n\t\tTraceID: int(span.traceID),\n\t\tSpanID:  int(span.spanID),\n\t\tSampled: span.flags&1 == 1,\n\t}, nil\n}\n"
  },
  {
    "path": "tracing_keys.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\nimport (\n\t\"strings\"\n\t\"sync\"\n)\n\n// tracingKeyPrefix is used to prefix all keys used by the OpenTracing Tracer to represent\n// its trace context and baggage. The prefixing is done in order to distinguish tracing\n// headers from the actual application headers and to hide the former from the user code.\nconst tracingKeyPrefix = \"$tracing$\"\n\n// tracingKeyMappingSize is the maximum number of tracing key mappings we cache.\nconst tracingKeyMappingSize = 100\n\ntype tracingKeysMapping struct {\n\tsync.RWMutex\n\tmapping map[string]string\n\tmapper  func(key string) string\n}\n\nvar tracingKeyEncoding = &tracingKeysMapping{\n\tmapping: make(map[string]string),\n\tmapper: func(key string) string {\n\t\treturn tracingKeyPrefix + key\n\t},\n}\n\nvar tracingKeyDecoding = &tracingKeysMapping{\n\tmapping: make(map[string]string),\n\tmapper: func(key string) string {\n\t\treturn key[len(tracingKeyPrefix):]\n\t},\n}\n\nfunc (m *tracingKeysMapping) mapAndCache(key string) string {\n\tm.RLock()\n\tv, ok := m.mapping[key]\n\tm.RUnlock()\n\tif ok {\n\t\treturn v\n\t}\n\tm.Lock()\n\tdefer m.Unlock()\n\tif v, ok := m.mapping[key]; ok {\n\t\treturn v\n\t}\n\tmappedKey := m.mapper(key)\n\tif len(m.mapping) < tracingKeyMappingSize {\n\t\tm.mapping[key] = mappedKey\n\t}\n\treturn mappedKey\n}\n\ntype tracingHeadersCarrier map[string]string\n\n// Set implements Set() of opentracing.TextMapWriter\nfunc (c tracingHeadersCarrier) Set(key, val string) {\n\tprefixedKey := tracingKeyEncoding.mapAndCache(key)\n\tc[prefixedKey] = val\n}\n\n// ForeachKey conforms to the TextMapReader interface.\nfunc (c tracingHeadersCarrier) ForeachKey(handler func(key, val string) error) error {\n\tfor k, v := range c {\n\t\tif !strings.HasPrefix(k, tracingKeyPrefix) {\n\t\t\tcontinue\n\t\t}\n\t\tnoPrefixKey := tracingKeyDecoding.mapAndCache(k)\n\t\tif err := handler(noPrefixKey, v); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c tracingHeadersCarrier) RemoveTracingKeys() {\n\tfor key := range c {\n\t\tif strings.HasPrefix(key, tracingKeyPrefix) {\n\t\t\tdelete(c, key)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tracing_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/uber/tchannel-go\"\n\t\"github.com/uber/tchannel-go/json\"\n\t\"github.com/uber/tchannel-go/testutils\"\n\t\"github.com/uber/tchannel-go/testutils/testtracing\"\n\t\"go.uber.org/atomic\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/ext\"\n\t\"github.com/opentracing/opentracing-go/mocktracer\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/context\"\n)\n\n// JSONHandler tests tracing over JSON encoding\ntype JSONHandler struct {\n\ttesttracing.TraceHandler\n\tt          *testing.T\n\tsideEffect func(ctx json.Context)\n}\n\nfunc (h *JSONHandler) callJSON(ctx json.Context, req *testtracing.TracingRequest) (*testtracing.TracingResponse, error) {\n\tresp := new(testtracing.TracingResponse)\n\tresp.ObserveSpan(ctx)\n\tif h.sideEffect != nil {\n\t\th.sideEffect(ctx)\n\t}\n\treturn resp, nil\n}\n\nfunc (h *JSONHandler) onError(ctx context.Context, err error) { h.t.Errorf(\"onError %v\", err) }\n\nfunc TestTracingSpanAttributes(t *testing.T) {\n\ttracer := mocktracer.New()\n\n\topts := &testutils.ChannelOpts{\n\t\tChannelOptions: ChannelOptions{Tracer: tracer},\n\t\tDisableRelay:   true,\n\t}\n\tWithVerifiedServer(t, opts, func(ch *Channel, hostPort string) {\n\t\tconst (\n\t\t\tcustomAppHeaderKey           = \"futurama\"\n\t\t\tcustomAppHeaderExpectedValue = \"simpsons\"\n\t\t)\n\t\tvar customAppHeaderValue atomic.String\n\t\t// Register JSON handler\n\t\tjsonHandler := &JSONHandler{\n\t\t\tTraceHandler: testtracing.TraceHandler{Ch: ch},\n\t\t\tt:            t,\n\t\t\tsideEffect: func(ctx json.Context) {\n\t\t\t\tcustomAppHeaderValue.Store(ctx.Headers()[customAppHeaderKey])\n\t\t\t},\n\t\t}\n\t\tjson.Register(ch, json.Handlers{\"call\": jsonHandler.callJSON}, jsonHandler.onError)\n\n\t\tspan := ch.Tracer().StartSpan(\"client\")\n\t\tspan.SetBaggageItem(testtracing.BaggageKey, testtracing.BaggageValue)\n\t\tctx := opentracing.ContextWithSpan(context.Background(), span)\n\t\troot := new(testtracing.TracingResponse).ObserveSpan(ctx)\n\n\t\t// Pretend that the client propagated tracing headers from upstream call, and test that the outbound call\n\t\t// will override them (https://github.com/uber/tchannel-go/issues/682).\n\t\ttracingHeaders := make(map[string]string)\n\t\tassert.NoError(t, tracer.Inject(span.Context(), opentracing.TextMap, opentracing.TextMapCarrier(tracingHeaders)))\n\t\trequestHeaders := map[string]string{\n\t\t\tcustomAppHeaderKey: customAppHeaderExpectedValue,\n\t\t}\n\t\tfor k := range tracingHeaders {\n\t\t\trequestHeaders[\"$tracing$\"+k] = \"garbage\"\n\t\t}\n\n\t\tctx, cancel := NewContextBuilder(2 * time.Second).SetParentContext(ctx).Build()\n\t\tdefer cancel()\n\n\t\tpeer := ch.Peers().GetOrAdd(ch.PeerInfo().HostPort)\n\t\tvar response testtracing.TracingResponse\n\t\trequire.NoError(t, json.CallPeer(json.WithHeaders(ctx, requestHeaders), peer, ch.PeerInfo().ServiceName,\n\t\t\t\"call\", &testtracing.TracingRequest{}, &response))\n\n\t\tassert.Equal(t, customAppHeaderExpectedValue, customAppHeaderValue.Load(), \"custom header was propagated\")\n\n\t\t// Spans are finished in inbound.doneSending() or outbound.doneReading(),\n\t\t// which are called on different go-routines and may execute *after* the\n\t\t// response has been received by the client. Give them a chance to finish.\n\t\tfor i := 0; i < 1000; i++ {\n\t\t\tif spanCount := len(testtracing.MockTracerSampledSpans(tracer)); spanCount == 2 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\ttime.Sleep(testutils.Timeout(time.Millisecond))\n\t\t}\n\t\tspans := testtracing.MockTracerSampledSpans(tracer)\n\t\tspanCount := len(spans)\n\t\tch.Logger().Debugf(\"end span count: %d\", spanCount)\n\n\t\t// finish span after taking count of recorded spans\n\t\tspan.Finish()\n\n\t\trequire.Equal(t, 2, spanCount, \"Wrong span count\")\n\t\tassert.Equal(t, root.TraceID, response.TraceID, \"Trace ID must match root span\")\n\t\tassert.Equal(t, testtracing.BaggageValue, response.Luggage, \"Baggage must match\")\n\n\t\tvar parent, child *mocktracer.MockSpan\n\t\tfor _, s := range spans {\n\t\t\tif s.Tag(\"span.kind\") == ext.SpanKindRPCClientEnum {\n\t\t\t\tparent = s\n\t\t\t\tch.Logger().Debugf(\"Found parent span: %+v\", s)\n\t\t\t} else if s.Tag(\"span.kind\") == ext.SpanKindRPCServerEnum {\n\t\t\t\tchild = s\n\t\t\t\tch.Logger().Debugf(\"Found child span: %+v\", s)\n\t\t\t}\n\t\t}\n\n\t\trequire.NotNil(t, parent)\n\t\trequire.NotNil(t, child)\n\n\t\ttraceID := func(s opentracing.Span) int {\n\t\t\treturn s.Context().(mocktracer.MockSpanContext).TraceID\n\t\t}\n\t\tspanID := func(s *mocktracer.MockSpan) int {\n\t\t\treturn s.Context().(mocktracer.MockSpanContext).SpanID\n\t\t}\n\t\tsampled := func(s *mocktracer.MockSpan) bool {\n\t\t\treturn s.Context().(mocktracer.MockSpanContext).Sampled\n\t\t}\n\n\t\trequire.Equal(t, traceID(span), traceID(parent), \"parent must be found\")\n\t\trequire.Equal(t, traceID(span), traceID(child), \"child must be found\")\n\t\tassert.Equal(t, traceID(parent), traceID(child))\n\t\tassert.Equal(t, spanID(parent), child.ParentID)\n\t\tassert.True(t, sampled(parent), \"should be sampled\")\n\t\tassert.True(t, sampled(child), \"should be sampled\")\n\t\tassert.Equal(t, \"call\", parent.OperationName)\n\t\tassert.Equal(t, \"call\", child.OperationName)\n\t\tassert.Equal(t, \"testService\", parent.Tag(\"peer.service\"))\n\t\tassert.Equal(t, \"testService\", child.Tag(\"peer.service\"))\n\t\tassert.Equal(t, \"json\", parent.Tag(\"as\"))\n\t\tassert.Equal(t, \"json\", child.Tag(\"as\"))\n\t\tassert.NotNil(t, parent.Tag(\"peer.ipv4\"))\n\t\tassert.NotNil(t, child.Tag(\"peer.ipv4\"))\n\t\tassert.NotNil(t, parent.Tag(\"peer.port\"))\n\t\tassert.NotNil(t, child.Tag(\"peer.port\"))\n\t\tassert.Equal(t, \"tchannel-go\", parent.Tag(\"component\"))\n\t\tassert.Equal(t, \"tchannel-go\", child.Tag(\"component\"))\n\t})\n}\n\n// Per https://github.com/uber/tchannel-go/issues/505, concurrent client calls\n// made with the same shared map used as headers were causing panic due to\n// concurrent writes to the map when injecting tracing headers.\nfunc TestReusableHeaders(t *testing.T) {\n\topts := &testutils.ChannelOpts{\n\t\tChannelOptions: ChannelOptions{Tracer: mocktracer.New()},\n\t}\n\tWithVerifiedServer(t, opts, func(ch *Channel, hostPort string) {\n\t\tjsonHandler := &JSONHandler{TraceHandler: testtracing.TraceHandler{Ch: ch}, t: t}\n\t\tjson.Register(ch, json.Handlers{\"call\": jsonHandler.callJSON}, jsonHandler.onError)\n\n\t\tspan := ch.Tracer().StartSpan(\"client\")\n\t\ttraceID := span.(*mocktracer.MockSpan).SpanContext.TraceID // for validation\n\t\tctx := opentracing.ContextWithSpan(context.Background(), span)\n\n\t\tsharedHeaders := map[string]string{\"life\": \"42\"}\n\t\tctx, cancel := NewContextBuilder(2 * time.Second).\n\t\t\tSetHeaders(sharedHeaders).\n\t\t\tSetParentContext(ctx).\n\t\t\tBuild()\n\t\tdefer cancel()\n\n\t\tpeer := ch.Peers().GetOrAdd(ch.PeerInfo().HostPort)\n\n\t\tvar wg sync.WaitGroup\n\t\tfor i := 0; i < 42; i++ {\n\t\t\twg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tvar response testtracing.TracingResponse\n\t\t\t\terr := json.CallPeer(json.Wrap(ctx), peer, ch.ServiceName(),\n\t\t\t\t\t\"call\", &testtracing.TracingRequest{}, &response)\n\t\t\t\tassert.NoError(t, err, \"json.Call failed\")\n\t\t\t\tassert.EqualValues(t, traceID, response.TraceID, \"traceID must match\")\n\t\t\t}()\n\t\t}\n\t\twg.Wait()\n\t\tassert.Equal(t, map[string]string{\"life\": \"42\"}, sharedHeaders, \"headers unchanged\")\n\t})\n}\n"
  },
  {
    "path": "trand/rand.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package trand provides a thread-safe random number generator.\npackage trand\n\nimport (\n\t\"math/rand\"\n\t\"sync\"\n\t\"time\"\n)\n\n// lockedSource allows a random number generator to be used by multiple goroutines\n// concurrently. The code is very similar to math/rand.lockedSource, which is\n// unfortunately not exposed.\ntype lockedSource struct {\n\tsync.Mutex\n\n\tsrc rand.Source\n}\n\n// New returns a rand.Rand that is threadsafe.\nfunc New(seed int64) *rand.Rand {\n\treturn rand.New(&lockedSource{src: rand.NewSource(seed)})\n}\n\n// NewSeeded returns a rand.Rand that's threadsafe and seeded with the current\n// time.\nfunc NewSeeded() *rand.Rand {\n\treturn New(time.Now().UnixNano())\n}\n\nfunc (r *lockedSource) Int63() (n int64) {\n\tr.Lock()\n\tn = r.src.Int63()\n\tr.Unlock()\n\treturn\n}\n\nfunc (r *lockedSource) Seed(seed int64) {\n\tr.Lock()\n\tr.src.Seed(seed)\n\tr.Unlock()\n}\n"
  },
  {
    "path": "typed/buffer.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage typed\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n)\n\nvar (\n\t// ErrEOF is returned when trying to read past end of buffer\n\tErrEOF = errors.New(\"buffer is too small\")\n\n\t// ErrBufferFull is returned when trying to write past end of buffer\n\tErrBufferFull = errors.New(\"no more room in buffer\")\n\n\t// errStringTooLong is returned when writing a string with length larger\n\t// than the allows length limit. Intentionally not exported, in case we\n\t// want to add more context in future.\n\terrStringTooLong = errors.New(\"string is too long\")\n)\n\n// A ReadBuffer is a wrapper around an underlying []byte with methods to read from\n// that buffer in big-endian format.\ntype ReadBuffer struct {\n\tinitialLength int\n\tremaining     []byte\n\terr           error\n}\n\n// NewReadBuffer returns a ReadBuffer wrapping a byte slice\nfunc NewReadBuffer(buffer []byte) *ReadBuffer {\n\treturn &ReadBuffer{initialLength: len(buffer), remaining: buffer}\n}\n\n// ReadSingleByte reads the next byte from the buffer\nfunc (r *ReadBuffer) ReadSingleByte() byte {\n\tb, _ := r.ReadByte()\n\treturn b\n}\n\n// ReadByte returns the next byte from the buffer.\n//\n// This method implements the ByteReader interface.\nfunc (r *ReadBuffer) ReadByte() (byte, error) {\n\tif r.err != nil {\n\t\treturn 0, r.err\n\t}\n\n\tif len(r.remaining) < 1 {\n\t\tr.err = ErrEOF\n\t\treturn 0, r.err\n\t}\n\n\tb := r.remaining[0]\n\tr.remaining = r.remaining[1:]\n\treturn b, nil\n}\n\n// ReadBytes returns the next n bytes from the buffer\nfunc (r *ReadBuffer) ReadBytes(n int) []byte {\n\tif r.err != nil {\n\t\treturn nil\n\t}\n\n\tif len(r.remaining) < n {\n\t\tr.err = ErrEOF\n\t\treturn nil\n\t}\n\n\tb := r.remaining[0:n]\n\tr.remaining = r.remaining[n:]\n\treturn b\n}\n\n// SkipBytes skips the next n bytes from the buffer\nfunc (r *ReadBuffer) SkipBytes(n int) {\n\tif r.err != nil {\n\t\treturn\n\t}\n\n\tif len(r.remaining) < n {\n\t\tr.err = ErrEOF\n\t\treturn\n\t}\n\n\tr.remaining = r.remaining[n:]\n}\n\n// ReadString returns a string of size n from the buffer\nfunc (r *ReadBuffer) ReadString(n int) string {\n\tif b := r.ReadBytes(n); b != nil {\n\t\t// TODO(mmihic): This creates a copy, which sucks\n\t\treturn string(b)\n\t}\n\n\treturn \"\"\n}\n\n// ReadUint16 returns the next value in the buffer as a uint16\nfunc (r *ReadBuffer) ReadUint16() uint16 {\n\tif b := r.ReadBytes(2); b != nil {\n\t\treturn binary.BigEndian.Uint16(b)\n\t}\n\n\treturn 0\n}\n\n// ReadUint32 returns the next value in the buffer as a uint32\nfunc (r *ReadBuffer) ReadUint32() uint32 {\n\tif b := r.ReadBytes(4); b != nil {\n\t\treturn binary.BigEndian.Uint32(b)\n\t}\n\n\treturn 0\n}\n\n// ReadUint64 returns the next value in the buffer as a uint64\nfunc (r *ReadBuffer) ReadUint64() uint64 {\n\tif b := r.ReadBytes(8); b != nil {\n\t\treturn binary.BigEndian.Uint64(b)\n\t}\n\n\treturn 0\n}\n\n// ReadUvarint reads an unsigned varint from the buffer.\nfunc (r *ReadBuffer) ReadUvarint() uint64 {\n\tv, _ := binary.ReadUvarint(r)\n\treturn v\n}\n\n// ReadLen8String reads an 8-bit length preceded string value\nfunc (r *ReadBuffer) ReadLen8String() string {\n\tn := r.ReadSingleByte()\n\treturn r.ReadString(int(n))\n}\n\n// ReadLen16String reads a 16-bit length preceded string value\nfunc (r *ReadBuffer) ReadLen16String() string {\n\tn := r.ReadUint16()\n\treturn r.ReadString(int(n))\n}\n\n// Remaining returns the unconsumed bytes.\nfunc (r *ReadBuffer) Remaining() []byte {\n\treturn r.remaining\n}\n\n// BytesRemaining returns the length of Remaining.\nfunc (r *ReadBuffer) BytesRemaining() int {\n\treturn len(r.Remaining())\n}\n\n// BytesRead returns the number of bytes consumed\nfunc (r *ReadBuffer) BytesRead() int {\n\treturn r.initialLength - len(r.remaining)\n}\n\n// Wrap initializes the buffer to read from the given byte slice\nfunc (r *ReadBuffer) Wrap(b []byte) {\n\tr.initialLength = len(b)\n\tr.remaining = b\n\tr.err = nil\n}\n\n// Err returns the error in the ReadBuffer\nfunc (r *ReadBuffer) Err() error { return r.err }\n\n// A WriteBuffer is a wrapper around an underlying []byte with methods to write to\n// that buffer in big-endian format.  The buffer is of fixed size, and does not grow.\ntype WriteBuffer struct {\n\tbuffer    []byte\n\tremaining []byte\n\terr       error\n}\n\n// NewWriteBuffer creates a WriteBuffer wrapping the given slice\nfunc NewWriteBuffer(buffer []byte) *WriteBuffer {\n\treturn &WriteBuffer{buffer: buffer, remaining: buffer}\n}\n\n// NewWriteBufferWithSize create a new WriteBuffer using an internal buffer of the given size\nfunc NewWriteBufferWithSize(size int) *WriteBuffer {\n\treturn NewWriteBuffer(make([]byte, size))\n}\n\n// WriteSingleByte writes a single byte to the buffer\nfunc (w *WriteBuffer) WriteSingleByte(n byte) {\n\tif w.err != nil {\n\t\treturn\n\t}\n\n\tif len(w.remaining) == 0 {\n\t\tw.setErr(ErrBufferFull)\n\t\treturn\n\t}\n\n\tw.remaining[0] = n\n\tw.remaining = w.remaining[1:]\n}\n\n// WriteBytes writes a slice of bytes to the buffer\nfunc (w *WriteBuffer) WriteBytes(in []byte) {\n\tif b := w.reserve(len(in)); b != nil {\n\t\tcopy(b, in)\n\t}\n}\n\n// WriteUint16 writes a big endian encoded uint16 value to the buffer\nfunc (w *WriteBuffer) WriteUint16(n uint16) {\n\tif b := w.reserve(2); b != nil {\n\t\tbinary.BigEndian.PutUint16(b, n)\n\t}\n}\n\n// WriteUint32 writes a big endian uint32 value to the buffer\nfunc (w *WriteBuffer) WriteUint32(n uint32) {\n\tif b := w.reserve(4); b != nil {\n\t\tbinary.BigEndian.PutUint32(b, n)\n\t}\n}\n\n// WriteUint64 writes a big endian uint64 to the buffer\nfunc (w *WriteBuffer) WriteUint64(n uint64) {\n\tif b := w.reserve(8); b != nil {\n\t\tbinary.BigEndian.PutUint64(b, n)\n\t}\n}\n\n// WriteUvarint writes an unsigned varint to the buffer\nfunc (w *WriteBuffer) WriteUvarint(n uint64) {\n\t// A uvarint could be up to 10 bytes long.\n\tbuf := make([]byte, 10)\n\tvarBytes := binary.PutUvarint(buf, n)\n\tif b := w.reserve(varBytes); b != nil {\n\t\tcopy(b, buf[0:varBytes])\n\t}\n}\n\n// WriteString writes a string to the buffer\nfunc (w *WriteBuffer) WriteString(s string) {\n\t// NB(mmihic): Don't just call WriteBytes; that will make a double copy\n\t// of the string due to the cast\n\tif b := w.reserve(len(s)); b != nil {\n\t\tcopy(b, s)\n\t}\n}\n\n// WriteLen8String writes an 8-bit length preceded string\nfunc (w *WriteBuffer) WriteLen8String(s string) {\n\tif int(byte(len(s))) != len(s) {\n\t\tw.setErr(errStringTooLong)\n\t}\n\n\tw.WriteSingleByte(byte(len(s)))\n\tw.WriteString(s)\n}\n\n// WriteLen16String writes a 16-bit length preceded string\nfunc (w *WriteBuffer) WriteLen16String(s string) {\n\tif int(uint16(len(s))) != len(s) {\n\t\tw.setErr(errStringTooLong)\n\t}\n\n\tw.WriteUint16(uint16(len(s)))\n\tw.WriteString(s)\n}\n\n// DeferByte reserves space in the buffer for a single byte, and returns a\n// reference that can be used to update that byte later\nfunc (w *WriteBuffer) DeferByte() ByteRef {\n\tif len(w.remaining) == 0 {\n\t\tw.setErr(ErrBufferFull)\n\t\treturn ByteRef(nil)\n\t}\n\n\t// Always zero out references, since the caller expects the default to be 0.\n\tw.remaining[0] = 0\n\tbufRef := ByteRef(w.remaining[0:])\n\tw.remaining = w.remaining[1:]\n\treturn bufRef\n}\n\n// DeferUint16 reserves space in the buffer for a uint16, and returns a\n// reference that can be used to update that uint16\nfunc (w *WriteBuffer) DeferUint16() Uint16Ref {\n\treturn Uint16Ref(w.deferred(2))\n}\n\n// DeferUint32 reserves space in the buffer for a uint32, and returns a\n// reference that can be used to update that uint32\nfunc (w *WriteBuffer) DeferUint32() Uint32Ref {\n\treturn Uint32Ref(w.deferred(4))\n}\n\n// DeferUint64 reserves space in the buffer for a uint64, and returns a\n// reference that can be used to update that uint64\nfunc (w *WriteBuffer) DeferUint64() Uint64Ref {\n\treturn Uint64Ref(w.deferred(8))\n}\n\n// DeferBytes reserves space in the buffer for a fixed sequence of bytes, and\n// returns a reference that can be used to update those bytes\nfunc (w *WriteBuffer) DeferBytes(n int) BytesRef {\n\treturn BytesRef(w.deferred(n))\n}\n\nfunc (w *WriteBuffer) deferred(n int) []byte {\n\tbs := w.reserve(n)\n\tfor i := range bs {\n\t\tbs[i] = 0\n\t}\n\treturn bs\n}\n\nfunc (w *WriteBuffer) reserve(n int) []byte {\n\tif w.err != nil {\n\t\treturn nil\n\t}\n\n\tif len(w.remaining) < n {\n\t\tw.setErr(ErrBufferFull)\n\t\treturn nil\n\t}\n\n\tb := w.remaining[0:n]\n\tw.remaining = w.remaining[n:]\n\treturn b\n}\n\n// BytesRemaining returns the number of available bytes remaining in the bufffer\nfunc (w *WriteBuffer) BytesRemaining() int {\n\treturn len(w.remaining)\n}\n\n// FlushTo flushes the written buffer to the given writer.\nfunc (w *WriteBuffer) FlushTo(iow io.Writer) (int, error) {\n\tdirty := w.buffer[0:w.BytesWritten()]\n\treturn iow.Write(dirty)\n}\n\n// BytesWritten returns the number of bytes that have been written to the buffer\nfunc (w *WriteBuffer) BytesWritten() int { return len(w.buffer) - len(w.remaining) }\n\n// Reset resets the buffer to an empty state, ready for writing\nfunc (w *WriteBuffer) Reset() {\n\tw.remaining = w.buffer\n\tw.err = nil\n}\n\nfunc (w *WriteBuffer) setErr(err error) {\n\t// Only store the first error\n\tif w.err != nil {\n\t\treturn\n\t}\n\n\tw.err = err\n}\n\n// Err returns the current error in the buffer\nfunc (w *WriteBuffer) Err() error { return w.err }\n\n// Wrap initializes the buffer to wrap the given byte slice\nfunc (w *WriteBuffer) Wrap(b []byte) {\n\tw.buffer = b\n\tw.remaining = b\n}\n\n// A ByteRef is a reference to a byte in a bufffer\ntype ByteRef []byte\n\n// Update updates the byte in the buffer\nfunc (ref ByteRef) Update(b byte) {\n\tif ref != nil {\n\t\tref[0] = b\n\t}\n}\n\n// A Uint16Ref is a reference to a uint16 placeholder in a buffer\ntype Uint16Ref []byte\n\n// Update updates the uint16 in the buffer\nfunc (ref Uint16Ref) Update(n uint16) {\n\tif ref != nil {\n\t\tbinary.BigEndian.PutUint16(ref, n)\n\t}\n}\n\n// A Uint32Ref is a reference to a uint32 placeholder in a buffer\ntype Uint32Ref []byte\n\n// Update updates the uint32 in the buffer\nfunc (ref Uint32Ref) Update(n uint32) {\n\tif ref != nil {\n\t\tbinary.BigEndian.PutUint32(ref, n)\n\t}\n}\n\n// A Uint64Ref is a reference to a uin64 placeholder in a buffer\ntype Uint64Ref []byte\n\n// Update updates the uint64 in the buffer\nfunc (ref Uint64Ref) Update(n uint64) {\n\tif ref != nil {\n\t\tbinary.BigEndian.PutUint64(ref, n)\n\t}\n}\n\n// A BytesRef is a reference to a multi-byte placeholder in a buffer\ntype BytesRef []byte\n\n// Update updates the bytes in the buffer\nfunc (ref BytesRef) Update(b []byte) {\n\tif ref != nil {\n\t\tcopy(ref, b)\n\t}\n}\n\n// UpdateString updates the bytes in the buffer from a string\nfunc (ref BytesRef) UpdateString(s string) {\n\tif ref != nil {\n\t\tcopy(ref, s)\n\t}\n}\n"
  },
  {
    "path": "typed/buffer_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage typed\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestSimple(t *testing.T) {\n\tbuf := make([]byte, 200)\n\n\tvar r ReadBuffer\n\tvar w WriteBuffer\n\n\t{\n\t\tw.Wrap(buf)\n\t\tw.WriteSingleByte(0xFC)\n\t\tr.Wrap(buf)\n\t\tassert.Equal(t, byte(0xFC), r.ReadSingleByte())\n\t}\n\n\t{\n\t\tw.Wrap(buf)\n\t\tw.WriteUint16(0xDEAD)\n\t\tr.Wrap(buf)\n\t\tassert.Equal(t, uint16(0xDEAD), r.ReadUint16())\n\t}\n\n\t{\n\t\tw.Wrap(buf)\n\t\tw.WriteUint32(0xBEEFDEAD)\n\t\tr.Wrap(buf)\n\t\tassert.Equal(t, uint32(0xBEEFDEAD), r.ReadUint32())\n\t}\n}\n\nfunc TestReadBufferSkipBytes(t *testing.T) {\n\texampleBytes := make([]byte, 128)\n\ttests := []struct {\n\t\tmsg           string\n\t\tbuf           *ReadBuffer\n\t\tnSkip         int\n\t\twantError     string\n\t\twantRead      int\n\t\twantRemaining int\n\t}{\n\t\t{\n\t\t\tmsg:           \"successful skip\",\n\t\t\tbuf:           NewReadBuffer(exampleBytes),\n\t\t\tnSkip:         64,\n\t\t\twantRead:      64,\n\t\t\twantRemaining: 64,\n\t\t},\n\t\t{\n\t\t\tmsg: \"error occurred prior to skip\",\n\t\t\tbuf: func() *ReadBuffer {\n\t\t\t\tbuf := NewReadBuffer(exampleBytes)\n\t\t\t\tbuf.err = errors.New(\"something bad happened\")\n\t\t\t\treturn buf\n\t\t\t}(),\n\t\t\tnSkip:         64,\n\t\t\twantError:     \"something bad happened\",\n\t\t\twantRead:      0,\n\t\t\twantRemaining: 128,\n\t\t},\n\t\t{\n\t\t\tmsg: \"not enough bytes remain\",\n\t\t\tbuf: func() *ReadBuffer {\n\t\t\t\tbuf := NewReadBuffer(exampleBytes)\n\t\t\t\treturn buf\n\t\t\t}(),\n\t\t\tnSkip:         256,\n\t\t\twantError:     \"buffer is too small\",\n\t\t\twantRead:      0,\n\t\t\twantRemaining: 128,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\ttt.buf.SkipBytes(tt.nSkip)\n\t\t\tif tt.wantError != \"\" {\n\t\t\t\trequire.EqualError(t, tt.buf.Err(), tt.wantError, \"Didn't get exepcted error\")\n\t\t\t}\n\t\t\tassert.Equal(t, tt.wantRead, tt.buf.BytesRead())\n\t\t\tassert.Equal(t, tt.wantRemaining, tt.buf.BytesRemaining())\n\t\t})\n\t}\n}\n\nfunc TestShortBuffer(t *testing.T) {\n\tr := NewReadBuffer([]byte{23})\n\tassert.EqualValues(t, 0, r.ReadUint16())\n\tassert.Equal(t, ErrEOF, r.Err())\n}\n\nfunc TestReadWrite(t *testing.T) {\n\ts := \"the small brown fix\"\n\tbslice := []byte(\"jumped over the lazy dog\")\n\n\tw := NewWriteBufferWithSize(1024)\n\tw.WriteUint64(0x0123456789ABCDEF)\n\tw.WriteUint32(0xABCDEF01)\n\tw.WriteUint16(0x2345)\n\tw.WriteUvarint(1)\n\tw.WriteUvarint(math.MaxInt16)\n\tw.WriteUvarint(math.MaxInt32)\n\tw.WriteUvarint(math.MaxInt64)\n\tw.WriteSingleByte(0xFF)\n\tw.WriteString(s)\n\tw.WriteBytes(bslice)\n\tw.WriteLen8String(\"hello\")\n\tw.WriteLen16String(\"This is a much larger string\")\n\trequire.NoError(t, w.Err())\n}\n\nfunc TestReadBufferTracking(t *testing.T) {\n\tbs := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n\n\t// Run twice, once on the original buffer, and once after wrapping.\n\trbuf := NewReadBuffer(bs)\n\tfor _, wrap := range []bool{false, true} {\n\t\tif wrap {\n\t\t\trbuf.Wrap(bs)\n\t\t}\n\n\t\tt.Run(\"nothing read\", func(t *testing.T) {\n\t\t\tassert.Equal(t, len(bs), rbuf.BytesRemaining(), \"BytesRemaining\")\n\t\t\tassert.Equal(t, bs, rbuf.Remaining(), \"Remaining\")\n\t\t\tassert.Zero(t, rbuf.BytesRead(), \"BytesRead\")\n\t\t})\n\n\t\tt.Run(\"partially consumed\", func(t *testing.T) {\n\t\t\trbuf.ReadByte()\n\t\t\trbuf.ReadUint32()\n\n\t\t\tassert.Equal(t, 5, rbuf.BytesRemaining(), \"BytesRemaining\")\n\t\t\tassert.Equal(t, bs[5:], rbuf.Remaining(), \"Remaining\")\n\t\t\tassert.Equal(t, 5, rbuf.BytesRead(), \"BytesRead\")\n\t\t})\n\n\t\tt.Run(\"fully consumed\", func(t *testing.T) {\n\t\t\trbuf.ReadByte()\n\t\t\trbuf.ReadUint32()\n\n\t\t\tassert.Zero(t, rbuf.BytesRemaining(), \"BytesRemaining\")\n\t\t\tassert.Empty(t, rbuf.Remaining(), \"Remaining\")\n\t\t\tassert.Equal(t, len(bs), rbuf.BytesRead(), \"BytesRead\")\n\t\t})\n\n\t\trequire.NoError(t, rbuf.Err())\n\t}\n}\n\nfunc TestDeferredWrites(t *testing.T) {\n\tw := NewWriteBufferWithSize(1024)\n\tu16ref := w.DeferUint16()\n\trequire.NotNil(t, u16ref)\n\n\tu32ref := w.DeferUint32()\n\trequire.NotNil(t, u32ref)\n\n\tu64ref := w.DeferUint64()\n\trequire.NotNil(t, u64ref)\n\n\tbref := w.DeferBytes(5)\n\trequire.NotNil(t, bref)\n\n\tsref := w.DeferBytes(5)\n\trequire.NotNil(t, sref)\n\n\tbyteref := w.DeferByte()\n\trequire.NotNil(t, byteref)\n\n\tassert.Equal(t, 2+4+8+5+5+1, w.BytesWritten())\n\n\tu16ref.Update(2040)\n\tu32ref.Update(495404)\n\tu64ref.Update(0x40950459)\n\tbref.Update([]byte{0x30, 0x12, 0x45, 0x55, 0x65})\n\tsref.UpdateString(\"where\")\n\tbyteref.Update(0x44)\n\n\tvar buf bytes.Buffer\n\tw.FlushTo(&buf)\n\n\tr := NewReadBuffer(buf.Bytes())\n\n\tu16 := r.ReadUint16()\n\tassert.Equal(t, uint16(2040), u16)\n\n\tu32 := r.ReadUint32()\n\tassert.Equal(t, uint32(495404), u32)\n\n\tu64 := r.ReadUint64()\n\tassert.Equal(t, uint64(0x40950459), u64)\n\n\tb := r.ReadBytes(5)\n\tassert.Equal(t, []byte{0x30, 0x12, 0x45, 0x55, 0x65}, b)\n\n\ts := r.ReadString(5)\n\tassert.Equal(t, \"where\", s)\n\n\tu8 := r.ReadSingleByte()\n\tassert.Equal(t, byte(0x44), u8)\n\tassert.NoError(t, r.Err())\n}\n\nfunc TestDirtyUnderlyingBuffer(t *testing.T) {\n\tbuf := make([]byte, 128)\n\tfor i := range buf {\n\t\tbuf[i] = ^byte(0)\n\t}\n\tw := NewWriteBuffer(buf)\n\n\t// Defer 1 + 2 + 4 + 8 + 5 = 20 bytes\n\tw.DeferByte()\n\tw.DeferUint16()\n\tw.DeferUint32()\n\tw.DeferUint64()\n\tw.DeferBytes(5)\n\n\tdefer1 := w.DeferByte()\n\tdefer2 := w.DeferUint16()\n\tdefer3 := w.DeferUint32()\n\tdefer4 := w.DeferUint64()\n\tdefer5 := w.DeferBytes(5)\n\n\tw.WriteUint16(16)\n\tw.WriteUint32(32)\n\tw.WriteUint64(64)\n\tw.WriteLen16String(\"len16 string\")\n\tw.WriteLen8String(\"len8 string\")\n\tw.WriteString(\"string\")\n\tw.WriteSingleByte(1)\n\tw.WriteBytes([]byte{1, 2, 3, 4, 5})\n\n\tdefer1.Update(11)\n\tdefer2.Update(116)\n\tdefer3.Update(132)\n\tdefer4.Update(164)\n\tdefer5.Update([]byte{11, 12, 13, 14, 15})\n\n\tr := NewReadBuffer(buf)\n\n\t// Deferred unwritten bytes should be 0.\n\tassert.EqualValues(t, 0, r.ReadSingleByte(), \"unwritten deferred should be 0\")\n\tassert.EqualValues(t, 0, r.ReadUint16(), \"unwritten deferred should be 0\")\n\tassert.EqualValues(t, 0, r.ReadUint32(), \"unwritten deferred should be 0\")\n\tassert.EqualValues(t, 0, r.ReadUint64(), \"unwritten deferred should be 0\")\n\tassert.Equal(t, []byte{0, 0, 0, 0, 0}, r.ReadBytes(5), \"unwritten deferred should be 0\")\n\n\t// Deferred written bytes.\n\tassert.EqualValues(t, 11, r.ReadSingleByte(), \"defer byte\")\n\tassert.EqualValues(t, 116, r.ReadUint16(), \"defer uint16\")\n\tassert.EqualValues(t, 132, r.ReadUint32(), \"defer uint32\")\n\tassert.EqualValues(t, 164, r.ReadUint64(), \"defer uint64\")\n\tassert.Equal(t, []byte{11, 12, 13, 14, 15}, r.ReadBytes(5), \"defer bytes\")\n\n\t// Normally written bytes.\n\tassert.EqualValues(t, 16, r.ReadUint16(), \"uint16\")\n\tassert.EqualValues(t, 32, r.ReadUint32(), \"uint32\")\n\tassert.EqualValues(t, 64, r.ReadUint64(), \"uint64\")\n\tassert.Equal(t, \"len16 string\", r.ReadLen16String(), \"len16 string\")\n\tassert.Equal(t, \"len8 string\", r.ReadLen8String(), \"len 8 string\")\n\tassert.Equal(t, \"string\", r.ReadString(6), \"string\")\n\tassert.EqualValues(t, 1, r.ReadSingleByte(), \"byte\")\n\tassert.Equal(t, []byte{1, 2, 3, 4, 5}, r.ReadBytes(5), \"bytes\")\n}\n"
  },
  {
    "path": "typed/reader.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage typed\n\nimport (\n\t\"encoding/binary\"\n\t\"io\"\n\t\"sync\"\n)\n\nconst maxPoolStringLen = 32\n\n// Reader is a reader that reads typed values from an io.Reader.\ntype Reader struct {\n\treader io.Reader\n\terr    error\n\tbuf    [maxPoolStringLen]byte\n}\n\nvar readerPool = sync.Pool{\n\tNew: func() interface{} {\n\t\treturn &Reader{}\n\t},\n}\n\n// NewReader returns a reader that reads typed values from the reader.\nfunc NewReader(reader io.Reader) *Reader {\n\tr := readerPool.Get().(*Reader)\n\tr.reader = reader\n\tr.err = nil\n\treturn r\n}\n\n// ReadUint16 reads a uint16.\nfunc (r *Reader) ReadUint16() uint16 {\n\tif r.err != nil {\n\t\treturn 0\n\t}\n\n\tbuf := r.buf[:2]\n\n\tvar readN int\n\treadN, r.err = io.ReadFull(r.reader, buf)\n\tif readN < 2 {\n\t\treturn 0\n\t}\n\treturn binary.BigEndian.Uint16(buf)\n}\n\n// ReadString reads a string of length n.\nfunc (r *Reader) ReadString(n int) string {\n\tif r.err != nil {\n\t\treturn \"\"\n\t}\n\n\tvar buf []byte\n\tif n <= maxPoolStringLen {\n\t\tbuf = r.buf[:n]\n\t} else {\n\t\tbuf = make([]byte, n)\n\t}\n\n\tvar readN int\n\treadN, r.err = io.ReadFull(r.reader, buf)\n\tif readN < n {\n\t\treturn \"\"\n\t}\n\ts := string(buf)\n\n\treturn s\n}\n\n// ReadLen16String reads a uint16-length prefixed string.\nfunc (r *Reader) ReadLen16String() string {\n\tlen := r.ReadUint16()\n\treturn r.ReadString(int(len))\n}\n\n// Err returns any errors hit while reading from the underlying reader.\nfunc (r *Reader) Err() error {\n\treturn r.err\n}\n\n// Release puts the Reader back in the pool.\nfunc (r *Reader) Release() {\n\treaderPool.Put(r)\n}\n"
  },
  {
    "path": "typed/reader_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage typed\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/uber/tchannel-go/testutils/testreader\"\n)\n\nfunc nString(n int) []byte {\n\tbuf := make([]byte, n)\n\treader := testreader.Looper([]byte{'a', 'b', 'c', 'd', 'e'})\n\tio.ReadFull(reader, buf)\n\treturn buf\n}\n\nfunc TestReader(t *testing.T) {\n\ts1 := nString(10)\n\ts2 := nString(800)\n\n\tvar buf []byte\n\tbuf = append(buf, 0, 1)       // uint16, 1\n\tbuf = append(buf, 0xff, 0xff) // uint16, 65535\n\tbuf = append(buf, 0, 10)      // uint16, 10\n\tbuf = append(buf, s1...)      // string, 10 bytes\n\tbuf = append(buf, 3, 32)      // uint16, 800\n\tbuf = append(buf, s2...)      // string, 800 bytes\n\tbuf = append(buf, 0, 10)      // uint16, 10\n\n\treader := NewReader(bytes.NewReader(buf))\n\n\tassert.Equal(t, uint16(1), reader.ReadUint16())\n\tassert.Equal(t, uint16(65535), reader.ReadUint16())\n\tassert.Equal(t, string(s1), reader.ReadLen16String())\n\tassert.Equal(t, string(s2), reader.ReadLen16String())\n\tassert.Equal(t, uint16(10), reader.ReadUint16())\n}\n\nfunc TestReaderErr(t *testing.T) {\n\ttests := []struct {\n\t\tchunks     [][]byte\n\t\tvalidation func(reader *Reader)\n\t}{\n\t\t{\n\t\t\tchunks: [][]byte{\n\t\t\t\t{0, 1},\n\t\t\t\tnil,\n\t\t\t\t{2, 3},\n\t\t\t},\n\t\t\tvalidation: func(reader *Reader) {\n\t\t\t\tassert.Equal(t, uint16(1), reader.ReadUint16(), \"Read unexpected value\")\n\t\t\t\tassert.Equal(t, uint16(0), reader.ReadUint16(), \"Expected default value\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tchunks: [][]byte{\n\t\t\t\t{0, 4},\n\t\t\t\t[]byte(\"test\"),\n\t\t\t\tnil,\n\t\t\t\t{'A', 'b'},\n\t\t\t},\n\t\t\tvalidation: func(reader *Reader) {\n\t\t\t\tassert.Equal(t, \"test\", reader.ReadLen16String(), \"Read unexpected value\")\n\t\t\t\tassert.Equal(t, \"\", reader.ReadString(2), \"Expected default value\")\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\twriter, chunkReader := testreader.ChunkReader()\n\t\treader := NewReader(chunkReader)\n\t\tdefer reader.Release()\n\n\t\tfor _, chunk := range tt.chunks {\n\t\t\twriter <- chunk\n\t\t}\n\t\tclose(writer)\n\n\t\ttt.validation(reader)\n\t\t// Once there's an error, all further calls should fail.\n\t\tassert.Equal(t, testreader.ErrUser, reader.Err(), \"Unexpected error\")\n\t\tassert.Equal(t, uint16(0), reader.ReadUint16(), \"Expected default value\")\n\t\tassert.Equal(t, \"\", reader.ReadString(1), \"Expected default value\")\n\t\tassert.Equal(t, \"\", reader.ReadLen16String(), \"Expected default value\")\n\t\tassert.Equal(t, testreader.ErrUser, reader.Err(), \"Unexpected error\")\n\t}\n}\n"
  },
  {
    "path": "typed/writer.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage typed\n\nimport (\n\t\"encoding/binary\"\n\t\"io\"\n\t\"sync\"\n)\n\ntype intBuffer [8]byte\n\nvar intBufferPool = sync.Pool{New: func() interface{} {\n\treturn new(intBuffer)\n}}\n\n// Writer is a writer that writes typed values to an io.Writer\ntype Writer struct {\n\twriter io.Writer\n\terr    error\n}\n\n// NewWriter creates a writer that writes typed value to a reader\nfunc NewWriter(w io.Writer) *Writer {\n\treturn &Writer{\n\t\twriter: w,\n\t}\n}\n\n// WriteBytes writes a slice of bytes to the io.Writer\nfunc (w *Writer) WriteBytes(b []byte) {\n\tif w.err != nil {\n\t\treturn\n\t}\n\n\tif _, err := w.writer.Write(b); err != nil {\n\t\tw.err = err\n\t}\n}\n\n// WriteUint16 writes a uint16 to the io.Writer\nfunc (w *Writer) WriteUint16(n uint16) {\n\tif w.err != nil {\n\t\treturn\n\t}\n\n\tsizeBuf := intBufferPool.Get().(*intBuffer)\n\tdefer intBufferPool.Put(sizeBuf)\n\n\tbinary.BigEndian.PutUint16(sizeBuf[:2], n)\n\tif _, err := w.writer.Write(sizeBuf[:2]); err != nil {\n\t\tw.err = err\n\t}\n}\n\n// WriteLen16Bytes writes a slice of bytes to the io.Writer preceded with\n// the length of the slice\nfunc (w *Writer) WriteLen16Bytes(b []byte) {\n\tif w.err != nil {\n\t\treturn\n\t}\n\n\tw.WriteUint16(uint16(len(b)))\n\tw.WriteBytes(b)\n}\n\n// Err returns the error state of the writer\nfunc (w *Writer) Err() error {\n\treturn w.err\n}\n"
  },
  {
    "path": "typed/writer_test.go",
    "content": "package typed\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype dummyWriter struct {\n\tcalls        int\n\tbytesWritten []byte\n\t// retError is a map of call ids to error strings\n\tretError map[int]string\n}\n\nfunc (w *dummyWriter) Write(b []byte) (int, error) {\n\tdefer func() { w.calls++ }()\n\n\tif w.retError[w.calls] != \"\" {\n\t\treturn 0, errors.New(w.retError[w.calls])\n\t}\n\tw.bytesWritten = append(w.bytesWritten, b...)\n\treturn len(b), nil\n}\n\nfunc TestWriter(t *testing.T) {\n\ttests := []struct {\n\t\tmsg              string\n\t\tw                *dummyWriter\n\t\tpreviousError    error\n\t\twantError        string\n\t\twantBytesWritten []byte\n\t}{\n\t\t{\n\t\t\tmsg: \"successful write\",\n\t\t\tw: &dummyWriter{\n\t\t\t\tretError: map[int]string{},\n\t\t\t},\n\t\t\twantBytesWritten: []byte{0, 1, 2, 0, 3, 4, 5, 6},\n\t\t},\n\t\t{\n\t\t\tmsg:           \"return error due to previous error\",\n\t\t\tpreviousError: errors.New(\"something went wrong previously\"),\n\t\t\tw:             &dummyWriter{},\n\t\t\twantError:     \"something went wrong previously\",\n\t\t},\n\t\t{\n\t\t\tmsg: \"error writing length\",\n\t\t\tw: &dummyWriter{\n\t\t\t\tretError: map[int]string{0: \"something went wrong\"},\n\t\t\t},\n\t\t\twantError: \"something went wrong\",\n\t\t},\n\t\t{\n\t\t\tmsg: \"error writing data\",\n\t\t\tw: &dummyWriter{\n\t\t\t\tretError: map[int]string{1: \"something went wrong\"},\n\t\t\t},\n\t\t\twantError: \"something went wrong\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\twrites := func(w *Writer) {\n\t\t\t\tw.WriteUint16(1)\n\t\t\t\tw.WriteBytes([]byte{2})\n\t\t\t\tw.WriteLen16Bytes([]byte{4, 5, 6})\n\t\t\t}\n\n\t\t\tw := NewWriter(tt.w)\n\t\t\tw.err = tt.previousError\n\t\t\twrites(w)\n\n\t\t\tif tt.wantError != \"\" {\n\t\t\t\trequire.EqualError(t, w.Err(), tt.wantError, \"Got unexpected error\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, w.Err(), \"Got unexpected error\")\n\t\t\tassert.Equal(t, tt.wantBytesWritten, tt.w.bytesWritten)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "utils_for_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\n// This file contains functions for tests to access internal tchannel state.\n// Since it has a _test.go suffix, it is only compiled with tests in this package.\n\nimport (\n\t\"net\"\n\n\t\"golang.org/x/net/context\"\n)\n\n// MexChannelBufferSize is the size of the message exchange channel buffer.\nconst MexChannelBufferSize = mexChannelBufferSize\n\n// SetOnUpdate sets onUpdate for a peer, which is called when the peer's score is\n// updated in all peer lists.\nfunc (p *Peer) SetOnUpdate(f func(*Peer)) {\n\tp.Lock()\n\tp.onUpdate = f\n\tp.Unlock()\n}\n\n// SetRandomSeed seeds all the random number generators in the channel so that\n// tests will be deterministic for a given seed.\nfunc (ch *Channel) SetRandomSeed(seed int64) {\n\tch.Peers().peerHeap.rng.Seed(seed)\n\tpeerRng.Seed(seed)\n\tfor _, sc := range ch.subChannels.subchannels {\n\t\tsc.peers.peerHeap.rng.Seed(seed + int64(len(sc.peers.peersByHostPort)))\n\t}\n}\n\n// Ping exports ping for tests.\nfunc (c *Connection) Ping(ctx context.Context) error {\n\treturn c.ping(ctx)\n}\n\n// Logger returns the logger for the specific connection for tests.\nfunc (c *Connection) Logger() Logger {\n\treturn c.log\n}\n\n// StopHealthCheck exports stopHealthCheck for tests.\nfunc (c *Connection) StopHealthCheck() {\n\tc.stopHealthCheck()\n}\n\n// OutboundConnection returns the underlying connection for an outbound call.\nfunc OutboundConnection(call *OutboundCall) (*Connection, net.Conn) {\n\tconn := call.conn\n\treturn conn, conn.conn\n}\n\n// InboundConnection returns the underlying connection for an incoming call.\nfunc InboundConnection(call IncomingCall) (*Connection, net.Conn) {\n\tinboundCall, ok := call.(*InboundCall)\n\tif !ok {\n\t\treturn nil, nil\n\t}\n\n\tconn := inboundCall.conn\n\treturn conn, inboundCall.Connection()\n}\n"
  },
  {
    "path": "verify_utils_test.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel_test\n\nimport (\n\t\"testing\"\n\n\t. \"github.com/uber/tchannel-go\"\n\n\t\"github.com/uber/tchannel-go/testutils\"\n)\n\n// WithVerifiedServer runs the given test function with a server channel that is verified\n// at the end to make sure there are no leaks (e.g. no exchanges leaked).\nfunc WithVerifiedServer(t *testing.T, opts *testutils.ChannelOpts, f func(serverCh *Channel, hostPort string)) {\n\ttestutils.WithTestServer(t, opts, func(t testing.TB, ts *testutils.TestServer) {\n\t\tf(ts.Server(), ts.HostPort())\n\t})\n}\n"
  },
  {
    "path": "version.go",
    "content": "// Copyright (c) 2015 Uber Technologies, Inc.\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage tchannel\n\n// VersionInfo identifies the version of the TChannel library.\n// Due to lack of proper package management, this version string will\n// be maintained manually.\nconst VersionInfo = \"1.34.6-dev\"\n"
  }
]