[
  {
    "path": ".circleci/config.yml",
    "content": "shared_configs:\n  simple_job_steps: &simple_job_steps\n    - checkout\n    - run:\n        name: Run tests\n        command: |\n          make test\n\n# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference\nversion: 2.1\njobs:\n  build-1-23:\n    working_directory: ~/repo\n    docker:\n      - image: cimg/go:1.23\n    steps: *simple_job_steps\n\n  build-1-24:\n    working_directory: ~/repo\n    docker:\n      - image: cimg/go:1.24\n    steps: *simple_job_steps\n\n  build-1-25:\n    working_directory: ~/repo\n    docker:\n      - image: cimg/go:1.25\n    steps:\n      - checkout\n      - run:\n          name: Run tests and linters\n          command: |\n            make ci\n\nworkflows:\n  pr-build-test:\n    jobs:\n      - build-1-23\n      - build-1-24\n      - build-1-25\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"gomod\"\n    directory: \"/\"\n    # Check for updates once a week\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/codesee-arch-diagram.yml",
    "content": "# This workflow was added by CodeSee. Learn more at https://codesee.io/\n# This is v2.0 of this workflow file\non:\n  push:\n    branches:\n      - master\n  pull_request_target:\n    types: [opened, synchronize, reopened]\n\nname: CodeSee\n\npermissions: read-all\n\njobs:\n  codesee:\n    runs-on: ubuntu-latest\n    continue-on-error: true\n    name: Analyze the repo with CodeSee\n    steps:\n      - uses: Codesee-io/codesee-action@v2\n        with:\n          codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "dist/\n.idea/\nVERSION\n.tmp/\n*.snap\n"
  },
  {
    "path": ".goreleaser.yml",
    "content": "builds:\n  - binary: grpcurl\n    main: ./cmd/grpcurl\n    goos:\n      - linux\n      - darwin\n      - windows\n    goarch:\n      - amd64\n      - 386\n      - arm\n      - arm64\n      - s390x\n      - ppc64le\n    goarm:\n      - 5\n      - 6\n      - 7\n    ignore:\n      - goos: darwin\n        goarch: 386\n      - goos: windows\n        goarch: arm64\n      - goos: darwin\n        goarch: arm\n      - goos: windows\n        goarch: arm\n      - goos: darwin\n        goarch: s390x\n      - goos: windows\n        goarch: s390x\n      - goos: darwin\n        goarch: ppc64le\n      - goos: windows\n        goarch: ppc64le\n    ldflags:\n      - -s -w -X main.version=v{{.Version}}\n\narchives:\n  - format: tar.gz\n    name_template: >-\n      {{ .Binary }}_{{ .Version }}_\n      {{- if eq .Os \"darwin\" }}osx{{ else }}{{ .Os }}{{ end }}_\n      {{- if eq .Arch \"amd64\" }}x86_64\n      {{- else if eq .Arch \"386\" }}x86_32\n      {{- else }}{{ .Arch }}{{ end }}\n      {{- with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 \"v1\") }}{{ .Amd64 }}{{ end }}\n    format_overrides:\n      - goos: windows\n        format: zip\n    files:\n      - LICENSE\n\nnfpms:\n  - vendor: Fullstory\n    homepage: https://github.com/fullstorydev/grpcurl/\n    maintainer: Engineering at Fullstory  <fixme@fixme>\n    description: 'Like cURL, but for gRPC: Command-line tool for interacting with gRPC servers'\n    license: MIT\n    id: nfpms\n    formats:\n      - deb\n      - rpm\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM golang:1.25-alpine AS builder\nLABEL maintainer=\"Fullstory Engineering\"\n\n# create non-privileged group and user\nRUN addgroup -S grpcurl && adduser -S grpcurl -G grpcurl\n\nWORKDIR /tmp/fullstorydev/grpcurl\n# copy just the files/sources we need to build grpcurl\nCOPY VERSION *.go go.* /tmp/fullstorydev/grpcurl/\nCOPY cmd /tmp/fullstorydev/grpcurl/cmd\n# and build a completely static binary (so we can use\n# scratch as basis for the final image)\nENV CGO_ENABLED=0\nENV GO111MODULE=on\nRUN go build -o /grpcurl \\\n    -ldflags \"-w -extldflags \\\"-static\\\" -X \\\"main.version=$(cat VERSION)\\\"\" \\\n    ./cmd/grpcurl\n\nFROM alpine:3 AS alpine\nWORKDIR /\nCOPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt\nCOPY --from=builder /etc/passwd /etc/passwd\nCOPY --from=builder /grpcurl /bin/grpcurl\nUSER grpcurl\n\nENTRYPOINT [\"/bin/grpcurl\"]\n\n# New FROM so we have a nice'n'tiny image\nFROM scratch\nWORKDIR /\nCOPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt\nCOPY --from=builder /etc/passwd /etc/passwd\nCOPY --from=builder /grpcurl /bin/grpcurl\nUSER grpcurl\n\nENTRYPOINT [\"/bin/grpcurl\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2017 Fullstory, 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 all\ncopies 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 THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "dev_build_version=$(shell git describe --tags --always --dirty)\n\nexport PATH := $(shell pwd)/.tmp/protoc/bin:$(PATH)\nexport PROTOC_VERSION := 22.0\n# Disable CGO for improved compatibility across distros\nexport CGO_ENABLED=0\nexport GOFLAGS=-trimpath\nexport GOWORK=off\n\n# TODO: run golint and errcheck, but only to catch *new* violations and\n# decide whether to change code or not (e.g. we need to be able to whitelist\n# violations already in the code). They can be useful to catch errors, but\n# they are just too noisy to be a requirement for a CI -- we don't even *want*\n# to fix some of the things they consider to be violations.\n.PHONY: ci\nci: deps checkgofmt checkgenerate vet staticcheck ineffassign predeclared test\n\n.PHONY: deps\ndeps:\n\tgo get -d -v -t ./...\n\tgo mod tidy\n\n.PHONY: updatedeps\nupdatedeps:\n\tgo get -d -v -t -u -f ./...\n\tgo mod tidy\n\n.PHONY: install\ninstall:\n\tgo install -ldflags '-X \"main.version=dev build $(dev_build_version)\"' ./...\n\n.PHONY: release\nrelease:\n\t@go install github.com/goreleaser/goreleaser@v1.21.0\n\tgoreleaser release --clean\n\n.PHONY: docker\ndocker:\n\t@echo $(dev_build_version) > VERSION\n\tdocker build -t fullstorydev/grpcurl:$(dev_build_version) .\n\t@rm VERSION\n\n.PHONY: generate\ngenerate: .tmp/protoc/bin/protoc\n\t@go install google.golang.org/protobuf/cmd/protoc-gen-go@a709e31e5d12\n\t@go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1.0\n\t@go install github.com/jhump/protoreflect/desc/sourceinfo/cmd/protoc-gen-gosrcinfo@v1.14.1\n\tgo generate ./...\n\tgo mod tidy\n\n.PHONY: checkgenerate\ncheckgenerate: generate\n\tgit status --porcelain -- '**/*.go'\n\t@if [ -n \"$$(git status --porcelain -- '**/*.go')\" ]; then \\\n\t\tgit diff -- '**/*.go'; \\\n\t\texit 1; \\\n\tfi\n\n.PHONY: checkgofmt\ncheckgofmt:\n\tgofmt -s -l .\n\t@if [ -n \"$$(gofmt -s -l .)\" ]; then \\\n\t\texit 1; \\\n\tfi\n\n.PHONY: vet\nvet:\n\tgo vet ./...\n\n.PHONY: staticcheck\nstaticcheck:\n\t@go install honnef.co/go/tools/cmd/staticcheck@2025.1.1\n\tstaticcheck -checks \"inherit,-SA1019\" ./...\n\n.PHONY: ineffassign\nineffassign:\n\t@go install github.com/gordonklaus/ineffassign@7953dde2c7bf\n\tineffassign .\n\n.PHONY: predeclared\npredeclared:\n\t@go install github.com/nishanths/predeclared@51e8c974458a0f93dc03fe356f91ae1a6d791e6f\n\tpredeclared ./...\n\n# Intentionally omitted from CI, but target here for ad-hoc reports.\n.PHONY: golint\ngolint:\n\t@go install golang.org/x/lint/golint@v0.0.0-20210508222113-6edffad5e616\n\tgolint -min_confidence 0.9 -set_exit_status ./...\n\n# Intentionally omitted from CI, but target here for ad-hoc reports.\n.PHONY: errcheck\nerrcheck:\n\t@go install github.com/kisielk/errcheck@v1.2.0\n\terrcheck ./...\n\n.PHONY: test\ntest: deps\n\tCGO_ENABLED=1 go test -race ./...\n\n.tmp/protoc/bin/protoc: ./Makefile ./download_protoc.sh\n\t./download_protoc.sh\n\n"
  },
  {
    "path": "README.md",
    "content": "# gRPCurl\n[![Build Status](https://circleci.com/gh/fullstorydev/grpcurl/tree/master.svg?style=svg)](https://circleci.com/gh/fullstorydev/grpcurl/tree/master)\n[![Go Report Card](https://goreportcard.com/badge/github.com/fullstorydev/grpcurl)](https://goreportcard.com/report/github.com/fullstorydev/grpcurl)\n[![Snap Release Status](https://snapcraft.io/grpcurl/badge.svg)](https://snapcraft.io/grpcurl)\n\n`grpcurl` is a command-line tool that lets you interact with gRPC servers. It's\nbasically `curl` for gRPC servers.\n\nThe main purpose for this tool is to invoke RPC methods on a gRPC server from the\ncommand-line. gRPC servers use a binary encoding on the wire\n([protocol buffers](https://developers.google.com/protocol-buffers/), or \"protobufs\"\nfor short). So they are basically impossible to interact with using regular `curl`\n(and older versions of `curl` that do not support HTTP/2 are of course non-starters).\nThis program accepts messages using JSON encoding, which is much more friendly for both\nhumans and scripts.\n\nWith this tool you can also browse the schema for gRPC services, either by querying\na server that supports [server reflection](https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1/reflection.proto),\nby reading proto source files, or by loading in compiled \"protoset\" files (files that contain\nencoded file [descriptor protos](https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto)).\nIn fact, the way the tool transforms JSON request data into a binary encoded protobuf\nis using that very same schema. So, if the server you interact with does not support\nreflection, you will either need the proto source files that define the service or need\nprotoset files that `grpcurl` can use.\n\nThis repo also provides a library package, `github.com/fullstorydev/grpcurl`, that has\nfunctions for simplifying the construction of other command-line tools that dynamically\ninvoke gRPC endpoints. This code is a great example of how to use the various packages of\nthe [protoreflect](https://godoc.org/github.com/jhump/protoreflect) library, and shows\noff what they can do.\n\nSee also the [`grpcurl` talk at GopherCon 2018](https://www.youtube.com/watch?v=dDr-8kbMnaw).\n\n## Features\n`grpcurl` supports all kinds of RPC methods, including streaming methods. You can even\noperate bi-directional streaming methods interactively by running `grpcurl` from an\ninteractive terminal and using stdin as the request body!\n\n`grpcurl` supports both secure/TLS servers _and_ plain-text servers (i.e. no TLS) and has\nnumerous options for TLS configuration. It also supports mutual TLS, where the client is\nrequired to present a client certificate.\n\nAs mentioned above, `grpcurl` works seamlessly if the server supports the reflection\nservice. If not, you can supply the `.proto` source files or you can supply protoset\nfiles (containing compiled descriptors, produced by `protoc`) to `grpcurl`.\n\n## Installation\n\n### Binaries\n\nDownload the binary from the [releases](https://github.com/fullstorydev/grpcurl/releases) page.\n\n### Homebrew (macOS)\n\nOn macOS, `grpcurl` is available via Homebrew:\n```shell\nbrew install grpcurl\n```\n\n### Docker\n\nFor platforms that support Docker, you can download an image that lets you run `grpcurl`:\n```shell\n# Download image\ndocker pull fullstorydev/grpcurl:latest\n# Run the tool\ndocker run fullstorydev/grpcurl api.grpc.me:443 list\n```\nNote that there are some pitfalls when using docker:\n- If you need to interact with a server listening on the host's loopback network, you must specify the host as `host.docker.internal` instead of `localhost` (for Mac or Windows) _OR_ have the container use the host network with `-network=\"host\"` (Linux only).\n- If you need to provide proto source files or descriptor sets, you must mount the folder containing the files as a volume (`-v $(pwd):/protos`) and adjust the import paths to container paths accordingly.\n- If you want to provide the request message via stdin, using the `-d @` option, you need to use the `-i` flag on the docker command.\n\n### Other Packages\n\nThere are numerous other ways to install `grpcurl`, thanks to support from third parties that\nhave created recipes/packages for it. These include other ways to install `grpcurl` on a variety\nof environments, including Windows and myriad Linux distributions.\n\nYou can see more details and the full list of other packages for `grpcurl` at _repology.org_:\nhttps://repology.org/project/grpcurl/information\n\n### Snap\n\nYou can install `grpcurl` using the snap package:\n\n`snap install grpcurl`\n\n### From Source\nIf you already have the [Go SDK](https://golang.org/doc/install) installed, you can use the `go`\ntool to install `grpcurl`:\n```shell\ngo install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest\n```\n\nThis installs the command into the `bin` sub-folder of wherever your `$GOPATH`\nenvironment variable points. (If you have no `GOPATH` environment variable set,\nthe default install location is `$HOME/go/bin`). If this directory is already in\nyour `$PATH`, then you should be good to go.\n\nIf you have already pulled down this repo to a location that is not in your\n`$GOPATH` and want to build from the sources, you can `cd` into the repo and then\nrun `make install`.\n\nIf you encounter compile errors and are using a version of the Go SDK older than 1.13,\nyou could have out-dated versions of `grpcurl`'s dependencies. You can update the\ndependencies by running `make updatedeps`. Or, if you are using Go 1.11 or 1.12, you\ncan add `GO111MODULE=on` as a prefix to the commands above, which will also build using\nthe right versions of dependencies (vs. whatever you may already have in your `GOPATH`).\n\n## Usage\nThe usage doc for the tool explains the numerous options:\n```shell\ngrpcurl -help\n```\n\nIn the sections below, you will find numerous examples demonstrating how to use\n`grpcurl`.\n\n### Invoking RPCs\nInvoking an RPC on a trusted server (e.g. TLS without self-signed key or custom CA)\nthat requires no client certs and supports server reflection is the simplest thing to\ndo with `grpcurl`. This minimal invocation sends an empty request body:\n```shell\ngrpcurl grpc.server.com:443 my.custom.server.Service/Method\n\n# no TLS\ngrpcurl -plaintext grpc.server.com:80 my.custom.server.Service/Method\n```\n\nTo send a non-empty request, use the `-d` argument. Note that all arguments must come\n*before* the server address and method name:\n```shell\ngrpcurl -d '{\"id\": 1234, \"tags\": [\"foo\",\"bar\"]}' \\\n    grpc.server.com:443 my.custom.server.Service/Method\n```\n\nAs can be seen in the example, the supplied body must be in JSON format. The body will\nbe parsed and then transmitted to the server in the protobuf binary format.\n\nIf you want to include `grpcurl` in a command pipeline, such as when using `jq` to\ncreate a request body, you can use `-d @`, which tells `grpcurl` to read the actual\nrequest body from stdin:\n```shell\ngrpcurl -d @ grpc.server.com:443 my.custom.server.Service/Method <<EOM\n{\n  \"id\": 1234,\n  \"tags\": [\n    \"foor\",\n    \"bar\"\n  ]\n}\nEOM\n```\n### Adding Headers/Metadata to Request\nAdding of headers / metadata to a rpc request is possible via the `-H name:value` command line option. Multiple headers can be added in a similar fashion.\nExample :\n```shell\ngrpcurl -H header1:value1 -H header2:value2 -d '{\"id\": 1234, \"tags\": [\"foo\",\"bar\"]}' grpc.server.com:443 my.custom.server.Service/Method\n```\nFor more usage guide, check out the help docs via `grpcurl -help`\n\n### Listing Services\nTo list all services exposed by a server, use the \"list\" verb. When using `.proto` source\nor protoset files instead of server reflection, this lists all services defined in the\nsource or protoset files.\n```shell\n# Server supports reflection\ngrpcurl localhost:8787 list\n\n# Using compiled protoset files\ngrpcurl -protoset my-protos.bin list\n\n# Using proto sources\ngrpcurl -import-path ../protos -proto my-stuff.proto list\n\n# Export proto files (use -proto-out-dir to specify the output directory)\ngrpcurl -plaintext -proto-out-dir \"out_protos\" \"localhost:8787\" describe my.custom.server.Service\n\n# Export protoset file (use -protoset-out to specify the output file)\ngrpcurl -plaintext -protoset-out \"out.protoset\" \"localhost:8787\" describe my.custom.server.Service\n\n```\n\nThe \"list\" verb also lets you see all methods in a particular service:\n```shell\ngrpcurl localhost:8787 list my.custom.server.Service\n```\n\n### Describing Elements\nThe \"describe\" verb will print the type of any symbol that the server knows about\nor that is found in a given protoset file. It also prints a description of that\nsymbol, in the form of snippets of proto source. It won't necessarily be the\noriginal source that defined the element, but it will be equivalent.\n\n```shell\n# Server supports reflection\ngrpcurl localhost:8787 describe my.custom.server.Service.MethodOne\n\n# Using compiled protoset files\ngrpcurl -protoset my-protos.bin describe my.custom.server.Service.MethodOne\n\n# Using proto sources\ngrpcurl -import-path ../protos -proto my-stuff.proto describe my.custom.server.Service.MethodOne\n```\n\n## Descriptor Sources\nThe `grpcurl` tool can operate on a variety of sources for descriptors. The descriptors\nare required, in order for `grpcurl` to understand the RPC schema, translate inputs\ninto the protobuf binary format as well as translate responses from the binary format\ninto text. The sections below document the supported sources and what command-line flags\nare needed to use them.\n\n### Server Reflection\n\nWithout any additional command-line flags, `grpcurl` will try to use [server reflection](https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1/reflection.proto).\n\nExamples for how to set up server reflection can be found [here](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md#known-implementations).\n\nWhen using reflection, the server address (host:port or path to Unix socket) is required\neven for \"list\" and \"describe\" operations, so that `grpcurl` can connect to the server\nand ask it for its descriptors.\n\n### Proto Source Files\nTo use `grpcurl` on servers that do not support reflection, you can use `.proto` source\nfiles.\n\nIn addition to using `-proto` flags to point `grpcurl` at the relevant proto source file(s),\nyou may also need to supply `-import-path` flags to tell `grpcurl` the folders from which\ndependencies can be imported.\n\nJust like when compiling with `protoc`, you do *not* need to provide an import path for the\nlocation of the standard protos included with `protoc` (which contain various \"well-known\ntypes\" with a package definition of `google.protobuf`). These files are \"known\" by `grpcurl`\nas a snapshot of their descriptors is built into the `grpcurl` binary.\n\nWhen using proto sources, you can omit the server address (host:port or path to Unix socket)\nwhen using the \"list\" and \"describe\" operations since they only need to consult the proto\nsource files.\n\n### Protoset Files\nYou can also use compiled protoset files with `grpcurl`. If you are scripting `grpcurl` and\nneed to re-use the same proto sources for many invocations, you will see better performance\nby using protoset files (since it skips the parsing and compilation steps with each\ninvocation).\n\nProtoset files contain binary encoded `google.protobuf.FileDescriptorSet` protos. To create\na protoset file, invoke `protoc` with the `*.proto` files that define the service:\n```shell\nprotoc --proto_path=. \\\n    --descriptor_set_out=myservice.protoset \\\n    --include_imports \\\n    my/custom/server/service.proto\n```\n\nThe `--descriptor_set_out` argument is what tells `protoc` to produce a protoset,\nand the `--include_imports` argument is necessary for the protoset to contain\neverything that `grpcurl` needs to process and understand the schema.\n\nWhen using protosets, you can omit the server address (host:port or path to Unix socket)\nwhen using the \"list\" and \"describe\" operations since they only need to consult the\nprotoset files.\n\n"
  },
  {
    "path": "cmd/grpcurl/grpcurl.go",
    "content": "// Command grpcurl makes gRPC requests (a la cURL, but HTTP/2). It can use a supplied descriptor\n// file, protobuf sources, or service reflection to translate JSON or text request data into the\n// appropriate protobuf messages and vice versa for presenting the response contents.\npackage main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/jhump/protoreflect/desc\" //lint:ignore SA1019 required to use APIs in other grpcurl package\n\t\"github.com/jhump/protoreflect/grpcreflect\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/credentials\"\n\t\"google.golang.org/grpc/credentials/alts\"\n\t\"google.golang.org/grpc/keepalive\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/types/descriptorpb\"\n\n\t// Register gzip compressor so compressed responses will work\n\t_ \"google.golang.org/grpc/encoding/gzip\"\n\t// Register xds so xds and xds-experimental resolver schemes work\n\t_ \"google.golang.org/grpc/xds\"\n\n\t\"github.com/fullstorydev/grpcurl\"\n)\n\n// To avoid confusion between program error codes and the gRPC response\n// status codes 'Cancelled' and 'Unknown', 1 and 2 respectively,\n// the response status codes emitted use an offset of 64\nconst statusCodeOffset = 64\n\nconst noVersion = \"dev build <no version set>\"\n\nvar version = noVersion\n\nvar (\n\texit = os.Exit\n\n\tisUnixSocket func() bool // nil when run on non-unix platform\n\n\tflags = flag.NewFlagSet(os.Args[0], flag.ExitOnError)\n\n\thelp = flags.Bool(\"help\", false, prettify(`\n\t\tPrint usage instructions and exit.`))\n\tprintVersion = flags.Bool(\"version\", false, prettify(`\n\t\tPrint version.`))\n\n\tplaintext = flags.Bool(\"plaintext\", false, prettify(`\n\t\tUse plain-text HTTP/2 when connecting to server (no TLS).`))\n\tinsecure = flags.Bool(\"insecure\", false, prettify(`\n\t\tSkip server certificate and domain verification. (NOT SECURE!) Not\n\t\tvalid with -plaintext option.`))\n\n\t// TLS Options\n\tcacert = flags.String(\"cacert\", \"\", prettify(`\n\t\tFile containing trusted root certificates for verifying the server.\n\t\tIgnored if -insecure is specified.`))\n\tcert = flags.String(\"cert\", \"\", prettify(`\n\t\tFile containing client certificate (public key), to present to the\n\t\tserver. Not valid with -plaintext option. Must also provide -key option.`))\n\tkey = flags.String(\"key\", \"\", prettify(`\n\t\tFile containing client private key, to present to the server. Not valid\n\t\twith -plaintext option. Must also provide -cert option.`))\n\n\t// ALTS Options\n\tusealts = flags.Bool(\"alts\", false, prettify(`\n\t\tUse Application Layer Transport Security (ALTS) when connecting to server.`))\n\taltsHandshakerServiceAddress = flags.String(\"alts-handshaker-service\", \"\", prettify(`If set, this server will be used to do the ATLS handshaking.`))\n\taltsTargetServiceAccounts    multiString\n\n\tprotoset      multiString\n\tprotoFiles    multiString\n\timportPaths   multiString\n\taddlHeaders   multiString\n\trpcHeaders    multiString\n\treflHeaders   multiString\n\texpandHeaders = flags.Bool(\"expand-headers\", false, prettify(`\n\t\tIf set, headers may use '${NAME}' syntax to reference environment\n\t\tvariables. These will be expanded to the actual environment variable\n\t\tvalue before sending to the server. For example, if there is an\n\t\tenvironment variable defined like FOO=bar, then a header of\n\t\t'key: ${FOO}' would expand to 'key: bar'. This applies to -H,\n\t\t-rpc-header, and -reflect-header options. No other expansion/escaping is\n\t\tperformed. This can be used to supply credentials/secrets without having\n\t\tto put them in command-line arguments.`))\n\tauthority = flags.String(\"authority\", \"\", prettify(`\n\t\tThe authoritative name of the remote server. This value is passed as the\n\t\tvalue of the \":authority\" pseudo-header in the HTTP/2 protocol. When TLS\n\t\tis used, this will also be used as the server name when verifying the\n\t\tserver's certificate. It defaults to the address that is provided in the\n\t\tpositional arguments, or 'localhost' in the case of a unix domain\n\t\tsocket.`))\n\tuserAgent = flags.String(\"user-agent\", \"\", prettify(`\n\t\tIf set, the specified value will be added to the User-Agent header set\n\t\tby the grpc-go library.\n\t\t`))\n\tdata = flags.String(\"d\", \"\", prettify(`\n\t\tData for request contents. If the value is '@' then the request contents\n\t\tare read from stdin. For calls that accept a stream of requests, the\n\t\tcontents should include all such request messages concatenated together\n\t\t(possibly delimited; see -format).`))\n\tformat = flags.String(\"format\", \"json\", prettify(`\n\t\tThe format of request data. The allowed values are 'json' or 'text'. For\n\t\t'json', the input data must be in JSON format. Multiple request values\n\t\tmay be concatenated (messages with a JSON representation other than\n\t\tobject must be separated by whitespace, such as a newline). For 'text',\n\t\tthe input data must be in the protobuf text format, in which case\n\t\tmultiple request values must be separated by the \"record separator\"\n\t\tASCII character: 0x1E. The stream should not end in a record separator.\n\t\tIf it does, it will be interpreted as a final, blank message after the\n\t\tseparator.`))\n\tallowUnknownFields = flags.Bool(\"allow-unknown-fields\", false, prettify(`\n\t\tWhen true, the request contents, if 'json' format is used, allows\n\t\tunknown fields to be present. They will be ignored when parsing\n\t\tthe request.`))\n\tconnectTimeout = flags.Float64(\"connect-timeout\", 0, prettify(`\n\t\tThe maximum time, in seconds, to wait for connection to be established.\n\t\tDefaults to 10 seconds.`))\n\tformatError = flags.Bool(\"format-error\", false, prettify(`\n\t\tWhen a non-zero status is returned, format the response using the\n\t\tvalue set by the -format flag .`))\n\tkeepaliveTime = flags.Float64(\"keepalive-time\", 0, prettify(`\n\t\tIf present, the maximum idle time in seconds, after which a keepalive\n\t\tprobe is sent. If the connection remains idle and no keepalive response\n\t\tis received for this same period then the connection is closed and the\n\t\toperation fails.`))\n\tmaxTime = flags.Float64(\"max-time\", 0, prettify(`\n\t\tThe maximum total time the operation can take, in seconds. This sets a\n                timeout on the gRPC context, allowing both client and server to give up\n\t\tafter the deadline has past. This is useful for preventing batch jobs\n                that use grpcurl from hanging due to slow or bad network links or due\n\t\tto incorrect stream method usage.`))\n\tmaxMsgSz = flags.Int(\"max-msg-sz\", 0, prettify(`\n\t\tThe maximum encoded size of a response message, in bytes, that grpcurl\n\t\twill accept. If not specified, defaults to 4,194,304 (4 megabytes).`))\n\temitDefaults = flags.Bool(\"emit-defaults\", false, prettify(`\n\t\tEmit default values for JSON-encoded responses.`))\n\tprotosetOut = flags.String(\"protoset-out\", \"\", prettify(`\n\t\tThe name of a file to be written that will contain a FileDescriptorSet\n\t\tproto. With the list and describe verbs, the listed or described\n\t\telements and their transitive dependencies will be written to the named\n\t\tfile if this option is given. When invoking an RPC and this option is\n\t\tgiven, the method being invoked and its transitive dependencies will be\n\t\tincluded in the output file.`))\n\tprotoOut = flags.String(\"proto-out-dir\", \"\", prettify(`\n\t\tThe name of a directory where the generated .proto files will be written.\n\t\tWith the list and describe verbs, the listed or described elements and\n\t\ttheir transitive dependencies will be written as .proto files in the\n\t\tspecified directory if this option is given. When invoking an RPC and\n\t\tthis option is given, the method being invoked and its transitive\n\t\tdependencies will be included in the generated .proto files in the\n\t\toutput directory.`))\n\tmsgTemplate = flags.Bool(\"msg-template\", false, prettify(`\n\t\tWhen describing messages, show a template of input data.`))\n\tverbose = flags.Bool(\"v\", false, prettify(`\n\t\tEnable verbose output.`))\n\tveryVerbose = flags.Bool(\"vv\", false, prettify(`\n\t\tEnable very verbose output (includes timing data).`))\n\tserverName = flags.String(\"servername\", \"\", prettify(`\n\t\tOverride server name when validating TLS certificate. This flag is\n\t\tignored if -plaintext or -insecure is used.\n\t\tNOTE: Prefer -authority. This flag may be removed in the future. It is\n\t\tan error to use both -authority and -servername (though this will be\n\t\tpermitted if they are both set to the same value, to increase backwards\n\t\tcompatibility with earlier releases that allowed both to be set).`))\n\treflection = optionalBoolFlag{val: true}\n)\n\nfunc init() {\n\tflags.Var(&addlHeaders, \"H\", prettify(`\n\t\tAdditional headers in 'name: value' format. May specify more than one\n\t\tvia multiple flags. These headers will also be included in reflection\n\t\trequests to a server.`))\n\tflags.Var(&rpcHeaders, \"rpc-header\", prettify(`\n\t\tAdditional RPC headers in 'name: value' format. May specify more than\n\t\tone via multiple flags. These headers will *only* be used when invoking\n\t\tthe requested RPC method. They are excluded from reflection requests.`))\n\tflags.Var(&reflHeaders, \"reflect-header\", prettify(`\n\t\tAdditional reflection headers in 'name: value' format. May specify more\n\t\tthan one via multiple flags. These headers will *only* be used during\n\t\treflection requests and will be excluded when invoking the requested RPC\n\t\tmethod.`))\n\tflags.Var(&protoset, \"protoset\", prettify(`\n\t\tThe name of a file containing an encoded FileDescriptorSet. This file's\n\t\tcontents will be used to determine the RPC schema instead of querying\n\t\tfor it from the remote server via the gRPC reflection API. When set: the\n\t\t'list' action lists the services found in the given descriptors (vs.\n\t\tthose exposed by the remote server), and the 'describe' action describes\n\t\tsymbols found in the given descriptors. May specify more than one via\n\t\tmultiple -protoset flags. It is an error to use both -protoset and\n\t\t-proto flags.`))\n\tflags.Var(&protoFiles, \"proto\", prettify(`\n\t\tThe name of a proto source file. Source files given will be used to\n\t\tdetermine the RPC schema instead of querying for it from the remote\n\t\tserver via the gRPC reflection API. When set: the 'list' action lists\n\t\tthe services found in the given files and their imports (vs. those\n\t\texposed by the remote server), and the 'describe' action describes\n\t\tsymbols found in the given files. May specify more than one via multiple\n\t\t-proto flags. Imports will be resolved using the given -import-path\n\t\tflags. Multiple proto files can be specified by specifying multiple\n\t\t-proto flags. It is an error to use both -protoset and -proto flags.`))\n\tflags.Var(&importPaths, \"import-path\", prettify(`\n\t\tThe path to a directory from which proto sources can be imported, for\n\t\tuse with -proto flags. Multiple import paths can be configured by\n\t\tspecifying multiple -import-path flags. Paths will be searched in the\n\t\torder given. If no import paths are given, all files (including all\n\t\timports) must be provided as -proto flags, and grpcurl will attempt to\n\t\tresolve all import statements from the set of file names given.`))\n\tflags.Var(&reflection, \"use-reflection\", prettify(`\n\t\tWhen true, server reflection will be used to determine the RPC schema.\n\t\tDefaults to true unless a -proto or -protoset option is provided. If\n\t\t-use-reflection is used in combination with a -proto or -protoset flag,\n\t\tthe provided descriptor sources will be used in addition to server\n\t\treflection to resolve messages and extensions.`))\n\tflags.Var(&altsTargetServiceAccounts, \"alts-target-service-account\", prettify(`\n\t\tThe full email address of the service account that the server is\n\t\texpected to be using when ALTS is used. You can specify this option\n\t\tmultiple times to indicate multiple allowed service accounts. If the\n\t\tserver authenticates with a service account that is not one of the\n\t\texpected accounts, the RPC will not be issued. If no such arguments are\n\t\tprovided, no check will be performed, and the RPC will be issued\n\t\tregardless of the server's service account.`))\n}\n\ntype multiString []string\n\nfunc (s *multiString) String() string {\n\treturn strings.Join(*s, \",\")\n}\n\nfunc (s *multiString) Set(value string) error {\n\t*s = append(*s, value)\n\treturn nil\n}\n\n// Uses a file source as a fallback for resolving symbols and extensions, but\n// only uses the reflection source for listing services\ntype compositeSource struct {\n\treflection grpcurl.DescriptorSource\n\tfile       grpcurl.DescriptorSource\n}\n\nfunc (cs compositeSource) ListServices() ([]string, error) {\n\treturn cs.reflection.ListServices()\n}\n\nfunc (cs compositeSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) {\n\td, err := cs.reflection.FindSymbol(fullyQualifiedName)\n\tif err == nil {\n\t\treturn d, nil\n\t}\n\treturn cs.file.FindSymbol(fullyQualifiedName)\n}\n\nfunc (cs compositeSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) {\n\texts, err := cs.reflection.AllExtensionsForType(typeName)\n\tif err != nil {\n\t\t// On error fall back to file source\n\t\treturn cs.file.AllExtensionsForType(typeName)\n\t}\n\t// Track the tag numbers from the reflection source\n\ttags := make(map[int32]bool)\n\tfor _, ext := range exts {\n\t\ttags[ext.GetNumber()] = true\n\t}\n\tfileExts, err := cs.file.AllExtensionsForType(typeName)\n\tif err != nil {\n\t\treturn exts, nil\n\t}\n\tfor _, ext := range fileExts {\n\t\t// Prioritize extensions found via reflection\n\t\tif !tags[ext.GetNumber()] {\n\t\t\texts = append(exts, ext)\n\t\t}\n\t}\n\treturn exts, nil\n}\n\ntype timingData struct {\n\tTitle  string\n\tStart  time.Time\n\tValue  time.Duration\n\tParent *timingData\n\tSub    []*timingData\n}\n\nfunc (d *timingData) Child(title string) *timingData {\n\tif d == nil {\n\t\treturn nil\n\t}\n\tchild := &timingData{Title: title, Start: time.Now()}\n\td.Sub = append(d.Sub, child)\n\treturn child\n}\n\nfunc (d *timingData) Done() {\n\tif d == nil {\n\t\treturn\n\t}\n\tif d.Value == 0 {\n\t\td.Value = time.Since(d.Start)\n\t}\n}\n\nfunc main() {\n\tflags.Usage = usage\n\tflags.Parse(os.Args[1:])\n\tif *help {\n\t\tusage()\n\t\tos.Exit(0)\n\t}\n\tif *printVersion {\n\t\tfmt.Fprintf(os.Stderr, \"%s %s\\n\", filepath.Base(os.Args[0]), version)\n\t\tos.Exit(0)\n\t}\n\n\t// default behavior is to use tls\n\tusetls := !*plaintext && !*usealts\n\n\t// Do extra validation on arguments and figure out what user asked us to do.\n\tif *connectTimeout < 0 {\n\t\tfail(nil, \"The -connect-timeout argument must not be negative.\")\n\t}\n\tif *keepaliveTime < 0 {\n\t\tfail(nil, \"The -keepalive-time argument must not be negative.\")\n\t}\n\tif *maxTime < 0 {\n\t\tfail(nil, \"The -max-time argument must not be negative.\")\n\t}\n\tif *maxMsgSz < 0 {\n\t\tfail(nil, \"The -max-msg-sz argument must not be negative.\")\n\t}\n\tif *plaintext && *usealts {\n\t\tfail(nil, \"The -plaintext and -alts arguments are mutually exclusive.\")\n\t}\n\tif *insecure && !usetls {\n\t\tfail(nil, \"The -insecure argument can only be used with TLS.\")\n\t}\n\tif *cert != \"\" && !usetls {\n\t\tfail(nil, \"The -cert argument can only be used with TLS.\")\n\t}\n\tif *key != \"\" && !usetls {\n\t\tfail(nil, \"The -key argument can only be used with TLS.\")\n\t}\n\tif (*key == \"\") != (*cert == \"\") {\n\t\tfail(nil, \"The -cert and -key arguments must be used together and both be present.\")\n\t}\n\tif *altsHandshakerServiceAddress != \"\" && !*usealts {\n\t\tfail(nil, \"The -alts-handshaker-service argument must be used with the -alts argument.\")\n\t}\n\tif len(altsTargetServiceAccounts) > 0 && !*usealts {\n\t\tfail(nil, \"The -alts-target-service-account argument must be used with the -alts argument.\")\n\t}\n\tif *format != \"json\" && *format != \"text\" {\n\t\tfail(nil, \"The -format option must be 'json' or 'text'.\")\n\t}\n\tif *emitDefaults && *format != \"json\" {\n\t\twarn(\"The -emit-defaults is only used when using json format.\")\n\t}\n\n\targs := flags.Args()\n\n\tif len(args) == 0 {\n\t\tfail(nil, \"Too few arguments.\")\n\t}\n\tvar target string\n\tif args[0] != \"list\" && args[0] != \"describe\" {\n\t\ttarget = args[0]\n\t\targs = args[1:]\n\t}\n\n\tif len(args) == 0 {\n\t\tfail(nil, \"Too few arguments.\")\n\t}\n\tvar list, describe, invoke bool\n\tif args[0] == \"list\" {\n\t\tlist = true\n\t\targs = args[1:]\n\t} else if args[0] == \"describe\" {\n\t\tdescribe = true\n\t\targs = args[1:]\n\t} else {\n\t\tinvoke = true\n\t}\n\n\tverbosityLevel := 0\n\tif *verbose {\n\t\tverbosityLevel = 1\n\t}\n\n\tvar rootTiming *timingData\n\tif *veryVerbose {\n\t\tverbosityLevel = 2\n\n\t\trootTiming = &timingData{Title: \"Timing Data\", Start: time.Now()}\n\t\tdefer func() {\n\t\t\trootTiming.Done()\n\t\t\tdumpTiming(rootTiming, 0)\n\t\t}()\n\t}\n\n\tvar symbol string\n\tif invoke {\n\t\tif len(args) == 0 {\n\t\t\tfail(nil, \"Too few arguments.\")\n\t\t}\n\t\tsymbol = args[0]\n\t\targs = args[1:]\n\t} else {\n\t\tif *data != \"\" {\n\t\t\twarn(\"The -d argument is not used with 'list' or 'describe' verb.\")\n\t\t}\n\t\tif len(rpcHeaders) > 0 {\n\t\t\twarn(\"The -rpc-header argument is not used with 'list' or 'describe' verb.\")\n\t\t}\n\t\tif len(args) > 0 {\n\t\t\tsymbol = args[0]\n\t\t\targs = args[1:]\n\t\t}\n\t}\n\n\tif len(args) > 0 {\n\t\tfail(nil, \"Too many arguments.\")\n\t}\n\tif invoke && target == \"\" {\n\t\tfail(nil, \"No host:port specified.\")\n\t}\n\tif len(protoset) == 0 && len(protoFiles) == 0 && target == \"\" {\n\t\tfail(nil, \"No host:port specified, no protoset specified, and no proto sources specified.\")\n\t}\n\tif len(protoset) > 0 && len(reflHeaders) > 0 {\n\t\twarn(\"The -reflect-header argument is not used when -protoset files are used.\")\n\t}\n\tif len(protoset) > 0 && len(protoFiles) > 0 {\n\t\tfail(nil, \"Use either -protoset files or -proto files, but not both.\")\n\t}\n\tif len(importPaths) > 0 && len(protoFiles) == 0 {\n\t\twarn(\"The -import-path argument is not used unless -proto files are used.\")\n\t}\n\tif !reflection.val && len(protoset) == 0 && len(protoFiles) == 0 {\n\t\tfail(nil, \"No protoset files or proto files specified and -use-reflection set to false.\")\n\t}\n\n\t// Protoset or protofiles provided and -use-reflection unset\n\tif !reflection.set && (len(protoset) > 0 || len(protoFiles) > 0) {\n\t\treflection.val = false\n\t}\n\n\tctx := context.Background()\n\tif *maxTime > 0 {\n\t\ttimeout := floatSecondsToDuration(*maxTime)\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, timeout)\n\t\tdefer cancel()\n\t}\n\n\tdial := func() *grpc.ClientConn {\n\t\tdialTiming := rootTiming.Child(\"Dial\")\n\t\tdefer dialTiming.Done()\n\t\tdialTime := 10 * time.Second\n\t\tif *connectTimeout > 0 {\n\t\t\tdialTime = floatSecondsToDuration(*connectTimeout)\n\t\t}\n\t\tctx, cancel := context.WithTimeout(ctx, dialTime)\n\t\tdefer cancel()\n\t\tvar opts []grpc.DialOption\n\t\tif *keepaliveTime > 0 {\n\t\t\ttimeout := floatSecondsToDuration(*keepaliveTime)\n\t\t\topts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{\n\t\t\t\tTime:    timeout,\n\t\t\t\tTimeout: timeout,\n\t\t\t}))\n\t\t}\n\t\tif *maxMsgSz > 0 {\n\t\t\topts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*maxMsgSz)))\n\t\t}\n\t\tif isUnixSocket != nil && isUnixSocket() && !strings.HasPrefix(target, \"unix://\") {\n\t\t\t// prepend unix:// to the address if it's not already there\n\t\t\t// this is to maintain backwards compatibility because the custom dialer is replaced by\n\t\t\t// the default dialer in grpc-go.\n\t\t\t// https://github.com/fullstorydev/grpcurl/pull/480\n\t\t\ttarget = \"unix://\" + target\n\t\t}\n\t\tvar creds credentials.TransportCredentials\n\t\tif *plaintext {\n\t\t\tif *authority != \"\" {\n\t\t\t\topts = append(opts, grpc.WithAuthority(*authority))\n\t\t\t}\n\t\t} else if *usealts {\n\t\t\tclientOptions := alts.DefaultClientOptions()\n\t\t\tif len(altsTargetServiceAccounts) > 0 {\n\t\t\t\tclientOptions.TargetServiceAccounts = altsTargetServiceAccounts\n\t\t\t}\n\t\t\tif *altsHandshakerServiceAddress != \"\" {\n\t\t\t\tclientOptions.HandshakerServiceAddress = *altsHandshakerServiceAddress\n\t\t\t}\n\t\t\tcreds = alts.NewClientCreds(clientOptions)\n\t\t} else if usetls {\n\t\t\ttlsTiming := dialTiming.Child(\"TLS Setup\")\n\t\t\tdefer tlsTiming.Done()\n\n\t\t\ttlsConf, err := grpcurl.ClientTLSConfig(*insecure, *cacert, *cert, *key)\n\t\t\tif err != nil {\n\t\t\t\tfail(err, \"Failed to create TLS config\")\n\t\t\t}\n\n\t\t\tsslKeylogFile := os.Getenv(\"SSLKEYLOGFILE\")\n\t\t\tif sslKeylogFile != \"\" {\n\t\t\t\tw, err := os.OpenFile(sslKeylogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)\n\t\t\t\tif err != nil {\n\t\t\t\t\tfail(err, \"Could not open SSLKEYLOGFILE %s\", sslKeylogFile)\n\t\t\t\t}\n\t\t\t\ttlsConf.KeyLogWriter = w\n\t\t\t}\n\n\t\t\tcreds = credentials.NewTLS(tlsConf)\n\n\t\t\t// can use either -servername or -authority; but not both\n\t\t\tif *serverName != \"\" && *authority != \"\" {\n\t\t\t\tif *serverName == *authority {\n\t\t\t\t\twarn(\"Both -servername and -authority are present; prefer only -authority.\")\n\t\t\t\t} else {\n\t\t\t\t\tfail(nil, \"Cannot specify different values for -servername and -authority.\")\n\t\t\t\t}\n\t\t\t}\n\t\t\toverrideName := *serverName\n\t\t\tif overrideName == \"\" {\n\t\t\t\toverrideName = *authority\n\t\t\t}\n\n\t\t\tif overrideName != \"\" {\n\t\t\t\topts = append(opts, grpc.WithAuthority(overrideName))\n\t\t\t}\n\t\t\ttlsTiming.Done()\n\t\t} else {\n\t\t\tpanic(\"Should have defaulted to use TLS.\")\n\t\t}\n\n\t\tgrpcurlUA := \"grpcurl/\" + version\n\t\tif version == noVersion {\n\t\t\tgrpcurlUA = \"grpcurl/dev-build (no version set)\"\n\t\t}\n\t\tif *userAgent != \"\" {\n\t\t\tgrpcurlUA = *userAgent + \" \" + grpcurlUA\n\t\t}\n\t\topts = append(opts, grpc.WithUserAgent(grpcurlUA))\n\n\t\tblockingDialTiming := dialTiming.Child(\"BlockingDial\")\n\t\tdefer blockingDialTiming.Done()\n\t\tcc, err := grpcurl.BlockingDial(ctx, \"\", target, creds, opts...)\n\t\tif err != nil {\n\t\t\tfail(err, \"Failed to dial target host %q\", target)\n\t\t}\n\t\treturn cc\n\t}\n\tprintFormattedStatus := func(w io.Writer, stat *status.Status, formatter grpcurl.Formatter) {\n\t\tformattedStatus, err := formatter(stat.Proto())\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(w, \"ERROR: %v\", err.Error())\n\t\t}\n\t\tfmt.Fprint(w, formattedStatus)\n\t}\n\n\tif *expandHeaders {\n\t\tvar err error\n\t\taddlHeaders, err = grpcurl.ExpandHeaders(addlHeaders)\n\t\tif err != nil {\n\t\t\tfail(err, \"Failed to expand additional headers\")\n\t\t}\n\t\trpcHeaders, err = grpcurl.ExpandHeaders(rpcHeaders)\n\t\tif err != nil {\n\t\t\tfail(err, \"Failed to expand rpc headers\")\n\t\t}\n\t\treflHeaders, err = grpcurl.ExpandHeaders(reflHeaders)\n\t\tif err != nil {\n\t\t\tfail(err, \"Failed to expand reflection headers\")\n\t\t}\n\t}\n\n\tvar cc *grpc.ClientConn\n\tvar descSource grpcurl.DescriptorSource\n\tvar refClient *grpcreflect.Client\n\tvar fileSource grpcurl.DescriptorSource\n\tif len(protoset) > 0 {\n\t\tvar err error\n\t\tfileSource, err = grpcurl.DescriptorSourceFromProtoSets(protoset...)\n\t\tif err != nil {\n\t\t\tfail(err, \"Failed to process proto descriptor sets.\")\n\t\t}\n\t} else if len(protoFiles) > 0 {\n\t\tvar err error\n\t\tfileSource, err = grpcurl.DescriptorSourceFromProtoFiles(importPaths, protoFiles...)\n\t\tif err != nil {\n\t\t\tfail(err, \"Failed to process proto source files.\")\n\t\t}\n\t}\n\tif reflection.val {\n\t\tmd := grpcurl.MetadataFromHeaders(append(addlHeaders, reflHeaders...))\n\t\trefCtx := metadata.NewOutgoingContext(ctx, md)\n\t\tcc = dial()\n\t\trefClient = grpcreflect.NewClientAuto(refCtx, cc)\n\t\trefClient.AllowMissingFileDescriptors()\n\t\treflSource := grpcurl.DescriptorSourceFromServer(ctx, refClient)\n\t\tif fileSource != nil {\n\t\t\tdescSource = compositeSource{reflSource, fileSource}\n\t\t} else {\n\t\t\tdescSource = reflSource\n\t\t}\n\t} else {\n\t\tdescSource = fileSource\n\t}\n\n\t// arrange for the RPCs to be cleanly shutdown\n\treset := func() {\n\t\tif refClient != nil {\n\t\t\trefClient.Reset()\n\t\t\trefClient = nil\n\t\t}\n\t\tif cc != nil {\n\t\t\tcc.Close()\n\t\t\tcc = nil\n\t\t}\n\t}\n\tdefer reset()\n\texit = func(code int) {\n\t\t// since defers aren't run by os.Exit...\n\t\treset()\n\t\tos.Exit(code)\n\t}\n\n\tif list {\n\t\tif symbol == \"\" {\n\t\t\tsvcs, err := grpcurl.ListServices(descSource)\n\t\t\tif err != nil {\n\t\t\t\tfail(err, \"Failed to list services\")\n\t\t\t}\n\t\t\tif len(svcs) == 0 {\n\t\t\t\tfmt.Println(\"(No services)\")\n\t\t\t} else {\n\t\t\t\tfor _, svc := range svcs {\n\t\t\t\t\tfmt.Printf(\"%s\\n\", svc)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err := writeProtoset(descSource, svcs...); err != nil {\n\t\t\t\tfail(err, \"Failed to write protoset to %s\", *protosetOut)\n\t\t\t}\n\t\t\tif err := writeProtos(descSource, svcs...); err != nil {\n\t\t\t\tfail(err, \"Failed to write protos to %s\", *protoOut)\n\t\t\t}\n\t\t} else {\n\t\t\tmethods, err := grpcurl.ListMethods(descSource, symbol)\n\t\t\tif err != nil {\n\t\t\t\tfail(err, \"Failed to list methods for service %q\", symbol)\n\t\t\t}\n\t\t\tif len(methods) == 0 {\n\t\t\t\tfmt.Println(\"(No methods)\") // probably unlikely\n\t\t\t} else {\n\t\t\t\tfor _, m := range methods {\n\t\t\t\t\tfmt.Printf(\"%s\\n\", m)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err := writeProtoset(descSource, symbol); err != nil {\n\t\t\t\tfail(err, \"Failed to write protoset to %s\", *protosetOut)\n\t\t\t}\n\t\t\tif err := writeProtos(descSource, symbol); err != nil {\n\t\t\t\tfail(err, \"Failed to write protos to %s\", *protoOut)\n\t\t\t}\n\t\t}\n\n\t} else if describe {\n\t\tvar symbols []string\n\t\tif symbol != \"\" {\n\t\t\tsymbols = []string{symbol}\n\t\t} else {\n\t\t\t// if no symbol given, describe all exposed services\n\t\t\tsvcs, err := descSource.ListServices()\n\t\t\tif err != nil {\n\t\t\t\tfail(err, \"Failed to list services\")\n\t\t\t}\n\t\t\tif len(svcs) == 0 {\n\t\t\t\tfmt.Println(\"Server returned an empty list of exposed services\")\n\t\t\t}\n\t\t\tsymbols = svcs\n\t\t}\n\t\tfor _, s := range symbols {\n\t\t\tif s[0] == '.' {\n\t\t\t\ts = s[1:]\n\t\t\t}\n\n\t\t\tdsc, err := descSource.FindSymbol(s)\n\t\t\tif err != nil {\n\t\t\t\tfail(err, \"Failed to resolve symbol %q\", s)\n\t\t\t}\n\n\t\t\tfqn := dsc.GetFullyQualifiedName()\n\t\t\tvar elementType string\n\t\t\tswitch d := dsc.(type) {\n\t\t\tcase *desc.MessageDescriptor:\n\t\t\t\telementType = \"a message\"\n\t\t\t\tparent, ok := d.GetParent().(*desc.MessageDescriptor)\n\t\t\t\tif ok {\n\t\t\t\t\tif d.IsMapEntry() {\n\t\t\t\t\t\tfor _, f := range parent.GetFields() {\n\t\t\t\t\t\t\tif f.IsMap() && f.GetMessageType() == d {\n\t\t\t\t\t\t\t\t// found it: describe the map field instead\n\t\t\t\t\t\t\t\telementType = \"the entry type for a map field\"\n\t\t\t\t\t\t\t\tdsc = f\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// see if it's a group\n\t\t\t\t\t\tfor _, f := range parent.GetFields() {\n\t\t\t\t\t\t\tif f.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP && f.GetMessageType() == d {\n\t\t\t\t\t\t\t\t// found it: describe the map field instead\n\t\t\t\t\t\t\t\telementType = \"the type of a group field\"\n\t\t\t\t\t\t\t\tdsc = f\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase *desc.FieldDescriptor:\n\t\t\t\telementType = \"a field\"\n\t\t\t\tif d.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP {\n\t\t\t\t\telementType = \"a group field\"\n\t\t\t\t} else if d.IsExtension() {\n\t\t\t\t\telementType = \"an extension\"\n\t\t\t\t}\n\t\t\tcase *desc.OneOfDescriptor:\n\t\t\t\telementType = \"a one-of\"\n\t\t\tcase *desc.EnumDescriptor:\n\t\t\t\telementType = \"an enum\"\n\t\t\tcase *desc.EnumValueDescriptor:\n\t\t\t\telementType = \"an enum value\"\n\t\t\tcase *desc.ServiceDescriptor:\n\t\t\t\telementType = \"a service\"\n\t\t\tcase *desc.MethodDescriptor:\n\t\t\t\telementType = \"a method\"\n\t\t\tdefault:\n\t\t\t\terr = fmt.Errorf(\"descriptor has unrecognized type %T\", dsc)\n\t\t\t\tfail(err, \"Failed to describe symbol %q\", s)\n\t\t\t}\n\n\t\t\ttxt, err := grpcurl.GetDescriptorText(dsc, descSource)\n\t\t\tif err != nil {\n\t\t\t\tfail(err, \"Failed to describe symbol %q\", s)\n\t\t\t}\n\t\t\tfmt.Printf(\"%s is %s:\\n\", fqn, elementType)\n\t\t\tfmt.Println(txt)\n\n\t\t\tif dsc, ok := dsc.(*desc.MessageDescriptor); ok && *msgTemplate {\n\t\t\t\t// for messages, also show a template in JSON, to make it easier to\n\t\t\t\t// create a request to invoke an RPC\n\t\t\t\ttmpl := grpcurl.MakeTemplate(dsc)\n\t\t\t\toptions := grpcurl.FormatOptions{EmitJSONDefaultFields: true}\n\t\t\t\t_, formatter, err := grpcurl.RequestParserAndFormatter(grpcurl.Format(*format), descSource, nil, options)\n\t\t\t\tif err != nil {\n\t\t\t\t\tfail(err, \"Failed to construct formatter for %q\", *format)\n\t\t\t\t}\n\t\t\t\tstr, err := formatter(tmpl)\n\t\t\t\tif err != nil {\n\t\t\t\t\tfail(err, \"Failed to print template for message %s\", s)\n\t\t\t\t}\n\t\t\t\tfmt.Println(\"\\nMessage template:\")\n\t\t\t\tfmt.Println(str)\n\t\t\t}\n\t\t}\n\t\tif err := writeProtoset(descSource, symbols...); err != nil {\n\t\t\tfail(err, \"Failed to write protoset to %s\", *protosetOut)\n\t\t}\n\t\tif err := writeProtos(descSource, symbol); err != nil {\n\t\t\tfail(err, \"Failed to write protos to %s\", *protoOut)\n\t\t}\n\n\t} else {\n\t\t// Invoke an RPC\n\t\tif cc == nil {\n\t\t\tcc = dial()\n\t\t}\n\t\tvar in io.Reader\n\t\tif *data == \"@\" {\n\t\t\tin = os.Stdin\n\t\t} else {\n\t\t\tin = strings.NewReader(*data)\n\t\t}\n\n\t\t// if not verbose output, then also include record delimiters\n\t\t// between each message, so output could potentially be piped\n\t\t// to another grpcurl process\n\t\tincludeSeparators := verbosityLevel == 0\n\t\toptions := grpcurl.FormatOptions{\n\t\t\tEmitJSONDefaultFields: *emitDefaults,\n\t\t\tIncludeTextSeparator:  includeSeparators,\n\t\t\tAllowUnknownFields:    *allowUnknownFields,\n\t\t}\n\t\trf, formatter, err := grpcurl.RequestParserAndFormatter(grpcurl.Format(*format), descSource, in, options)\n\t\tif err != nil {\n\t\t\tfail(err, \"Failed to construct request parser and formatter for %q\", *format)\n\t\t}\n\t\th := &grpcurl.DefaultEventHandler{\n\t\t\tOut:            os.Stdout,\n\t\t\tFormatter:      formatter,\n\t\t\tVerbosityLevel: verbosityLevel,\n\t\t}\n\n\t\tinvokeTiming := rootTiming.Child(\"InvokeRPC\")\n\t\terr = grpcurl.InvokeRPC(ctx, descSource, cc, symbol, append(addlHeaders, rpcHeaders...), h, rf.Next)\n\t\tinvokeTiming.Done()\n\t\tif err != nil {\n\t\t\tif errStatus, ok := status.FromError(err); ok && *formatError {\n\t\t\t\th.Status = errStatus\n\t\t\t} else {\n\t\t\t\tfail(err, \"Error invoking method %q\", symbol)\n\t\t\t}\n\t\t}\n\t\treqSuffix := \"\"\n\t\trespSuffix := \"\"\n\t\treqCount := rf.NumRequests()\n\t\tif reqCount != 1 {\n\t\t\treqSuffix = \"s\"\n\t\t}\n\t\tif h.NumResponses != 1 {\n\t\t\trespSuffix = \"s\"\n\t\t}\n\t\tif verbosityLevel > 0 {\n\t\t\tfmt.Printf(\"Sent %d request%s and received %d response%s\\n\", reqCount, reqSuffix, h.NumResponses, respSuffix)\n\t\t}\n\t\tif h.Status.Code() != codes.OK {\n\t\t\tif *formatError {\n\t\t\t\tprintFormattedStatus(os.Stderr, h.Status, formatter)\n\t\t\t} else {\n\t\t\t\tgrpcurl.PrintStatus(os.Stderr, h.Status, formatter)\n\t\t\t}\n\t\t\texit(statusCodeOffset + int(h.Status.Code()))\n\t\t}\n\t}\n}\n\nfunc dumpTiming(td *timingData, lvl int) {\n\tvar ind strings.Builder\n\tfor x := 0; x < lvl; x++ {\n\t\tind.WriteString(\"  \")\n\t}\n\tfmt.Printf(\"%s%s: %s\\n\", ind.String(), td.Title, td.Value)\n\tfor _, sd := range td.Sub {\n\t\tdumpTiming(sd, lvl+1)\n\t}\n}\n\nfunc usage() {\n\tfmt.Fprintf(os.Stderr, `Usage:\n\t%s [flags] [address] [list|describe] [symbol]\n\nThe 'address' is only optional when used with 'list' or 'describe' and a\nprotoset or proto flag is provided.\n\nIf 'list' is indicated, the symbol (if present) should be a fully-qualified\nservice name. If present, all methods of that service are listed. If not\npresent, all exposed services are listed, or all services defined in protosets.\n\nIf 'describe' is indicated, the descriptor for the given symbol is shown. The\nsymbol should be a fully-qualified service, enum, or message name. If no symbol\nis given then the descriptors for all exposed or known services are shown.\n\nIf neither verb is present, the symbol must be a fully-qualified method name in\n'service/method' or 'service.method' format. In this case, the request body will\nbe used to invoke the named method. If no body is given but one is required\n(i.e. the method is unary or server-streaming), an empty instance of the\nmethod's request type will be sent.\n\nThe address will typically be in the form \"host:port\" where host can be an IP\naddress or a hostname and port is a numeric port or service name. If an IPv6\naddress is given, it must be surrounded by brackets, like \"[2001:db8::1]\". For\nUnix variants, if a -unix=true flag is present, then the address must be the\npath to the domain socket.\n\nAvailable flags:\n`, os.Args[0])\n\tflags.PrintDefaults()\n}\n\nfunc prettify(docString string) string {\n\tparts := strings.Split(docString, \"\\n\")\n\n\t// cull empty lines and also remove trailing and leading spaces\n\t// from each line in the doc string\n\tj := 0\n\tfor _, part := range parts {\n\t\tpart = strings.TrimSpace(part)\n\t\tif part == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tparts[j] = part\n\t\tj++\n\t}\n\n\treturn strings.Join(parts[:j], \"\\n\")\n}\n\nfunc warn(msg string, args ...interface{}) {\n\tmsg = fmt.Sprintf(\"Warning: %s\\n\", msg)\n\tfmt.Fprintf(os.Stderr, msg, args...)\n}\n\nfunc fail(err error, msg string, args ...interface{}) {\n\tif err != nil {\n\t\tmsg += \": %v\"\n\t\targs = append(args, err)\n\t}\n\tfmt.Fprintf(os.Stderr, msg, args...)\n\tfmt.Fprintln(os.Stderr)\n\tif err != nil {\n\t\texit(1)\n\t} else {\n\t\t// nil error means it was CLI usage issue\n\t\tfmt.Fprintf(os.Stderr, \"Try '%s -help' for more details.\\n\", os.Args[0])\n\t\texit(2)\n\t}\n}\n\nfunc writeProtoset(descSource grpcurl.DescriptorSource, symbols ...string) error {\n\tif *protosetOut == \"\" {\n\t\treturn nil\n\t}\n\tf, err := os.Create(*protosetOut)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\treturn grpcurl.WriteProtoset(f, descSource, symbols...)\n}\n\nfunc writeProtos(descSource grpcurl.DescriptorSource, symbols ...string) error {\n\tif *protoOut == \"\" {\n\t\treturn nil\n\t}\n\treturn grpcurl.WriteProtoFiles(*protoOut, descSource, symbols...)\n}\n\ntype optionalBoolFlag struct {\n\tset, val bool\n}\n\nfunc (f *optionalBoolFlag) String() string {\n\tif !f.set {\n\t\treturn \"unset\"\n\t}\n\treturn strconv.FormatBool(f.val)\n}\n\nfunc (f *optionalBoolFlag) Set(s string) error {\n\tv, err := strconv.ParseBool(s)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.set = true\n\tf.val = v\n\treturn nil\n}\n\nfunc (f *optionalBoolFlag) IsBoolFlag() bool {\n\treturn true\n}\n\nfunc floatSecondsToDuration(seconds float64) time.Duration {\n\tdurationFloat := seconds * float64(time.Second)\n\tif durationFloat > math.MaxInt64 {\n\t\t// Avoid overflow\n\t\treturn math.MaxInt64\n\t}\n\treturn time.Duration(durationFloat)\n}\n"
  },
  {
    "path": "cmd/grpcurl/indent_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"testing\"\n)\n\nfunc TestFlagDocIndent(t *testing.T) {\n\t// Tests the prettify() and indent() function. The indent() function\n\t// differs by Go version, due to differences in \"flags\" package across\n\t// versions. Run with multiple versions of Go to ensure that doc output\n\t// is properly indented, regardless of Go version.\n\n\tvar fs flag.FlagSet\n\tvar buf bytes.Buffer\n\tfs.SetOutput(&buf)\n\n\tfs.String(\"foo\", \"\", prettify(`\n\t\tThis is a flag doc string.\n\t\tIt has multiple lines.\n\t\tMore than two, actually.`))\n\tfs.Int(\"bar\", 100, prettify(`This is a simple flag doc string.`))\n\tfs.Bool(\"baz\", false, prettify(`\n\t\tThis is another long doc string.\n\t\tIt also has multiple lines. But not as long as the first one.`))\n\n\tfs.PrintDefaults()\n\n\texpected :=\n\t\t`  -bar int\n    \tThis is a simple flag doc string. (default 100)\n  -baz\n    \tThis is another long doc string.\n    \tIt also has multiple lines. But not as long as the first one.\n  -foo string\n    \tThis is a flag doc string.\n    \tIt has multiple lines.\n    \tMore than two, actually.\n`\n\n\tactual := buf.String()\n\tif actual != expected {\n\t\tt.Errorf(\"Flag output had wrong indentation.\\nExpecting:\\n%s\\nGot:\\n%s\", expected, actual)\n\t}\n}\n"
  },
  {
    "path": "cmd/grpcurl/unix.go",
    "content": "//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows\n// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows\n\npackage main\n\nvar (\n\tunix = flags.Bool(\"unix\", false, prettify(`\n\t\tIndicates that the server address is the path to a Unix domain socket.`))\n)\n\nfunc init() {\n\tisUnixSocket = func() bool {\n\t\treturn *unix\n\t}\n}\n"
  },
  {
    "path": "desc_source.go",
    "content": "package grpcurl\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync\"\n\n\t\"github.com/golang/protobuf/proto\"              //lint:ignore SA1019 we have to import these because some of their types appear in exported API\n\t\"github.com/jhump/protoreflect/desc\"            //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/desc/protoparse\" //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/desc/protoprint\"\n\t\"github.com/jhump/protoreflect/dynamic\" //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/grpcreflect\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/types/descriptorpb\"\n)\n\n// ErrReflectionNotSupported is returned by DescriptorSource operations that\n// rely on interacting with the reflection service when the source does not\n// actually expose the reflection service. When this occurs, an alternate source\n// (like file descriptor sets) must be used.\nvar ErrReflectionNotSupported = errors.New(\"server does not support the reflection API\")\n\n// DescriptorSource is a source of protobuf descriptor information. It can be backed by a FileDescriptorSet\n// proto (like a file generated by protoc) or a remote server that supports the reflection API.\ntype DescriptorSource interface {\n\t// ListServices returns a list of fully-qualified service names. It will be all services in a set of\n\t// descriptor files or the set of all services exposed by a gRPC server.\n\tListServices() ([]string, error)\n\t// FindSymbol returns a descriptor for the given fully-qualified symbol name.\n\tFindSymbol(fullyQualifiedName string) (desc.Descriptor, error)\n\t// AllExtensionsForType returns all known extension fields that extend the given message type name.\n\tAllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error)\n}\n\n// DescriptorSourceFromProtoSets creates a DescriptorSource that is backed by the named files, whose contents\n// are encoded FileDescriptorSet protos.\nfunc DescriptorSourceFromProtoSets(fileNames ...string) (DescriptorSource, error) {\n\tfiles := &descriptorpb.FileDescriptorSet{}\n\tfor _, fileName := range fileNames {\n\t\tb, err := os.ReadFile(fileName)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not load protoset file %q: %v\", fileName, err)\n\t\t}\n\t\tvar fs descriptorpb.FileDescriptorSet\n\t\terr = proto.Unmarshal(b, &fs)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not parse contents of protoset file %q: %v\", fileName, err)\n\t\t}\n\t\tfiles.File = append(files.File, fs.File...)\n\t}\n\treturn DescriptorSourceFromFileDescriptorSet(files)\n}\n\n// DescriptorSourceFromProtoFiles creates a DescriptorSource that is backed by the named files,\n// whose contents are Protocol Buffer source files. The given importPaths are used to locate\n// any imported files.\nfunc DescriptorSourceFromProtoFiles(importPaths []string, fileNames ...string) (DescriptorSource, error) {\n\tfileNames, err := protoparse.ResolveFilenames(importPaths, fileNames...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tp := protoparse.Parser{\n\t\tImportPaths:           importPaths,\n\t\tInferImportPaths:      len(importPaths) == 0,\n\t\tIncludeSourceCodeInfo: true,\n\t}\n\tfds, err := p.ParseFiles(fileNames...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not parse given files: %v\", err)\n\t}\n\treturn DescriptorSourceFromFileDescriptors(fds...)\n}\n\n// DescriptorSourceFromFileDescriptorSet creates a DescriptorSource that is backed by the FileDescriptorSet.\nfunc DescriptorSourceFromFileDescriptorSet(files *descriptorpb.FileDescriptorSet) (DescriptorSource, error) {\n\tunresolved := map[string]*descriptorpb.FileDescriptorProto{}\n\tfor _, fd := range files.File {\n\t\tunresolved[fd.GetName()] = fd\n\t}\n\tresolved := map[string]*desc.FileDescriptor{}\n\tfor _, fd := range files.File {\n\t\t_, err := resolveFileDescriptor(unresolved, resolved, fd.GetName())\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn &fileSource{files: resolved}, nil\n}\n\nfunc resolveFileDescriptor(unresolved map[string]*descriptorpb.FileDescriptorProto, resolved map[string]*desc.FileDescriptor, filename string) (*desc.FileDescriptor, error) {\n\tif r, ok := resolved[filename]; ok {\n\t\treturn r, nil\n\t}\n\tfd, ok := unresolved[filename]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"no descriptor found for %q\", filename)\n\t}\n\tdeps := make([]*desc.FileDescriptor, 0, len(fd.GetDependency()))\n\tfor _, dep := range fd.GetDependency() {\n\t\tdepFd, err := resolveFileDescriptor(unresolved, resolved, dep)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdeps = append(deps, depFd)\n\t}\n\tresult, err := desc.CreateFileDescriptor(fd, deps...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresolved[filename] = result\n\treturn result, nil\n}\n\n// DescriptorSourceFromFileDescriptors creates a DescriptorSource that is backed by the given\n// file descriptors\nfunc DescriptorSourceFromFileDescriptors(files ...*desc.FileDescriptor) (DescriptorSource, error) {\n\tfds := map[string]*desc.FileDescriptor{}\n\tfor _, fd := range files {\n\t\tif err := addFile(fd, fds); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn &fileSource{files: fds}, nil\n}\n\nfunc addFile(fd *desc.FileDescriptor, fds map[string]*desc.FileDescriptor) error {\n\tname := fd.GetName()\n\tif existing, ok := fds[name]; ok {\n\t\t// already added this file\n\t\tif existing != fd {\n\t\t\t// doh! duplicate files provided\n\t\t\treturn fmt.Errorf(\"given files include multiple copies of %q\", name)\n\t\t}\n\t\treturn nil\n\t}\n\tfds[name] = fd\n\tfor _, dep := range fd.GetDependencies() {\n\t\tif err := addFile(dep, fds); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\ntype fileSource struct {\n\tfiles  map[string]*desc.FileDescriptor\n\ter     *dynamic.ExtensionRegistry\n\terInit sync.Once\n}\n\nfunc (fs *fileSource) ListServices() ([]string, error) {\n\tset := map[string]bool{}\n\tfor _, fd := range fs.files {\n\t\tfor _, svc := range fd.GetServices() {\n\t\t\tset[svc.GetFullyQualifiedName()] = true\n\t\t}\n\t}\n\tsl := make([]string, 0, len(set))\n\tfor svc := range set {\n\t\tsl = append(sl, svc)\n\t}\n\treturn sl, nil\n}\n\n// GetAllFiles returns all of the underlying file descriptors. This is\n// more thorough and more efficient than the fallback strategy used by\n// the GetAllFiles package method, for enumerating all files from a\n// descriptor source.\nfunc (fs *fileSource) GetAllFiles() ([]*desc.FileDescriptor, error) {\n\tfiles := make([]*desc.FileDescriptor, len(fs.files))\n\ti := 0\n\tfor _, fd := range fs.files {\n\t\tfiles[i] = fd\n\t\ti++\n\t}\n\treturn files, nil\n}\n\nfunc (fs *fileSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) {\n\tfor _, fd := range fs.files {\n\t\tif dsc := fd.FindSymbol(fullyQualifiedName); dsc != nil {\n\t\t\treturn dsc, nil\n\t\t}\n\t}\n\treturn nil, notFound(\"Symbol\", fullyQualifiedName)\n}\n\nfunc (fs *fileSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) {\n\tfs.erInit.Do(func() {\n\t\tfs.er = &dynamic.ExtensionRegistry{}\n\t\tfor _, fd := range fs.files {\n\t\t\tfs.er.AddExtensionsFromFile(fd)\n\t\t}\n\t})\n\treturn fs.er.AllExtensionsForType(typeName), nil\n}\n\n// DescriptorSourceFromServer creates a DescriptorSource that uses the given gRPC reflection client\n// to interrogate a server for descriptor information. If the server does not support the reflection\n// API then the various DescriptorSource methods will return ErrReflectionNotSupported\nfunc DescriptorSourceFromServer(_ context.Context, refClient *grpcreflect.Client) DescriptorSource {\n\treturn serverSource{client: refClient}\n}\n\ntype serverSource struct {\n\tclient *grpcreflect.Client\n}\n\nfunc (ss serverSource) ListServices() ([]string, error) {\n\tsvcs, err := ss.client.ListServices()\n\treturn svcs, reflectionSupport(err)\n}\n\nfunc (ss serverSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) {\n\tfile, err := ss.client.FileContainingSymbol(fullyQualifiedName)\n\tif err != nil {\n\t\treturn nil, reflectionSupport(err)\n\t}\n\td := file.FindSymbol(fullyQualifiedName)\n\tif d == nil {\n\t\treturn nil, notFound(\"Symbol\", fullyQualifiedName)\n\t}\n\treturn d, nil\n}\n\nfunc (ss serverSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) {\n\tvar exts []*desc.FieldDescriptor\n\tnums, err := ss.client.AllExtensionNumbersForType(typeName)\n\tif err != nil {\n\t\treturn nil, reflectionSupport(err)\n\t}\n\tfor _, fieldNum := range nums {\n\t\text, err := ss.client.ResolveExtension(typeName, fieldNum)\n\t\tif err != nil {\n\t\t\treturn nil, reflectionSupport(err)\n\t\t}\n\t\texts = append(exts, ext)\n\t}\n\treturn exts, nil\n}\n\nfunc reflectionSupport(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif stat, ok := status.FromError(err); ok && stat.Code() == codes.Unimplemented {\n\t\treturn ErrReflectionNotSupported\n\t}\n\treturn err\n}\n\n// WriteProtoset will use the given descriptor source to resolve all of the given\n// symbols and write a proto file descriptor set with their definitions to the\n// given output. The output will include descriptors for all files in which the\n// symbols are defined as well as their transitive dependencies.\nfunc WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string) error {\n\tfilenames, fds, err := getFileDescriptors(symbols, descSource)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// now expand that to include transitive dependencies in topologically sorted\n\t// order (such that file always appears after its dependencies)\n\texpandedFiles := make(map[string]struct{}, len(fds))\n\tallFilesSlice := make([]*descriptorpb.FileDescriptorProto, 0, len(fds))\n\tfor _, filename := range filenames {\n\t\tallFilesSlice = addFilesToSet(allFilesSlice, expandedFiles, fds[filename])\n\t}\n\t// now we can serialize to file\n\tb, err := proto.Marshal(&descriptorpb.FileDescriptorSet{File: allFilesSlice})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to serialize file descriptor set: %v\", err)\n\t}\n\tif _, err := out.Write(b); err != nil {\n\t\treturn fmt.Errorf(\"failed to write file descriptor set: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc addFilesToSet(allFiles []*descriptorpb.FileDescriptorProto, expanded map[string]struct{}, fd *desc.FileDescriptor) []*descriptorpb.FileDescriptorProto {\n\tif _, ok := expanded[fd.GetName()]; ok {\n\t\t// already seen this one\n\t\treturn allFiles\n\t}\n\texpanded[fd.GetName()] = struct{}{}\n\t// add all dependencies first\n\tfor _, dep := range fd.GetDependencies() {\n\t\tallFiles = addFilesToSet(allFiles, expanded, dep)\n\t}\n\treturn append(allFiles, fd.AsFileDescriptorProto())\n}\n\n// WriteProtoFiles will use the given descriptor source to resolve all the given\n// symbols and write proto files with their definitions to the given output directory.\nfunc WriteProtoFiles(outProtoDirPath string, descSource DescriptorSource, symbols ...string) error {\n\tfilenames, fds, err := getFileDescriptors(symbols, descSource)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// now expand that to include transitive dependencies in topologically sorted\n\t// order (such that file always appears after its dependencies)\n\texpandedFiles := make(map[string]struct{}, len(fds))\n\tallFileDescriptors := make([]*desc.FileDescriptor, 0, len(fds))\n\tfor _, filename := range filenames {\n\t\tallFileDescriptors = addFilesToFileDescriptorList(allFileDescriptors, expandedFiles, fds[filename])\n\t}\n\tpr := protoprint.Printer{}\n\t// now we can serialize to files\n\tfor i := range allFileDescriptors {\n\t\tif err := writeProtoFile(outProtoDirPath, allFileDescriptors[i], &pr); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc writeProtoFile(outProtoDirPath string, fd *desc.FileDescriptor, pr *protoprint.Printer) error {\n\toutFile := filepath.Join(outProtoDirPath, fd.GetFullyQualifiedName())\n\toutDir := filepath.Dir(outFile)\n\tif err := os.MkdirAll(outDir, 0777); err != nil {\n\t\treturn fmt.Errorf(\"failed to create directory %q: %w\", outDir, err)\n\t}\n\n\tf, err := os.Create(outFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create proto file %q: %w\", outFile, err)\n\t}\n\tdefer f.Close()\n\tif err := pr.PrintProtoFile(fd, f); err != nil {\n\t\treturn fmt.Errorf(\"failed to write proto file %q: %w\", outFile, err)\n\t}\n\treturn nil\n}\n\nfunc getFileDescriptors(symbols []string, descSource DescriptorSource) ([]string, map[string]*desc.FileDescriptor, error) {\n\t// compute set of file descriptors\n\tfilenames := make([]string, 0, len(symbols))\n\tfds := make(map[string]*desc.FileDescriptor, len(symbols))\n\tfor _, sym := range symbols {\n\t\td, err := descSource.FindSymbol(sym)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to find descriptor for %q: %v\", sym, err)\n\t\t}\n\t\tfd := d.GetFile()\n\t\tif _, ok := fds[fd.GetName()]; !ok {\n\t\t\tfds[fd.GetName()] = fd\n\t\t\tfilenames = append(filenames, fd.GetName())\n\t\t}\n\t}\n\treturn filenames, fds, nil\n}\n\nfunc addFilesToFileDescriptorList(allFiles []*desc.FileDescriptor, expanded map[string]struct{}, fd *desc.FileDescriptor) []*desc.FileDescriptor {\n\tif _, ok := expanded[fd.GetName()]; ok {\n\t\t// already seen this one\n\t\treturn allFiles\n\t}\n\texpanded[fd.GetName()] = struct{}{}\n\t// add all dependencies first\n\tfor _, dep := range fd.GetDependencies() {\n\t\tallFiles = addFilesToFileDescriptorList(allFiles, expanded, dep)\n\t}\n\treturn append(allFiles, fd)\n}\n"
  },
  {
    "path": "desc_source_test.go",
    "content": "package grpcurl\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/golang/protobuf/proto\" //lint:ignore SA1019 we have to import this because it appears in exported API\n\t\"google.golang.org/protobuf/types/descriptorpb\"\n)\n\nfunc TestWriteProtoset(t *testing.T) {\n\texampleProtoset, err := loadProtoset(\"./internal/testing/example.protoset\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to load example.protoset: %v\", err)\n\t}\n\ttestProtoset, err := loadProtoset(\"./internal/testing/test.protoset\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to load test.protoset: %v\", err)\n\t}\n\n\tmergedProtoset := &descriptorpb.FileDescriptorSet{\n\t\tFile: append(exampleProtoset.File, testProtoset.File...),\n\t}\n\n\tdescSrc, err := DescriptorSourceFromFileDescriptorSet(mergedProtoset)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create descriptor source: %v\", err)\n\t}\n\n\tcheckWriteProtoset(t, descSrc, exampleProtoset, \"TestService\")\n\tcheckWriteProtoset(t, descSrc, testProtoset, \"testing.TestService\")\n\tcheckWriteProtoset(t, descSrc, mergedProtoset, \"TestService\", \"testing.TestService\")\n}\n\nfunc loadProtoset(path string) (*descriptorpb.FileDescriptorSet, error) {\n\tb, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar protoset descriptorpb.FileDescriptorSet\n\tif err := proto.Unmarshal(b, &protoset); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &protoset, nil\n}\n\nfunc checkWriteProtoset(t *testing.T, descSrc DescriptorSource, protoset *descriptorpb.FileDescriptorSet, symbols ...string) {\n\tvar buf bytes.Buffer\n\tif err := WriteProtoset(&buf, descSrc, symbols...); err != nil {\n\t\tt.Fatalf(\"failed to write protoset: %v\", err)\n\t}\n\n\tvar result descriptorpb.FileDescriptorSet\n\tif err := proto.Unmarshal(buf.Bytes(), &result); err != nil {\n\t\tt.Fatalf(\"failed to unmarshal written protoset: %v\", err)\n\t}\n\n\tif !proto.Equal(protoset, &result) {\n\t\tt.Fatalf(\"written protoset not equal to input:\\nExpecting: %s\\nActual: %s\", protoset, &result)\n\t}\n}\n"
  },
  {
    "path": "download_protoc.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\ncd $(dirname $0)\n\nif [[ -z \"$PROTOC_VERSION\" ]]; then\n  echo \"Set PROTOC_VERSION env var to indicate the version to download\" >&2\n  exit 1\nfi\nPROTOC_OS=\"$(uname -s)\"\nPROTOC_ARCH=\"$(uname -m)\"\ncase \"${PROTOC_OS}\" in\n  Darwin) PROTOC_OS=\"osx\" ;;\n  Linux) PROTOC_OS=\"linux\" ;;\n  *)\n    echo \"Invalid value for uname -s: ${PROTOC_OS}\" >&2\n    exit 1\nesac\n\n# This is for macs with M1 chips. Precompiled binaries for osx/amd64 are not available for download, so for that case\n# we download the x86_64 version instead. This will work as long as rosetta2 is installed.\nif [ \"$PROTOC_OS\" = \"osx\" ] && [ \"$PROTOC_ARCH\" = \"arm64\" ]; then\n  PROTOC_ARCH=\"x86_64\"\nfi\n\nPROTOC=\"${PWD}/.tmp/protoc/bin/protoc\"\n\nif [[ \"$(${PROTOC} --version 2>/dev/null)\" != \"libprotoc 3.${PROTOC_VERSION}\" ]]; then\n  rm -rf ./.tmp/protoc\n  mkdir -p .tmp/protoc\n  curl -L \"https://github.com/google/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-${PROTOC_OS}-${PROTOC_ARCH}.zip\" > .tmp/protoc/protoc.zip\n  pushd ./.tmp/protoc && unzip protoc.zip && popd\nfi\n\n"
  },
  {
    "path": "format.go",
    "content": "package grpcurl\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/golang/protobuf/jsonpb\"     //lint:ignore SA1019 we have to import these because some of their types appear in exported API\n\t\"github.com/golang/protobuf/proto\"      //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/desc\"    //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/dynamic\" //lint:ignore SA1019 same as above\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/status\"\n)\n\n// RequestParser processes input into messages.\ntype RequestParser interface {\n\t// Next parses input data into the given request message. If called after\n\t// input is exhausted, it returns io.EOF. If the caller re-uses the same\n\t// instance in multiple calls to Next, it should call msg.Reset() in between\n\t// each call.\n\tNext(msg proto.Message) error\n\t// NumRequests returns the number of messages that have been parsed and\n\t// returned by a call to Next.\n\tNumRequests() int\n}\n\ntype jsonRequestParser struct {\n\tdec          *json.Decoder\n\tunmarshaler  jsonpb.Unmarshaler\n\trequestCount int\n}\n\n// NewJSONRequestParser returns a RequestParser that reads data in JSON format\n// from the given reader. The given resolver is used to assist with decoding of\n// google.protobuf.Any messages.\n//\n// Input data that contains more than one message should just include all\n// messages concatenated (though whitespace is necessary to separate some kinds\n// of values in JSON).\n//\n// If the given reader has no data, the returned parser will return io.EOF on\n// the very first call.\nfunc NewJSONRequestParser(in io.Reader, resolver jsonpb.AnyResolver) RequestParser {\n\treturn &jsonRequestParser{\n\t\tdec:         json.NewDecoder(in),\n\t\tunmarshaler: jsonpb.Unmarshaler{AnyResolver: resolver},\n\t}\n}\n\n// NewJSONRequestParserWithUnmarshaler is like NewJSONRequestParser but\n// accepts a protobuf jsonpb.Unmarshaler instead of jsonpb.AnyResolver.\nfunc NewJSONRequestParserWithUnmarshaler(in io.Reader, unmarshaler jsonpb.Unmarshaler) RequestParser {\n\treturn &jsonRequestParser{\n\t\tdec:         json.NewDecoder(in),\n\t\tunmarshaler: unmarshaler,\n\t}\n}\n\nfunc (f *jsonRequestParser) Next(m proto.Message) error {\n\tvar msg json.RawMessage\n\tif err := f.dec.Decode(&msg); err != nil {\n\t\treturn err\n\t}\n\tf.requestCount++\n\treturn f.unmarshaler.Unmarshal(bytes.NewReader(msg), m)\n}\n\nfunc (f *jsonRequestParser) NumRequests() int {\n\treturn f.requestCount\n}\n\nconst (\n\ttextSeparatorChar = '\\x1e'\n)\n\ntype textRequestParser struct {\n\tr            *bufio.Reader\n\terr          error\n\trequestCount int\n}\n\n// NewTextRequestParser returns a RequestParser that reads data in the protobuf\n// text format from the given reader.\n//\n// Input data that contains more than one message should include an ASCII\n// 'Record Separator' character (0x1E) between each message.\n//\n// Empty text is a valid text format and represents an empty message. So if the\n// given reader has no data, the returned parser will yield an empty message\n// for the first call to Next and then return io.EOF thereafter. This also means\n// that if the input data ends with a record separator, then a final empty\n// message will be parsed *after* the separator.\nfunc NewTextRequestParser(in io.Reader) RequestParser {\n\treturn &textRequestParser{r: bufio.NewReader(in)}\n}\n\nfunc (f *textRequestParser) Next(m proto.Message) error {\n\tif f.err != nil {\n\t\treturn f.err\n\t}\n\n\tvar b []byte\n\tb, f.err = f.r.ReadBytes(textSeparatorChar)\n\tif f.err != nil && f.err != io.EOF {\n\t\treturn f.err\n\t}\n\t// remove delimiter\n\tif len(b) > 0 && b[len(b)-1] == textSeparatorChar {\n\t\tb = b[:len(b)-1]\n\t}\n\n\tf.requestCount++\n\n\treturn proto.UnmarshalText(string(b), m)\n}\n\nfunc (f *textRequestParser) NumRequests() int {\n\treturn f.requestCount\n}\n\n// Formatter translates messages into string representations.\ntype Formatter func(proto.Message) (string, error)\n\n// NewJSONFormatter returns a formatter that returns JSON strings. The JSON will\n// include empty/default values (instead of just omitted them) if emitDefaults\n// is true. The given resolver is used to assist with encoding of\n// google.protobuf.Any messages.\nfunc NewJSONFormatter(emitDefaults bool, resolver jsonpb.AnyResolver) Formatter {\n\tmarshaler := jsonpb.Marshaler{\n\t\tEmitDefaults: emitDefaults,\n\t\tAnyResolver:  resolver,\n\t}\n\t// Workaround for indentation issue in jsonpb with Any messages.\n\t// Bug was originally fixed in https://github.com/golang/protobuf/pull/834\n\t// but later re-introduced before the module was deprecated and frozen.\n\t// If jsonpb is ever replaced with google.golang.org/protobuf/encoding/protojson\n\t// this workaround will no longer be needed.\n\tformatter := func(message proto.Message) (string, error) {\n\t\toutput, err := marshaler.MarshalToString(message)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tvar buf bytes.Buffer\n\t\tif err := json.Indent(&buf, []byte(output), \"\", \"  \"); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn buf.String(), nil\n\t}\n\treturn formatter\n}\n\n// NewTextFormatter returns a formatter that returns strings in the protobuf\n// text format. If includeSeparator is true then, when invoked to format\n// multiple messages, all messages after the first one will be prefixed with the\n// ASCII 'Record Separator' character (0x1E).\nfunc NewTextFormatter(includeSeparator bool) Formatter {\n\ttf := textFormatter{useSeparator: includeSeparator}\n\treturn tf.format\n}\n\ntype textFormatter struct {\n\tuseSeparator bool\n\tnumFormatted int\n}\n\nvar protoTextMarshaler = proto.TextMarshaler{ExpandAny: true}\n\nfunc (tf *textFormatter) format(m proto.Message) (string, error) {\n\tvar buf bytes.Buffer\n\tif tf.useSeparator && tf.numFormatted > 0 {\n\t\tif err := buf.WriteByte(textSeparatorChar); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\t// If message implements MarshalText method (such as a *dynamic.Message),\n\t// it won't get details about whether or not to format to text compactly\n\t// or with indentation. So first see if the message also implements a\n\t// MarshalTextIndent method and use that instead if available.\n\ttype indentMarshaler interface {\n\t\tMarshalTextIndent() ([]byte, error)\n\t}\n\n\tif indenter, ok := m.(indentMarshaler); ok {\n\t\tb, err := indenter.MarshalTextIndent()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif _, err := buf.Write(b); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t} else if err := protoTextMarshaler.Marshal(&buf, m); err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// no trailing newline needed\n\tstr := buf.String()\n\tif len(str) > 0 && str[len(str)-1] == '\\n' {\n\t\tstr = str[:len(str)-1]\n\t}\n\n\ttf.numFormatted++\n\n\treturn str, nil\n}\n\n// Format of request data. The allowed values are 'json' or 'text'.\ntype Format string\n\nconst (\n\t// FormatJSON specifies input data in JSON format. Multiple request values\n\t// may be concatenated (messages with a JSON representation other than\n\t// object must be separated by whitespace, such as a newline)\n\tFormatJSON = Format(\"json\")\n\n\t// FormatText specifies input data must be in the protobuf text format.\n\t// Multiple request values must be separated by the \"record separator\"\n\t// ASCII character: 0x1E. The stream should not end in a record separator.\n\t// If it does, it will be interpreted as a final, blank message after the\n\t// separator.\n\tFormatText = Format(\"text\")\n)\n\n// AnyResolverFromDescriptorSource returns an AnyResolver that will search for\n// types using the given descriptor source.\nfunc AnyResolverFromDescriptorSource(source DescriptorSource) jsonpb.AnyResolver {\n\treturn &anyResolver{source: source}\n}\n\n// AnyResolverFromDescriptorSourceWithFallback returns an AnyResolver that will\n// search for types using the given descriptor source and then fallback to a\n// special message if the type is not found. The fallback type will render to\n// JSON with a \"@type\" property, just like an Any message, but also with a\n// custom \"@value\" property that includes the binary encoded payload.\nfunc AnyResolverFromDescriptorSourceWithFallback(source DescriptorSource) jsonpb.AnyResolver {\n\tres := anyResolver{source: source}\n\treturn &anyResolverWithFallback{AnyResolver: &res}\n}\n\ntype anyResolver struct {\n\tsource DescriptorSource\n\n\ter dynamic.ExtensionRegistry\n\n\tmu       sync.RWMutex\n\tmf       *dynamic.MessageFactory\n\tresolved map[string]func() proto.Message\n}\n\nfunc (r *anyResolver) Resolve(typeUrl string) (proto.Message, error) {\n\tmname := typeUrl\n\tif slash := strings.LastIndex(mname, \"/\"); slash >= 0 {\n\t\tmname = mname[slash+1:]\n\t}\n\n\tr.mu.RLock()\n\tfactory := r.resolved[mname]\n\tr.mu.RUnlock()\n\n\t// already resolved?\n\tif factory != nil {\n\t\treturn factory(), nil\n\t}\n\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\t// double-check, in case we were racing with another goroutine\n\t// that resolved this one\n\tfactory = r.resolved[mname]\n\tif factory != nil {\n\t\treturn factory(), nil\n\t}\n\n\t// use descriptor source to resolve message type\n\td, err := r.source.FindSymbol(mname)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmd, ok := d.(*desc.MessageDescriptor)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unknown message: %s\", typeUrl)\n\t}\n\t// populate any extensions for this message, too (if there are any)\n\tif exts, err := r.source.AllExtensionsForType(mname); err == nil {\n\t\tif err := r.er.AddExtension(exts...); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif r.mf == nil {\n\t\tr.mf = dynamic.NewMessageFactoryWithExtensionRegistry(&r.er)\n\t}\n\n\tfactory = func() proto.Message {\n\t\treturn r.mf.NewMessage(md)\n\t}\n\tif r.resolved == nil {\n\t\tr.resolved = map[string]func() proto.Message{}\n\t}\n\tr.resolved[mname] = factory\n\treturn factory(), nil\n}\n\n// anyResolverWithFallback can provide a fallback value for unknown\n// messages that will format itself to JSON using an \"@value\" field\n// that has the base64-encoded data for the unknown message value.\ntype anyResolverWithFallback struct {\n\tjsonpb.AnyResolver\n}\n\nfunc (r anyResolverWithFallback) Resolve(typeUrl string) (proto.Message, error) {\n\tmsg, err := r.AnyResolver.Resolve(typeUrl)\n\tif err == nil {\n\t\treturn msg, err\n\t}\n\n\t// Try \"default\" resolution logic. This mirrors the default behavior\n\t// of jsonpb, which checks to see if the given message name is registered\n\t// in the proto package.\n\tmname := typeUrl\n\tif slash := strings.LastIndex(mname, \"/\"); slash >= 0 {\n\t\tmname = mname[slash+1:]\n\t}\n\t//lint:ignore SA1019 new non-deprecated API requires other code changes; deferring...\n\tmt := proto.MessageType(mname)\n\tif mt != nil {\n\t\treturn reflect.New(mt.Elem()).Interface().(proto.Message), nil\n\t}\n\n\t// finally, fallback to a special placeholder that can marshal itself\n\t// to JSON using a special \"@value\" property to show base64-encoded\n\t// data for the embedded message\n\treturn &unknownAny{TypeUrl: typeUrl, Error: fmt.Sprintf(\"%s is not recognized; see @value for raw binary message data\", mname)}, nil\n}\n\ntype unknownAny struct {\n\tTypeUrl string `json:\"@type\"`\n\tError   string `json:\"@error\"`\n\tValue   string `json:\"@value\"`\n}\n\nfunc (a *unknownAny) MarshalJSONPB(jsm *jsonpb.Marshaler) ([]byte, error) {\n\tif jsm.Indent != \"\" {\n\t\treturn json.MarshalIndent(a, \"\", jsm.Indent)\n\t}\n\treturn json.Marshal(a)\n}\n\nfunc (a *unknownAny) Unmarshal(b []byte) error {\n\ta.Value = base64.StdEncoding.EncodeToString(b)\n\treturn nil\n}\n\nfunc (a *unknownAny) Reset() {\n\ta.Value = \"\"\n}\n\nfunc (a *unknownAny) String() string {\n\tb, err := a.MarshalJSONPB(&jsonpb.Marshaler{})\n\tif err != nil {\n\t\treturn fmt.Sprintf(\"ERROR: %v\", err.Error())\n\t}\n\treturn string(b)\n}\n\nfunc (a *unknownAny) ProtoMessage() {\n}\n\nvar _ proto.Message = (*unknownAny)(nil)\n\n// FormatOptions is a set of flags that are passed to a JSON or text formatter.\ntype FormatOptions struct {\n\t// EmitJSONDefaultFields flag, when true, includes empty/default values in the output.\n\t// FormatJSON only flag.\n\tEmitJSONDefaultFields bool\n\n\t// AllowUnknownFields is an option for the parser. When true,\n\t// it accepts input which includes unknown fields. These unknown fields\n\t// are skipped instead of returning an error.\n\t// FormatJSON only flag.\n\tAllowUnknownFields bool\n\n\t// IncludeTextSeparator is true then, when invoked to format multiple messages,\n\t// all messages after the first one will be prefixed with the\n\t// ASCII 'Record Separator' character (0x1E).\n\t// It might be useful when the output is piped to another grpcurl process.\n\t// FormatText only flag.\n\tIncludeTextSeparator bool\n}\n\n// RequestParserAndFormatter returns a request parser and formatter for the\n// given format. The given descriptor source may be used for parsing message\n// data (if needed by the format).\n// It accepts a set of options. The field EmitJSONDefaultFields and IncludeTextSeparator\n// are options for JSON and protobuf text formats, respectively. The AllowUnknownFields field\n// is a JSON-only format flag.\n// Requests will be parsed from the given in.\nfunc RequestParserAndFormatter(format Format, descSource DescriptorSource, in io.Reader, opts FormatOptions) (RequestParser, Formatter, error) {\n\tswitch format {\n\tcase FormatJSON:\n\t\tresolver := AnyResolverFromDescriptorSource(descSource)\n\t\tunmarshaler := jsonpb.Unmarshaler{AnyResolver: resolver, AllowUnknownFields: opts.AllowUnknownFields}\n\t\treturn NewJSONRequestParserWithUnmarshaler(in, unmarshaler), NewJSONFormatter(opts.EmitJSONDefaultFields, anyResolverWithFallback{AnyResolver: resolver}), nil\n\tcase FormatText:\n\t\treturn NewTextRequestParser(in), NewTextFormatter(opts.IncludeTextSeparator), nil\n\tdefault:\n\t\treturn nil, nil, fmt.Errorf(\"unknown format: %s\", format)\n\t}\n}\n\n// RequestParserAndFormatterFor returns a request parser and formatter for the\n// given format. The given descriptor source may be used for parsing message\n// data (if needed by the format). The flags emitJSONDefaultFields and\n// includeTextSeparator are options for JSON and protobuf text formats,\n// respectively. Requests will be parsed from the given in.\n// This function is deprecated. Please use RequestParserAndFormatter instead.\n// DEPRECATED\nfunc RequestParserAndFormatterFor(format Format, descSource DescriptorSource, emitJSONDefaultFields, includeTextSeparator bool, in io.Reader) (RequestParser, Formatter, error) {\n\treturn RequestParserAndFormatter(format, descSource, in, FormatOptions{\n\t\tEmitJSONDefaultFields: emitJSONDefaultFields,\n\t\tIncludeTextSeparator:  includeTextSeparator,\n\t})\n}\n\n// DefaultEventHandler logs events to a writer. This is not thread-safe, but is\n// safe for use with InvokeRPC as long as NumResponses and Status are not read\n// until the call to InvokeRPC completes.\ntype DefaultEventHandler struct {\n\tOut       io.Writer\n\tFormatter Formatter\n\t// 0 = default\n\t// 1 = verbose\n\t// 2 = very verbose\n\tVerbosityLevel int\n\n\t// NumResponses is the number of responses that have been received.\n\tNumResponses int\n\t// Status is the status that was received at the end of an RPC. It is\n\t// nil if the RPC is still in progress.\n\tStatus *status.Status\n}\n\n// NewDefaultEventHandler returns an InvocationEventHandler that logs events to\n// the given output. If verbose is true, all events are logged. Otherwise, only\n// response messages are logged.\n//\n// Deprecated: NewDefaultEventHandler exists for compatibility.\n// It doesn't allow fine control over the `VerbosityLevel`\n// and provides only 0 and 1 options (which corresponds to the `verbose` argument).\n// Use DefaultEventHandler{} initializer directly.\nfunc NewDefaultEventHandler(out io.Writer, descSource DescriptorSource, formatter Formatter, verbose bool) *DefaultEventHandler {\n\tverbosityLevel := 0\n\tif verbose {\n\t\tverbosityLevel = 1\n\t}\n\treturn &DefaultEventHandler{\n\t\tOut:            out,\n\t\tFormatter:      formatter,\n\t\tVerbosityLevel: verbosityLevel,\n\t}\n}\n\nvar _ InvocationEventHandler = (*DefaultEventHandler)(nil)\n\nfunc (h *DefaultEventHandler) OnResolveMethod(md *desc.MethodDescriptor) {\n\tif h.VerbosityLevel > 0 {\n\t\ttxt, err := GetDescriptorText(md, nil)\n\t\tif err == nil {\n\t\t\tfmt.Fprintf(h.Out, \"\\nResolved method descriptor:\\n%s\\n\", txt)\n\t\t}\n\t}\n}\n\nfunc (h *DefaultEventHandler) OnSendHeaders(md metadata.MD) {\n\tif h.VerbosityLevel > 0 {\n\t\tfmt.Fprintf(h.Out, \"\\nRequest metadata to send:\\n%s\\n\", MetadataToString(md))\n\t}\n}\n\nfunc (h *DefaultEventHandler) OnReceiveHeaders(md metadata.MD) {\n\tif h.VerbosityLevel > 0 {\n\t\tfmt.Fprintf(h.Out, \"\\nResponse headers received:\\n%s\\n\", MetadataToString(md))\n\t}\n}\n\nfunc (h *DefaultEventHandler) OnReceiveResponse(resp proto.Message) {\n\th.NumResponses++\n\tif h.VerbosityLevel > 1 {\n\t\tfmt.Fprintf(h.Out, \"\\nEstimated response size: %d bytes\\n\", proto.Size(resp))\n\t}\n\tif h.VerbosityLevel > 0 {\n\t\tfmt.Fprint(h.Out, \"\\nResponse contents:\\n\")\n\t}\n\tif respStr, err := h.Formatter(resp); err != nil {\n\t\tfmt.Fprintf(h.Out, \"Failed to format response message %d: %v\\n\", h.NumResponses, err)\n\t} else {\n\t\tfmt.Fprintln(h.Out, respStr)\n\t}\n}\n\nfunc (h *DefaultEventHandler) OnReceiveTrailers(stat *status.Status, md metadata.MD) {\n\th.Status = stat\n\tif h.VerbosityLevel > 0 {\n\t\tfmt.Fprintf(h.Out, \"\\nResponse trailers received:\\n%s\\n\", MetadataToString(md))\n\t}\n}\n\n// PrintStatus prints details about the given status to the given writer. The given\n// formatter is used to print any detail messages that may be included in the status.\n// If the given status has a code of OK, \"OK\" is printed and that is all. Otherwise,\n// \"ERROR:\" is printed along with a line showing the code, one showing the message\n// string, and each detail message if any are present. The detail messages will be\n// printed as proto text format or JSON, depending on the given formatter.\nfunc PrintStatus(w io.Writer, stat *status.Status, formatter Formatter) {\n\tif stat.Code() == codes.OK {\n\t\tfmt.Fprintln(w, \"OK\")\n\t\treturn\n\t}\n\tfmt.Fprintf(w, \"ERROR:\\n  Code: %s\\n  Message: %s\\n\", stat.Code().String(), stat.Message())\n\n\tstatpb := stat.Proto()\n\tif len(statpb.Details) > 0 {\n\t\tfmt.Fprintf(w, \"  Details:\\n\")\n\t\tfor i, det := range statpb.Details {\n\t\t\tprefix := fmt.Sprintf(\"  %d)\", i+1)\n\t\t\tfmt.Fprintf(w, \"%s\\t\", prefix)\n\t\t\tprefix = strings.Repeat(\" \", len(prefix)) + \"\\t\"\n\n\t\t\toutput, err := formatter(det)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintf(w, \"Error parsing detail message: %v\\n\", err)\n\t\t\t} else {\n\t\t\t\tlines := strings.Split(output, \"\\n\")\n\t\t\t\tfor i, line := range lines {\n\t\t\t\t\tif i == 0 {\n\t\t\t\t\t\t// first line is already indented\n\t\t\t\t\t\tfmt.Fprintf(w, \"%s\\n\", line)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfmt.Fprintf(w, \"%s%s\\n\", prefix, line)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "format_test.go",
    "content": "package grpcurl\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/golang/protobuf/jsonpb\"  //lint:ignore SA1019 we have to import these because some of their types appear in exported API\n\t\"github.com/golang/protobuf/proto\"   //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/desc\" //lint:ignore SA1019 same as above\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/protobuf/types/known/structpb\"\n)\n\nfunc TestRequestParser(t *testing.T) {\n\tsource, err := DescriptorSourceFromProtoSets(\"internal/testing/example.protoset\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create descriptor source: %v\", err)\n\t}\n\n\tmsg, err := makeProto()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create message: %v\", err)\n\t}\n\n\ttestCases := []struct {\n\t\tformat         Format\n\t\tinput          string\n\t\texpectedOutput []proto.Message\n\t}{\n\t\t{\n\t\t\tformat: FormatJSON,\n\t\t\tinput:  \"\",\n\t\t},\n\t\t{\n\t\t\tformat:         FormatJSON,\n\t\t\tinput:          messageAsJSON,\n\t\t\texpectedOutput: []proto.Message{msg},\n\t\t},\n\t\t{\n\t\t\tformat:         FormatJSON,\n\t\t\tinput:          messageAsJSON + messageAsJSON + messageAsJSON,\n\t\t\texpectedOutput: []proto.Message{msg, msg, msg},\n\t\t},\n\t\t{\n\t\t\t// unlike JSON, empty input yields one empty message (vs. zero messages)\n\t\t\tformat:         FormatText,\n\t\t\tinput:          \"\",\n\t\t\texpectedOutput: []proto.Message{&structpb.Value{}},\n\t\t},\n\t\t{\n\t\t\tformat:         FormatText,\n\t\t\tinput:          messageAsText,\n\t\t\texpectedOutput: []proto.Message{msg},\n\t\t},\n\t\t{\n\t\t\tformat:         FormatText,\n\t\t\tinput:          messageAsText + string(textSeparatorChar),\n\t\t\texpectedOutput: []proto.Message{msg, &structpb.Value{}},\n\t\t},\n\t\t{\n\t\t\tformat:         FormatText,\n\t\t\tinput:          messageAsText + string(textSeparatorChar) + messageAsText + string(textSeparatorChar) + messageAsText,\n\t\t\texpectedOutput: []proto.Message{msg, msg, msg},\n\t\t},\n\t}\n\n\tfor i, tc := range testCases {\n\t\tname := fmt.Sprintf(\"#%d, %s, %d message(s)\", i+1, tc.format, len(tc.expectedOutput))\n\t\trf, _, err := RequestParserAndFormatter(tc.format, source, strings.NewReader(tc.input), FormatOptions{})\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Failed to create parser and formatter: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tnumReqs := 0\n\t\tfor {\n\t\t\tvar req structpb.Value\n\t\t\terr := rf.Next(&req)\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t} else if err != nil {\n\t\t\t\tt.Errorf(\"%s, msg %d: unexpected error: %v\", name, numReqs, err)\n\t\t\t}\n\t\t\tif !proto.Equal(&req, tc.expectedOutput[numReqs]) {\n\t\t\t\tt.Errorf(\"%s, msg %d: incorrect message;\\nexpecting:\\n%v\\ngot:\\n%v\", name, numReqs, tc.expectedOutput[numReqs], &req)\n\t\t\t}\n\t\t\tnumReqs++\n\t\t}\n\t\tif rf.NumRequests() != numReqs {\n\t\t\tt.Errorf(\"%s: factory reported wrong number of requests: expecting %d, got %d\", name, numReqs, rf.NumRequests())\n\t\t}\n\t}\n}\n\n// Handler prints response data (and headers/trailers in verbose mode).\n// This verifies that we get the right output in both JSON and proto text modes.\nfunc TestHandler(t *testing.T) {\n\tsource, err := DescriptorSourceFromProtoSets(\"internal/testing/example.protoset\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create descriptor source: %v\", err)\n\t}\n\td, err := source.FindSymbol(\"TestService.GetFiles\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to find method 'TestService.GetFiles': %v\", err)\n\t}\n\tmd, ok := d.(*desc.MethodDescriptor)\n\tif !ok {\n\t\tt.Fatalf(\"wrong kind of descriptor found: %T\", d)\n\t}\n\n\treqHeaders := metadata.Pairs(\"foo\", \"123\", \"bar\", \"456\")\n\trespHeaders := metadata.Pairs(\"foo\", \"abc\", \"bar\", \"def\", \"baz\", \"xyz\")\n\trespTrailers := metadata.Pairs(\"a\", \"1\", \"b\", \"2\", \"c\", \"3\")\n\trsp, err := makeProto()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create response message: %v\", err)\n\t}\n\n\tfor _, format := range []Format{FormatJSON, FormatText} {\n\t\tfor _, numMessages := range []int{1, 3} {\n\t\t\tfor verbosityLevel := 0; verbosityLevel <= 2; verbosityLevel++ {\n\t\t\t\tname := fmt.Sprintf(\"%s, %d message(s)\", format, numMessages)\n\t\t\t\tif verbosityLevel > 0 {\n\t\t\t\t\tname += fmt.Sprintf(\", verbosityLevel=%d\", verbosityLevel)\n\t\t\t\t}\n\n\t\t\t\tverbose := verbosityLevel > 0\n\n\t\t\t\t_, formatter, err := RequestParserAndFormatter(format, source, nil, FormatOptions{IncludeTextSeparator: !verbose})\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"Failed to create parser and formatter: %v\", err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tvar buf bytes.Buffer\n\t\t\t\th := &DefaultEventHandler{\n\t\t\t\t\tOut:            &buf,\n\t\t\t\t\tFormatter:      formatter,\n\t\t\t\t\tVerbosityLevel: verbosityLevel,\n\t\t\t\t}\n\n\t\t\t\th.OnResolveMethod(md)\n\t\t\t\th.OnSendHeaders(reqHeaders)\n\t\t\t\th.OnReceiveHeaders(respHeaders)\n\t\t\t\tfor i := 0; i < numMessages; i++ {\n\t\t\t\t\th.OnReceiveResponse(rsp)\n\t\t\t\t}\n\t\t\t\th.OnReceiveTrailers(nil, respTrailers)\n\n\t\t\t\texpectedOutput := \"\"\n\t\t\t\tif verbose {\n\t\t\t\t\texpectedOutput += verbosePrefix\n\t\t\t\t}\n\t\t\t\tfor i := 0; i < numMessages; i++ {\n\t\t\t\t\tif verbosityLevel > 1 {\n\t\t\t\t\t\texpectedOutput += verboseResponseSize\n\t\t\t\t\t}\n\t\t\t\t\tif verbose {\n\t\t\t\t\t\texpectedOutput += verboseResponseHeader\n\t\t\t\t\t}\n\t\t\t\t\tif format == \"json\" {\n\t\t\t\t\t\texpectedOutput += messageAsJSON\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif i > 0 && !verbose {\n\t\t\t\t\t\t\texpectedOutput += string(textSeparatorChar)\n\t\t\t\t\t\t}\n\t\t\t\t\t\texpectedOutput += messageAsText\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif verbose {\n\t\t\t\t\texpectedOutput += verboseSuffix\n\t\t\t\t}\n\n\t\t\t\tout := buf.String()\n\t\t\t\tif !compare(out, expectedOutput) {\n\t\t\t\t\tt.Errorf(\"%s: Incorrect output. Expected:\\n%s\\nGot:\\n%s\", name, expectedOutput, out)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// compare checks that actual and expected are equal, returning true if so.\n// A simple equality check (==) does not suffice because jsonpb formats\n// structpb.Value strangely. So if that formatting gets fixed, we don't\n// want this test in grpcurl to suddenly start failing. So we check each\n// line and compare the lines after stripping whitespace (which removes\n// the jsonpb format anomalies).\nfunc compare(actual, expected string) bool {\n\tactualLines := strings.Split(actual, \"\\n\")\n\texpectedLines := strings.Split(expected, \"\\n\")\n\tif len(actualLines) != len(expectedLines) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(actualLines); i++ {\n\t\tif strings.TrimSpace(actualLines[i]) != strings.TrimSpace(expectedLines[i]) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc makeProto() (proto.Message, error) {\n\tvar rsp structpb.Value\n\terr := jsonpb.UnmarshalString(`{\n\t\t\"foo\": [\"abc\", \"def\", \"ghi\"],\n\t\t\"bar\": { \"a\": 1, \"b\": 2 },\n\t\t\"baz\": true,\n\t\t\"null\": null\n\t}`, &rsp)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &rsp, nil\n}\n\nvar (\n\tverbosePrefix = `\nResolved method descriptor:\nrpc GetFiles ( .TestRequest ) returns ( .TestResponse );\n\nRequest metadata to send:\nbar: 456\nfoo: 123\n\nResponse headers received:\nbar: def\nbaz: xyz\nfoo: abc\n`\n\tverboseSuffix = `\nResponse trailers received:\na: 1\nb: 2\nc: 3\n`\n\tverboseResponseSize = `\nEstimated response size: 100 bytes\n`\n\tverboseResponseHeader = `\nResponse contents:\n`\n\tmessageAsJSON = `{\n  \"bar\": {\n    \"a\": 1,\n    \"b\": 2\n  },\n  \"baz\": true,\n  \"foo\": [\n    \"abc\",\n    \"def\",\n    \"ghi\"\n  ],\n  \"null\": null\n}\n`\n\tmessageAsText = `struct_value: <\n  fields: <\n    key: \"bar\"\n    value: <\n      struct_value: <\n        fields: <\n          key: \"a\"\n          value: <\n            number_value: 1\n          >\n        >\n        fields: <\n          key: \"b\"\n          value: <\n            number_value: 2\n          >\n        >\n      >\n    >\n  >\n  fields: <\n    key: \"baz\"\n    value: <\n      bool_value: true\n    >\n  >\n  fields: <\n    key: \"foo\"\n    value: <\n      list_value: <\n        values: <\n          string_value: \"abc\"\n        >\n        values: <\n          string_value: \"def\"\n        >\n        values: <\n          string_value: \"ghi\"\n        >\n      >\n    >\n  >\n  fields: <\n    key: \"null\"\n    value: <\n      null_value: NULL_VALUE\n    >\n  >\n>\n`\n)\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/fullstorydev/grpcurl\n\ngo 1.24.0\n\ntoolchain go1.24.1\n\nrequire (\n\tgithub.com/golang/protobuf v1.5.4\n\tgithub.com/jhump/protoreflect v1.18.0\n\tgoogle.golang.org/grpc v1.66.2\n\tgoogle.golang.org/protobuf v1.36.11\n)\n\nrequire (\n\tcel.dev/expr v0.15.0 // indirect\n\tcloud.google.com/go/compute/metadata v0.3.0 // indirect\n\tgithub.com/census-instrumentation/opencensus-proto v0.4.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect\n\tgithub.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 // indirect\n\tgithub.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect\n\tgithub.com/jhump/protoreflect/v2 v2.0.0-beta.1 // indirect\n\tgithub.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect\n\tgithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect\n\tgolang.org/x/net v0.38.0 // indirect\n\tgolang.org/x/oauth2 v0.27.0 // indirect\n\tgolang.org/x/sync v0.12.0 // indirect\n\tgolang.org/x/sys v0.31.0 // indirect\n\tgolang.org/x/text v0.23.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w=\ncel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg=\ncloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=\ncloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=\ngithub.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=\ngithub.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=\ngithub.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=\ngithub.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw=\ngithub.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=\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/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc=\ngithub.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA=\ngithub.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=\ngithub.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/jhump/protoreflect v1.18.0 h1:TOz0MSR/0JOZ5kECB/0ufGnC2jdsgZ123Rd/k4Z5/2w=\ngithub.com/jhump/protoreflect v1.18.0/go.mod h1:ezWcltJIVF4zYdIFM+D/sHV4Oh5LNU08ORzCGfwvTz8=\ngithub.com/jhump/protoreflect/v2 v2.0.0-beta.1 h1:Dw1rslK/VotaUGYsv53XVWITr+5RCPXfvvlGrM/+B6w=\ngithub.com/jhump/protoreflect/v2 v2.0.0-beta.1/go.mod h1:D9LBEowZyv8/iSu97FU2zmXG3JxVTmNw21mu63niFzU=\ngithub.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14=\ngithub.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=\ngithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=\ngithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=\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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngolang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=\ngolang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=\ngolang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=\ngolang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=\ngolang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=\ngolang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=\ngolang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=\ngolang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=\ngolang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=\ngolang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=\ngoogle.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=\ngoogle.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "grpcurl.go",
    "content": "// Package grpcurl provides the core functionality exposed by the grpcurl command, for\n// dynamically connecting to a server, using the reflection service to inspect the server,\n// and invoking RPCs. The grpcurl command-line tool constructs a DescriptorSource, based\n// on the command-line parameters, and supplies an InvocationEventHandler to supply request\n// data (which can come from command-line args or the process's stdin) and to log the\n// events (to the process's stdout).\npackage grpcurl\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"regexp\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/golang/protobuf/proto\"   //lint:ignore SA1019 we have to import these because some of their types appear in exported API\n\t\"github.com/jhump/protoreflect/desc\" //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/desc/protoprint\"\n\t\"github.com/jhump/protoreflect/dynamic\" //lint:ignore SA1019 same as above\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials\"\n\t\"google.golang.org/grpc/credentials/insecure\"\n\txdsCredentials \"google.golang.org/grpc/credentials/xds\"\n\t_ \"google.golang.org/grpc/health\" // import grpc/health to enable transparent client side checking\n\t\"google.golang.org/grpc/metadata\"\n\tprotov2 \"google.golang.org/protobuf/proto\"\n\t\"google.golang.org/protobuf/types/descriptorpb\"\n\t\"google.golang.org/protobuf/types/known/anypb\"\n\t\"google.golang.org/protobuf/types/known/emptypb\"\n\t\"google.golang.org/protobuf/types/known/structpb\"\n)\n\n// ListServices uses the given descriptor source to return a sorted list of fully-qualified\n// service names.\nfunc ListServices(source DescriptorSource) ([]string, error) {\n\tsvcs, err := source.ListServices()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsort.Strings(svcs)\n\treturn svcs, nil\n}\n\ntype sourceWithFiles interface {\n\tGetAllFiles() ([]*desc.FileDescriptor, error)\n}\n\nvar _ sourceWithFiles = (*fileSource)(nil)\n\n// GetAllFiles uses the given descriptor source to return a list of file descriptors.\nfunc GetAllFiles(source DescriptorSource) ([]*desc.FileDescriptor, error) {\n\tvar files []*desc.FileDescriptor\n\tsrcFiles, ok := source.(sourceWithFiles)\n\n\t// If an error occurs, we still try to load as many files as we can, so that\n\t// caller can decide whether to ignore error or not.\n\tvar firstError error\n\tif ok {\n\t\tfiles, firstError = srcFiles.GetAllFiles()\n\t} else {\n\t\t// Source does not implement GetAllFiles method, so use ListServices\n\t\t// and grab files from there.\n\t\tsvcNames, err := source.ListServices()\n\t\tif err != nil {\n\t\t\tfirstError = err\n\t\t} else {\n\t\t\tallFiles := map[string]*desc.FileDescriptor{}\n\t\t\tfor _, name := range svcNames {\n\t\t\t\td, err := source.FindSymbol(name)\n\t\t\t\tif err != nil {\n\t\t\t\t\tif firstError == nil {\n\t\t\t\t\t\tfirstError = err\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\taddAllFilesToSet(d.GetFile(), allFiles)\n\t\t\t\t}\n\t\t\t}\n\t\t\tfiles = make([]*desc.FileDescriptor, len(allFiles))\n\t\t\ti := 0\n\t\t\tfor _, fd := range allFiles {\n\t\t\t\tfiles[i] = fd\n\t\t\t\ti++\n\t\t\t}\n\t\t}\n\t}\n\n\tsort.Sort(filesByName(files))\n\treturn files, firstError\n}\n\ntype filesByName []*desc.FileDescriptor\n\nfunc (f filesByName) Len() int {\n\treturn len(f)\n}\n\nfunc (f filesByName) Less(i, j int) bool {\n\treturn f[i].GetName() < f[j].GetName()\n}\n\nfunc (f filesByName) Swap(i, j int) {\n\tf[i], f[j] = f[j], f[i]\n}\n\nfunc addAllFilesToSet(fd *desc.FileDescriptor, all map[string]*desc.FileDescriptor) {\n\tif _, ok := all[fd.GetName()]; ok {\n\t\t// already added\n\t\treturn\n\t}\n\tall[fd.GetName()] = fd\n\tfor _, dep := range fd.GetDependencies() {\n\t\taddAllFilesToSet(dep, all)\n\t}\n}\n\n// ListMethods uses the given descriptor source to return a sorted list of method names\n// for the specified fully-qualified service name.\nfunc ListMethods(source DescriptorSource, serviceName string) ([]string, error) {\n\tdsc, err := source.FindSymbol(serviceName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif sd, ok := dsc.(*desc.ServiceDescriptor); !ok {\n\t\treturn nil, notFound(\"Service\", serviceName)\n\t} else {\n\t\tmethods := make([]string, 0, len(sd.GetMethods()))\n\t\tfor _, method := range sd.GetMethods() {\n\t\t\tmethods = append(methods, method.GetFullyQualifiedName())\n\t\t}\n\t\tsort.Strings(methods)\n\t\treturn methods, nil\n\t}\n}\n\n// MetadataFromHeaders converts a list of header strings (each string in\n// \"Header-Name: Header-Value\" form) into metadata. If a string has a header\n// name without a value (e.g. does not contain a colon), the value is assumed\n// to be blank. Binary headers (those whose names end in \"-bin\") should be\n// base64-encoded. But if they cannot be base64-decoded, they will be assumed to\n// be in raw form and used as is.\nfunc MetadataFromHeaders(headers []string) metadata.MD {\n\tmd := make(metadata.MD)\n\tfor _, part := range headers {\n\t\tif part != \"\" {\n\t\t\tpieces := strings.SplitN(part, \":\", 2)\n\t\t\tif len(pieces) == 1 {\n\t\t\t\tpieces = append(pieces, \"\") // if no value was specified, just make it \"\" (maybe the header value doesn't matter)\n\t\t\t}\n\t\t\theaderName := strings.ToLower(strings.TrimSpace(pieces[0]))\n\t\t\tval := strings.TrimSpace(pieces[1])\n\t\t\tif strings.HasSuffix(headerName, \"-bin\") {\n\t\t\t\tif v, err := decode(val); err == nil {\n\t\t\t\t\tval = v\n\t\t\t\t}\n\t\t\t}\n\t\t\tmd[headerName] = append(md[headerName], val)\n\t\t}\n\t}\n\treturn md\n}\n\nvar envVarRegex = regexp.MustCompile(`\\${\\w+}`)\n\n// ExpandHeaders expands environment variables contained in the header string.\n// If no corresponding environment variable is found an error is returned.\n// TODO: Add escaping for `${`\nfunc ExpandHeaders(headers []string) ([]string, error) {\n\texpandedHeaders := make([]string, len(headers))\n\tfor idx, header := range headers {\n\t\tif header == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tresults := envVarRegex.FindAllString(header, -1)\n\t\tif len(results) == 0 {\n\t\t\texpandedHeaders[idx] = headers[idx]\n\t\t\tcontinue\n\t\t}\n\t\texpandedHeader := header\n\t\tfor _, result := range results {\n\t\t\tenvVarName := result[2 : len(result)-1] // strip leading `${` and trailing `}`\n\t\t\tenvVarValue, ok := os.LookupEnv(envVarName)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"header %q refers to missing environment variable %q\", header, envVarName)\n\t\t\t}\n\t\t\texpandedHeader = strings.Replace(expandedHeader, result, envVarValue, -1)\n\t\t}\n\t\texpandedHeaders[idx] = expandedHeader\n\t}\n\treturn expandedHeaders, nil\n}\n\nvar base64Codecs = []*base64.Encoding{base64.StdEncoding, base64.URLEncoding, base64.RawStdEncoding, base64.RawURLEncoding}\n\nfunc decode(val string) (string, error) {\n\tvar firstErr error\n\tvar b []byte\n\t// we are lenient and can accept any of the flavors of base64 encoding\n\tfor _, d := range base64Codecs {\n\t\tvar err error\n\t\tb, err = d.DecodeString(val)\n\t\tif err != nil {\n\t\t\tif firstErr == nil {\n\t\t\t\tfirstErr = err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\treturn string(b), nil\n\t}\n\treturn \"\", firstErr\n}\n\n// MetadataToString returns a string representation of the given metadata, for\n// displaying to users.\nfunc MetadataToString(md metadata.MD) string {\n\tif len(md) == 0 {\n\t\treturn \"(empty)\"\n\t}\n\n\tkeys := make([]string, 0, len(md))\n\tfor k := range md {\n\t\tkeys = append(keys, k)\n\t}\n\tsort.Strings(keys)\n\n\tvar b bytes.Buffer\n\tfirst := true\n\tfor _, k := range keys {\n\t\tvs := md[k]\n\t\tfor _, v := range vs {\n\t\t\tif first {\n\t\t\t\tfirst = false\n\t\t\t} else {\n\t\t\t\tb.WriteString(\"\\n\")\n\t\t\t}\n\t\t\tb.WriteString(k)\n\t\t\tb.WriteString(\": \")\n\t\t\tif strings.HasSuffix(k, \"-bin\") {\n\t\t\t\tv = base64.StdEncoding.EncodeToString([]byte(v))\n\t\t\t}\n\t\t\tb.WriteString(v)\n\t\t}\n\t}\n\treturn b.String()\n}\n\nvar printer = &protoprint.Printer{\n\tCompact:                  true,\n\tOmitComments:             protoprint.CommentsNonDoc,\n\tSortElements:             true,\n\tForceFullyQualifiedNames: true,\n}\n\n// GetDescriptorText returns a string representation of the given descriptor.\n// This returns a snippet of proto source that describes the given element.\nfunc GetDescriptorText(dsc desc.Descriptor, _ DescriptorSource) (string, error) {\n\t// Note: DescriptorSource is not used, but remains an argument for backwards\n\t// compatibility with previous implementation.\n\ttxt, err := printer.PrintProtoToString(dsc)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\t// callers don't expect trailing newlines\n\tif txt[len(txt)-1] == '\\n' {\n\t\ttxt = txt[:len(txt)-1]\n\t}\n\treturn txt, nil\n}\n\n// EnsureExtensions uses the given descriptor source to download extensions for\n// the given message. It returns a copy of the given message, but as a dynamic\n// message that knows about all extensions known to the given descriptor source.\nfunc EnsureExtensions(source DescriptorSource, msg proto.Message) proto.Message {\n\t// load any server extensions so we can properly describe custom options\n\tdsc, err := desc.LoadMessageDescriptorForMessage(msg)\n\tif err != nil {\n\t\treturn msg\n\t}\n\n\tvar ext dynamic.ExtensionRegistry\n\tif err = fetchAllExtensions(source, &ext, dsc, map[string]bool{}); err != nil {\n\t\treturn msg\n\t}\n\n\t// convert message into dynamic message that knows about applicable extensions\n\t// (that way we can show meaningful info for custom options instead of printing as unknown)\n\tmsgFactory := dynamic.NewMessageFactoryWithExtensionRegistry(&ext)\n\tdm, err := fullyConvertToDynamic(msgFactory, msg)\n\tif err != nil {\n\t\treturn msg\n\t}\n\treturn dm\n}\n\n// fetchAllExtensions recursively fetches from the server extensions for the given message type as well as\n// for all message types of nested fields. The extensions are added to the given dynamic registry of extensions\n// so that all server-known extensions can be correctly parsed by grpcurl.\nfunc fetchAllExtensions(source DescriptorSource, ext *dynamic.ExtensionRegistry, md *desc.MessageDescriptor, alreadyFetched map[string]bool) error {\n\tmsgTypeName := md.GetFullyQualifiedName()\n\tif alreadyFetched[msgTypeName] {\n\t\treturn nil\n\t}\n\talreadyFetched[msgTypeName] = true\n\tif len(md.GetExtensionRanges()) > 0 {\n\t\tfds, err := source.AllExtensionsForType(msgTypeName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to query for extensions of type %s: %v\", msgTypeName, err)\n\t\t}\n\t\tfor _, fd := range fds {\n\t\t\tif err := ext.AddExtension(fd); err != nil {\n\t\t\t\treturn fmt.Errorf(\"could not register extension %s of type %s: %v\", fd.GetFullyQualifiedName(), msgTypeName, err)\n\t\t\t}\n\t\t}\n\t}\n\t// recursively fetch extensions for the types of any message fields\n\tfor _, fd := range md.GetFields() {\n\t\tif fd.GetMessageType() != nil {\n\t\t\terr := fetchAllExtensions(source, ext, fd.GetMessageType(), alreadyFetched)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// fullyConvertToDynamic attempts to convert the given message to a dynamic message as well\n// as any nested messages it may contain as field values. If the given message factory has\n// extensions registered that were not known when the given message was parsed, this effectively\n// allows re-parsing to identify those extensions.\nfunc fullyConvertToDynamic(msgFact *dynamic.MessageFactory, msg proto.Message) (proto.Message, error) {\n\tif _, ok := msg.(*dynamic.Message); ok {\n\t\treturn msg, nil // already a dynamic message\n\t}\n\tmd, err := desc.LoadMessageDescriptorForMessage(msg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnewMsg := msgFact.NewMessage(md)\n\tdm, ok := newMsg.(*dynamic.Message)\n\tif !ok {\n\t\t// if message factory didn't produce a dynamic message, then we should leave msg as is\n\t\treturn msg, nil\n\t}\n\n\tif err := dm.ConvertFrom(msg); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// recursively convert all field values, too\n\tfor _, fd := range md.GetFields() {\n\t\tif fd.IsMap() {\n\t\t\tif fd.GetMapValueType().GetMessageType() != nil {\n\t\t\t\tm := dm.GetField(fd).(map[interface{}]interface{})\n\t\t\t\tfor k, v := range m {\n\t\t\t\t\t// keys can't be nested messages; so we only need to recurse through map values, not keys\n\t\t\t\t\tnewVal, err := fullyConvertToDynamic(msgFact, v.(proto.Message))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\tdm.PutMapField(fd, k, newVal)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if fd.IsRepeated() {\n\t\t\tif fd.GetMessageType() != nil {\n\t\t\t\ts := dm.GetField(fd).([]interface{})\n\t\t\t\tfor i, e := range s {\n\t\t\t\t\tnewVal, err := fullyConvertToDynamic(msgFact, e.(proto.Message))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\tdm.SetRepeatedField(fd, i, newVal)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif fd.GetMessageType() != nil {\n\t\t\t\tv := dm.GetField(fd)\n\t\t\t\tnewVal, err := fullyConvertToDynamic(msgFact, v.(proto.Message))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tdm.SetField(fd, newVal)\n\t\t\t}\n\t\t}\n\t}\n\treturn dm, nil\n}\n\n// MakeTemplate returns a message instance for the given descriptor that is a\n// suitable template for creating an instance of that message in JSON. In\n// particular, it ensures that any repeated fields (which include map fields)\n// are not empty, so they will render with a single element (to show the types\n// and optionally nested fields). It also ensures that nested messages are not\n// nil by setting them to a message that is also fleshed out as a template\n// message.\nfunc MakeTemplate(md *desc.MessageDescriptor) proto.Message {\n\treturn makeTemplate(md, nil)\n}\n\nfunc makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescriptor) proto.Message {\n\tswitch md.GetFullyQualifiedName() {\n\tcase \"google.protobuf.Any\":\n\t\t// empty type URL is not allowed by JSON representation\n\t\t// so we must give it a dummy type\n\t\tvar anyVal anypb.Any\n\t\t_ = anypb.MarshalFrom(&anyVal, &emptypb.Empty{}, protov2.MarshalOptions{})\n\t\treturn &anyVal\n\tcase \"google.protobuf.Value\":\n\t\t// unset kind is not allowed by JSON representation\n\t\t// so we must give it something\n\t\treturn &structpb.Value{\n\t\t\tKind: &structpb.Value_StructValue{StructValue: &structpb.Struct{\n\t\t\t\tFields: map[string]*structpb.Value{\n\t\t\t\t\t\"google.protobuf.Value\": {Kind: &structpb.Value_StringValue{\n\t\t\t\t\t\tStringValue: \"supports arbitrary JSON\",\n\t\t\t\t\t}},\n\t\t\t\t},\n\t\t\t}},\n\t\t}\n\tcase \"google.protobuf.ListValue\":\n\t\treturn &structpb.ListValue{\n\t\t\tValues: []*structpb.Value{\n\t\t\t\t{\n\t\t\t\t\tKind: &structpb.Value_StructValue{StructValue: &structpb.Struct{\n\t\t\t\t\t\tFields: map[string]*structpb.Value{\n\t\t\t\t\t\t\t\"google.protobuf.ListValue\": {Kind: &structpb.Value_StringValue{\n\t\t\t\t\t\t\t\tStringValue: \"is an array of arbitrary JSON values\",\n\t\t\t\t\t\t\t}},\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\tcase \"google.protobuf.Struct\":\n\t\treturn &structpb.Struct{\n\t\t\tFields: map[string]*structpb.Value{\n\t\t\t\t\"google.protobuf.Struct\": {Kind: &structpb.Value_StringValue{\n\t\t\t\t\tStringValue: \"supports arbitrary JSON objects\",\n\t\t\t\t}},\n\t\t\t},\n\t\t}\n\t}\n\n\tdm := dynamic.NewMessage(md)\n\n\t// if the message is a recursive structure, we don't want to blow the stack\n\tif slices.Contains(path, md) {\n\t\t// already visited this type; avoid infinite recursion\n\t\treturn dm\n\t}\n\tpath = append(path, dm.GetMessageDescriptor())\n\n\t// for repeated fields, add a single element with default value\n\t// and for message fields, add a message with all default fields\n\t// that also has non-nil message and non-empty repeated fields\n\n\tfor _, fd := range dm.GetMessageDescriptor().GetFields() {\n\t\tif fd.IsRepeated() {\n\t\t\tswitch fd.GetType() {\n\t\t\tcase descriptorpb.FieldDescriptorProto_TYPE_FIXED32,\n\t\t\t\tdescriptorpb.FieldDescriptorProto_TYPE_UINT32:\n\t\t\t\tdm.AddRepeatedField(fd, uint32(0))\n\n\t\t\tcase descriptorpb.FieldDescriptorProto_TYPE_SFIXED32,\n\t\t\t\tdescriptorpb.FieldDescriptorProto_TYPE_SINT32,\n\t\t\t\tdescriptorpb.FieldDescriptorProto_TYPE_INT32,\n\t\t\t\tdescriptorpb.FieldDescriptorProto_TYPE_ENUM:\n\t\t\t\tdm.AddRepeatedField(fd, int32(0))\n\n\t\t\tcase descriptorpb.FieldDescriptorProto_TYPE_FIXED64,\n\t\t\t\tdescriptorpb.FieldDescriptorProto_TYPE_UINT64:\n\t\t\t\tdm.AddRepeatedField(fd, uint64(0))\n\n\t\t\tcase descriptorpb.FieldDescriptorProto_TYPE_SFIXED64,\n\t\t\t\tdescriptorpb.FieldDescriptorProto_TYPE_SINT64,\n\t\t\t\tdescriptorpb.FieldDescriptorProto_TYPE_INT64:\n\t\t\t\tdm.AddRepeatedField(fd, int64(0))\n\n\t\t\tcase descriptorpb.FieldDescriptorProto_TYPE_STRING:\n\t\t\t\tdm.AddRepeatedField(fd, \"\")\n\n\t\t\tcase descriptorpb.FieldDescriptorProto_TYPE_BYTES:\n\t\t\t\tdm.AddRepeatedField(fd, []byte{})\n\n\t\t\tcase descriptorpb.FieldDescriptorProto_TYPE_BOOL:\n\t\t\t\tdm.AddRepeatedField(fd, false)\n\n\t\t\tcase descriptorpb.FieldDescriptorProto_TYPE_FLOAT:\n\t\t\t\tdm.AddRepeatedField(fd, float32(0))\n\n\t\t\tcase descriptorpb.FieldDescriptorProto_TYPE_DOUBLE:\n\t\t\t\tdm.AddRepeatedField(fd, float64(0))\n\n\t\t\tcase descriptorpb.FieldDescriptorProto_TYPE_MESSAGE,\n\t\t\t\tdescriptorpb.FieldDescriptorProto_TYPE_GROUP:\n\t\t\t\tdm.AddRepeatedField(fd, makeTemplate(fd.GetMessageType(), path))\n\t\t\t}\n\t\t} else if fd.GetMessageType() != nil {\n\t\t\tdm.SetField(fd, makeTemplate(fd.GetMessageType(), path))\n\t\t}\n\t}\n\treturn dm\n}\n\n// ClientTransportCredentials is a helper function that constructs a TLS config with\n// the given properties (see ClientTLSConfig) and then constructs and returns gRPC\n// transport credentials using that config.\n//\n// Deprecated: Use grpcurl.ClientTLSConfig and credentials.NewTLS instead.\nfunc ClientTransportCredentials(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string) (credentials.TransportCredentials, error) {\n\ttlsConf, err := ClientTLSConfig(insecureSkipVerify, cacertFile, clientCertFile, clientKeyFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn credentials.NewTLS(tlsConf), nil\n}\n\n// ClientTLSConfig builds transport-layer config for a gRPC client using the\n// given properties. If cacertFile is blank, only standard trusted certs are used to\n// verify the server certs. If clientCertFile is blank, the client will not use a client\n// certificate. If clientCertFile is not blank then clientKeyFile must not be blank.\nfunc ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string) (*tls.Config, error) {\n\tvar tlsConf tls.Config\n\n\tif clientCertFile != \"\" {\n\t\t// Load the client certificates from disk\n\t\tcertificate, err := tls.LoadX509KeyPair(clientCertFile, clientKeyFile)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not load client key pair: %v\", err)\n\t\t}\n\t\ttlsConf.Certificates = []tls.Certificate{certificate}\n\t}\n\n\tif insecureSkipVerify {\n\t\ttlsConf.InsecureSkipVerify = true\n\t} else if cacertFile != \"\" {\n\t\t// Create a certificate pool from the certificate authority\n\t\tcertPool := x509.NewCertPool()\n\t\tca, err := os.ReadFile(cacertFile)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not read ca certificate: %v\", err)\n\t\t}\n\n\t\t// Append the certificates from the CA\n\t\tif ok := certPool.AppendCertsFromPEM(ca); !ok {\n\t\t\treturn nil, errors.New(\"failed to append ca certs\")\n\t\t}\n\n\t\ttlsConf.RootCAs = certPool\n\t}\n\n\treturn &tlsConf, nil\n}\n\n// ServerTransportCredentials builds transport credentials for a gRPC server using the\n// given properties. If cacertFile is blank, the server will not request client certs\n// unless requireClientCerts is true. When requireClientCerts is false and cacertFile is\n// not blank, the server will verify client certs when presented, but will not require\n// client certs. The serverCertFile and serverKeyFile must both not be blank.\nfunc ServerTransportCredentials(cacertFile, serverCertFile, serverKeyFile string, requireClientCerts bool) (credentials.TransportCredentials, error) {\n\tvar tlsConf tls.Config\n\t// TODO(jh): Remove this line once https://github.com/golang/go/issues/28779 is fixed\n\t// in Go tip. Until then, the recently merged TLS 1.3 support breaks the TLS tests.\n\ttlsConf.MaxVersion = tls.VersionTLS12\n\n\t// Load the server certificates from disk\n\tcertificate, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not load key pair: %v\", err)\n\t}\n\ttlsConf.Certificates = []tls.Certificate{certificate}\n\n\tif cacertFile != \"\" {\n\t\t// Create a certificate pool from the certificate authority\n\t\tcertPool := x509.NewCertPool()\n\t\tca, err := os.ReadFile(cacertFile)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not read ca certificate: %v\", err)\n\t\t}\n\n\t\t// Append the certificates from the CA\n\t\tif ok := certPool.AppendCertsFromPEM(ca); !ok {\n\t\t\treturn nil, errors.New(\"failed to append ca certs\")\n\t\t}\n\n\t\ttlsConf.ClientCAs = certPool\n\t}\n\n\tif requireClientCerts {\n\t\ttlsConf.ClientAuth = tls.RequireAndVerifyClientCert\n\t} else if cacertFile != \"\" {\n\t\ttlsConf.ClientAuth = tls.VerifyClientCertIfGiven\n\t} else {\n\t\ttlsConf.ClientAuth = tls.NoClientCert\n\t}\n\n\treturn credentials.NewTLS(&tlsConf), nil\n}\n\n// BlockingDial is a helper method to dial the given address, using optional TLS credentials,\n// and blocking until the returned connection is ready. If the given credentials are nil, the\n// connection will be insecure (plain-text).\n// The network parameter should be left empty in most cases when your address is a RFC 3986\n// compliant URI. The resolver from grpc-go will resolve the correct network type.\nfunc BlockingDial(ctx context.Context, network, address string, creds credentials.TransportCredentials, opts ...grpc.DialOption) (*grpc.ClientConn, error) {\n\tif creds == nil {\n\t\tcreds = insecure.NewCredentials()\n\t}\n\n\tvar err error\n\tif strings.HasPrefix(address, \"xds:///\") {\n\t\t// The xds:/// prefix is used to signal to the gRPC client to use an xDS server to resolve the\n\t\t// target. The relevant credentials will be automatically pulled from the GRPC_XDS_BOOTSTRAP or\n\t\t// GRPC_XDS_BOOTSTRAP_CONFIG env vars.\n\t\tcreds, err = xdsCredentials.NewClientCredentials(xdsCredentials.ClientOptions{FallbackCreds: creds})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// grpc.Dial doesn't provide any information on permanent connection errors (like\n\t// TLS handshake failures). So in order to provide good error messages, we need a\n\t// custom dialer that can provide that info. That means we manage the TLS handshake.\n\tresult := make(chan interface{}, 1)\n\n\twriteResult := func(res interface{}) {\n\t\t// non-blocking write: we only need the first result\n\t\tselect {\n\t\tcase result <- res:\n\t\tdefault:\n\t\t}\n\t}\n\n\t// custom credentials and dialer will notify on error via the\n\t// writeResult function\n\tcreds = &errSignalingCreds{\n\t\tTransportCredentials: creds,\n\t\twriteResult:          writeResult,\n\t}\n\n\tswitch network {\n\tcase \"\":\n\t\t// no-op, use address as-is\n\tcase \"tcp\":\n\t\tif strings.HasPrefix(address, \"unix://\") {\n\t\t\treturn nil, fmt.Errorf(\"tcp network type cannot use unix address %s\", address)\n\t\t}\n\tcase \"unix\":\n\t\tif !strings.HasPrefix(address, \"unix://\") {\n\t\t\t// prepend unix:// to the address if it's not already there\n\t\t\t// this is to maintain backwards compatibility because the custom dialer is replaced by\n\t\t\t// the default dialer in grpc-go.\n\t\t\t// https://github.com/fullstorydev/grpcurl/pull/480\n\t\t\taddress = \"unix://\" + address\n\t\t}\n\tdefault:\n\t\t// custom dialer for other networks\n\t\tdialer := func(ctx context.Context, address string) (net.Conn, error) {\n\t\t\tconn, err := (&net.Dialer{}).DialContext(ctx, network, address)\n\t\t\tif err != nil {\n\t\t\t\t// capture the error so we can provide a better message\n\t\t\t\twriteResult(err)\n\t\t\t}\n\t\t\treturn conn, err\n\t\t}\n\t\topts = append([]grpc.DialOption{grpc.WithContextDialer(dialer)}, opts...)\n\t}\n\n\t// Even with grpc.FailOnNonTempDialError, this call will usually timeout in\n\t// the face of TLS handshake errors. So we can't rely on grpc.WithBlock() to\n\t// know when we're done. So we run it in a goroutine and then use result\n\t// channel to either get the connection or fail-fast.\n\tgo func() {\n\t\t// We put grpc.FailOnNonTempDialError *before* the explicitly provided\n\t\t// options so that it could be overridden.\n\t\topts = append([]grpc.DialOption{grpc.FailOnNonTempDialError(true)}, opts...)\n\t\t// But we don't want caller to be able to override these two, so we put\n\t\t// them *after* the explicitly provided options.\n\t\topts = append(opts, grpc.WithBlock(), grpc.WithTransportCredentials(creds))\n\n\t\tconn, err := grpc.DialContext(ctx, address, opts...)\n\t\tvar res interface{}\n\t\tif err != nil {\n\t\t\tres = err\n\t\t} else {\n\t\t\tres = conn\n\t\t}\n\t\twriteResult(res)\n\t}()\n\n\tselect {\n\tcase res := <-result:\n\t\tif conn, ok := res.(*grpc.ClientConn); ok {\n\t\t\treturn conn, nil\n\t\t}\n\t\treturn nil, res.(error)\n\tcase <-ctx.Done():\n\t\treturn nil, ctx.Err()\n\t}\n}\n\n// errSignalingCreds is a wrapper around a TransportCredentials value, but\n// it will use the writeResult function to notify on error.\ntype errSignalingCreds struct {\n\tcredentials.TransportCredentials\n\twriteResult func(res interface{})\n}\n\nfunc (c *errSignalingCreds) ClientHandshake(ctx context.Context, addr string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {\n\tconn, auth, err := c.TransportCredentials.ClientHandshake(ctx, addr, rawConn)\n\tif err != nil {\n\t\tc.writeResult(err)\n\t}\n\treturn conn, auth, err\n}\n"
  },
  {
    "path": "grpcurl_test.go",
    "content": "package grpcurl_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/golang/protobuf/jsonpb\"  //lint:ignore SA1019 we have to import these because some of their types appear in exported API\n\t\"github.com/golang/protobuf/proto\"   //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/desc\" //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/grpcreflect\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/credentials/insecure\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/reflection\"\n\t\"google.golang.org/grpc/status\"\n\n\t. \"github.com/fullstorydev/grpcurl\"\n\tgrpcurl_testing \"github.com/fullstorydev/grpcurl/internal/testing\"\n\tjsonpbtest \"github.com/fullstorydev/grpcurl/internal/testing/jsonpb_test_proto\"\n)\n\nvar (\n\tsourceProtoset   DescriptorSource\n\tsourceProtoFiles DescriptorSource\n\tccNoReflect      *grpc.ClientConn\n\n\tsourceReflect DescriptorSource\n\tccReflect     *grpc.ClientConn\n\n\tdescSources []descSourceCase\n)\n\ntype descSourceCase struct {\n\tname        string\n\tsource      DescriptorSource\n\tincludeRefl bool\n}\n\n// NB: These tests intentionally use the deprecated InvokeRpc since that\n// calls the other (non-deprecated InvokeRPC). That allows the tests to\n// easily exercise both functions.\n\nfunc TestMain(m *testing.M) {\n\tvar err error\n\tsourceProtoset, err = DescriptorSourceFromProtoSets(\"internal/testing/test.protoset\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tsourceProtoFiles, err = DescriptorSourceFromProtoFiles([]string{\"internal/testing\"}, \"test.proto\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a server that includes the reflection service\n\tsvrReflect := grpc.NewServer()\n\tgrpcurl_testing.RegisterTestServiceServer(svrReflect, grpcurl_testing.TestServer{})\n\treflection.Register(svrReflect)\n\tvar portReflect int\n\tif l, err := net.Listen(\"tcp\", \"127.0.0.1:0\"); err != nil {\n\t\tpanic(err)\n\t} else {\n\t\tportReflect = l.Addr().(*net.TCPAddr).Port\n\t\tgo svrReflect.Serve(l)\n\t}\n\tdefer svrReflect.Stop()\n\n\t// And a corresponding client\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\tif ccReflect, err = grpc.DialContext(ctx, fmt.Sprintf(\"127.0.0.1:%d\", portReflect),\n\t\tgrpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()); err != nil {\n\t\tpanic(err)\n\t}\n\tdefer ccReflect.Close()\n\trefClient := grpcreflect.NewClientAuto(context.Background(), ccReflect)\n\tdefer refClient.Reset()\n\n\tsourceReflect = DescriptorSourceFromServer(context.Background(), refClient)\n\n\t// Also create a server that does *not* include the reflection service\n\tsvrProtoset := grpc.NewServer()\n\tgrpcurl_testing.RegisterTestServiceServer(svrProtoset, grpcurl_testing.TestServer{})\n\tvar portProtoset int\n\tif l, err := net.Listen(\"tcp\", \"127.0.0.1:0\"); err != nil {\n\t\tpanic(err)\n\t} else {\n\t\tportProtoset = l.Addr().(*net.TCPAddr).Port\n\t\tgo svrProtoset.Serve(l)\n\t}\n\tdefer svrProtoset.Stop()\n\n\t// And a corresponding client\n\tctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\tif ccNoReflect, err = grpc.DialContext(ctx, fmt.Sprintf(\"127.0.0.1:%d\", portProtoset),\n\t\tgrpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()); err != nil {\n\t\tpanic(err)\n\t}\n\tdefer ccNoReflect.Close()\n\n\tdescSources = []descSourceCase{\n\t\t{\"protoset\", sourceProtoset, false},\n\t\t{\"proto\", sourceProtoFiles, false},\n\t\t{\"reflect\", sourceReflect, true},\n\t}\n\n\tos.Exit(m.Run())\n}\n\nfunc TestServerDoesNotSupportReflection(t *testing.T) {\n\trefClient := grpcreflect.NewClientAuto(context.Background(), ccNoReflect)\n\tdefer refClient.Reset()\n\n\trefSource := DescriptorSourceFromServer(context.Background(), refClient)\n\n\t_, err := ListServices(refSource)\n\tif err != ErrReflectionNotSupported {\n\t\tt.Errorf(\"ListServices should have returned ErrReflectionNotSupported; instead got %v\", err)\n\t}\n\n\t_, err = ListMethods(refSource, \"SomeService\")\n\tif err != ErrReflectionNotSupported {\n\t\tt.Errorf(\"ListMethods should have returned ErrReflectionNotSupported; instead got %v\", err)\n\t}\n\n\terr = InvokeRpc(context.Background(), refSource, ccNoReflect, \"FooService/Method\", nil, nil, nil)\n\t// InvokeRpc wraps the error, so we just verify the returned error includes the right message\n\tif err == nil || !strings.Contains(err.Error(), ErrReflectionNotSupported.Error()) {\n\t\tt.Errorf(\"InvokeRpc should have returned ErrReflectionNotSupported; instead got %v\", err)\n\t}\n}\n\nfunc TestProtosetWithImports(t *testing.T) {\n\tsourceProtoset, err := DescriptorSourceFromProtoSets(\"internal/testing/example.protoset\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to load protoset: %v\", err)\n\t}\n\t// really shallow check of the loaded descriptors\n\tif sd, err := sourceProtoset.FindSymbol(\"TestService\"); err != nil {\n\t\tt.Errorf(\"failed to find TestService in protoset: %v\", err)\n\t} else if sd == nil {\n\t\tt.Errorf(\"FindSymbol returned nil for TestService\")\n\t} else if _, ok := sd.(*desc.ServiceDescriptor); !ok {\n\t\tt.Errorf(\"FindSymbol returned wrong kind of descriptor for TestService: %T\", sd)\n\t}\n\tif md, err := sourceProtoset.FindSymbol(\"TestRequest\"); err != nil {\n\t\tt.Errorf(\"failed to find TestRequest in protoset: %v\", err)\n\t} else if md == nil {\n\t\tt.Errorf(\"FindSymbol returned nil for TestRequest\")\n\t} else if _, ok := md.(*desc.MessageDescriptor); !ok {\n\t\tt.Errorf(\"FindSymbol returned wrong kind of descriptor for TestRequest: %T\", md)\n\t}\n}\n\nfunc TestListServices(t *testing.T) {\n\tfor _, ds := range descSources {\n\t\tt.Run(ds.name, func(t *testing.T) {\n\t\t\tdoTestListServices(t, ds.source, ds.includeRefl)\n\t\t})\n\t}\n}\n\nfunc doTestListServices(t *testing.T, source DescriptorSource, includeReflection bool) {\n\tnames, err := ListServices(source)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to list services: %v\", err)\n\t}\n\tvar expected []string\n\tif includeReflection {\n\t\t// when using server reflection, we see the TestService as well as the ServerReflection service\n\t\texpected = []string{\"grpc.reflection.v1.ServerReflection\", \"grpc.reflection.v1alpha.ServerReflection\", \"testing.TestService\"}\n\t} else {\n\t\t// without reflection, we see all services defined in the same test.proto file, which is the\n\t\t// TestService as well as UnimplementedService\n\t\texpected = []string{\"testing.TestService\", \"testing.UnimplementedService\"}\n\t}\n\tif !reflect.DeepEqual(expected, names) {\n\t\tt.Errorf(\"ListServices returned wrong results: wanted %v, got %v\", expected, names)\n\t}\n}\n\nfunc TestListMethods(t *testing.T) {\n\tfor _, ds := range descSources {\n\t\tt.Run(ds.name, func(t *testing.T) {\n\t\t\tdoTestListMethods(t, ds.source, ds.includeRefl)\n\t\t})\n\t}\n}\n\nfunc doTestListMethods(t *testing.T, source DescriptorSource, includeReflection bool) {\n\tnames, err := ListMethods(source, \"testing.TestService\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to list methods for TestService: %v\", err)\n\t}\n\texpected := []string{\n\t\t\"testing.TestService.EmptyCall\",\n\t\t\"testing.TestService.FullDuplexCall\",\n\t\t\"testing.TestService.HalfDuplexCall\",\n\t\t\"testing.TestService.StreamingInputCall\",\n\t\t\"testing.TestService.StreamingOutputCall\",\n\t\t\"testing.TestService.UnaryCall\",\n\t}\n\tif !reflect.DeepEqual(expected, names) {\n\t\tt.Errorf(\"ListMethods returned wrong results: wanted %v, got %v\", expected, names)\n\t}\n\n\tif includeReflection {\n\t\t// when using server reflection, we see the TestService as well as the ServerReflection service\n\t\tnames, err = ListMethods(source, \"grpc.reflection.v1.ServerReflection\")\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to list methods for ServerReflection: %v\", err)\n\t\t}\n\t\texpected = []string{\"grpc.reflection.v1.ServerReflection.ServerReflectionInfo\"}\n\t} else {\n\t\t// without reflection, we see all services defined in the same test.proto file, which is the\n\t\t// TestService as well as UnimplementedService\n\t\tnames, err = ListMethods(source, \"testing.UnimplementedService\")\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to list methods for ServerReflection: %v\", err)\n\t\t}\n\t\texpected = []string{\"testing.UnimplementedService.UnimplementedCall\"}\n\t}\n\tif !reflect.DeepEqual(expected, names) {\n\t\tt.Errorf(\"ListMethods returned wrong results: wanted %v, got %v\", expected, names)\n\t}\n\n\t// force an error\n\t_, err = ListMethods(source, \"FooService\")\n\tif err != nil && !strings.Contains(err.Error(), \"Symbol not found: FooService\") {\n\t\tt.Errorf(\"ListMethods should have returned 'not found' error but instead returned %v\", err)\n\t}\n}\n\nfunc TestGetAllFiles(t *testing.T) {\n\texpectedFiles := []string{\"test.proto\"}\n\texpectedFilesWithReflection := []string{\n\t\t\"grpc/reflection/v1/reflection.proto\", \"grpc/reflection/v1alpha/reflection.proto\", \"test.proto\",\n\t}\n\n\tfor _, ds := range descSources {\n\t\tt.Run(ds.name, func(t *testing.T) {\n\t\t\tfiles, err := GetAllFiles(ds.source)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"failed to get all files: %v\", err)\n\t\t\t}\n\t\t\tnames := fileNames(files)\n\t\t\tmatch := false\n\t\t\tvar expected []string\n\t\t\tif ds.includeRefl {\n\t\t\t\texpected = expectedFilesWithReflection\n\t\t\t} else {\n\t\t\t\texpected = expectedFiles\n\t\t\t}\n\t\t\tmatch = reflect.DeepEqual(expected, names)\n\t\t\tif !match {\n\t\t\t\tt.Errorf(\"GetAllFiles returned wrong results: wanted %v, got %v\", expected, names)\n\t\t\t}\n\t\t})\n\t}\n\n\t// try cases with more complicated set of files\n\totherSourceProtoset, err := DescriptorSourceFromProtoSets(\"internal/testing/test.protoset\", \"internal/testing/example.protoset\")\n\tif err != nil {\n\t\tt.Fatal(err.Error())\n\t}\n\totherSourceProtoFiles, err := DescriptorSourceFromProtoFiles([]string{\"internal/testing\"}, \"test.proto\", \"example.proto\")\n\tif err != nil {\n\t\tt.Fatal(err.Error())\n\t}\n\totherDescSources := []descSourceCase{\n\t\t{\"protoset[b]\", otherSourceProtoset, false},\n\t\t{\"proto[b]\", otherSourceProtoFiles, false},\n\t}\n\texpectedFiles = []string{\n\t\t\"example.proto\",\n\t\t\"example2.proto\",\n\t\t\"google/protobuf/any.proto\",\n\t\t\"google/protobuf/descriptor.proto\",\n\t\t\"google/protobuf/empty.proto\",\n\t\t\"google/protobuf/timestamp.proto\",\n\t\t\"test.proto\",\n\t}\n\tfor _, ds := range otherDescSources {\n\t\tt.Run(ds.name, func(t *testing.T) {\n\t\t\tfiles, err := GetAllFiles(ds.source)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"failed to get all files: %v\", err)\n\t\t\t}\n\t\t\tnames := fileNames(files)\n\t\t\tif !reflect.DeepEqual(expectedFiles, names) {\n\t\t\t\tt.Errorf(\"GetAllFiles returned wrong results: wanted %v, got %v\", expectedFiles, names)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExpandHeaders(t *testing.T) {\n\tinHeaders := []string{\"key1: ${value}\", \"key2: bar\", \"key3: ${woo\", \"key4: woo}\", \"key5: ${TEST}\",\n\t\t\"key6: ${TEST_VAR}\", \"${TEST}: ${TEST_VAR}\", \"key8: ${EMPTY}\"}\n\tos.Setenv(\"value\", \"value\")\n\tos.Setenv(\"TEST\", \"value5\")\n\tos.Setenv(\"TEST_VAR\", \"value6\")\n\tos.Setenv(\"EMPTY\", \"\")\n\texpectedHeaders := map[string]bool{\"key1: value\": true, \"key2: bar\": true, \"key3: ${woo\": true, \"key4: woo}\": true,\n\t\t\"key5: value5\": true, \"key6: value6\": true, \"value5: value6\": true, \"key8: \": true}\n\n\toutHeaders, err := ExpandHeaders(inHeaders)\n\tif err != nil {\n\t\tt.Errorf(\"The ExpandHeaders function generated an unexpected error %s\", err)\n\t}\n\tfor _, expandedHeader := range outHeaders {\n\t\tif _, ok := expectedHeaders[expandedHeader]; !ok {\n\t\t\tt.Errorf(\"The ExpandHeaders function has returned an unexpected header. Received unexpected header %s\", expandedHeader)\n\t\t}\n\t}\n\n\tbadHeaders := []string{\"key: ${DNE}\"}\n\t_, err = ExpandHeaders(badHeaders)\n\tif err == nil {\n\t\tt.Errorf(\"The ExpandHeaders function should return an error for missing environment variables %q\", badHeaders)\n\t}\n}\n\nfunc fileNames(files []*desc.FileDescriptor) []string {\n\tnames := make([]string, len(files))\n\tfor i, f := range files {\n\t\tnames[i] = f.GetName()\n\t}\n\treturn names\n}\n\nconst expectKnownType = `{\n  \"dur\": \"0s\",\n  \"ts\": \"1970-01-01T00:00:00Z\",\n  \"dbl\": 0,\n  \"flt\": 0,\n  \"i64\": \"0\",\n  \"u64\": \"0\",\n  \"i32\": 0,\n  \"u32\": 0,\n  \"bool\": false,\n  \"str\": \"\",\n  \"bytes\": null,\n  \"st\": {\"google.protobuf.Struct\": \"supports arbitrary JSON objects\"},\n  \"an\": {\"@type\": \"type.googleapis.com/google.protobuf.Empty\", \"value\": {}},\n  \"lv\": [{\"google.protobuf.ListValue\": \"is an array of arbitrary JSON values\"}],\n  \"val\": {\"google.protobuf.Value\": \"supports arbitrary JSON\"}\n}`\n\nfunc TestMakeTemplateKnownTypes(t *testing.T) {\n\tdescriptor, err := desc.LoadMessageDescriptorForMessage((*jsonpbtest.KnownTypes)(nil))\n\tif err != nil {\n\t\tt.Fatalf(\"failed to load descriptor: %v\", err)\n\t}\n\tmessage := MakeTemplate(descriptor)\n\n\tjsm := jsonpb.Marshaler{EmitDefaults: true}\n\tout, err := jsm.MarshalToString(message)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to marshal to JSON: %v\", err)\n\t}\n\n\t// make sure template JSON matches expected\n\tvar actual, expected interface{}\n\tif err := json.Unmarshal([]byte(out), &actual); err != nil {\n\t\tt.Fatalf(\"failed to parse actual JSON: %v\", err)\n\t}\n\tif err := json.Unmarshal([]byte(expectKnownType), &expected); err != nil {\n\t\tt.Fatalf(\"failed to parse expected JSON: %v\", err)\n\t}\n\n\tif !reflect.DeepEqual(actual, expected) {\n\t\tt.Errorf(\"template message is not as expected; want:\\n%s\\ngot:\\n%s\", expectKnownType, out)\n\t}\n}\n\nfunc TestDescribe(t *testing.T) {\n\tfor _, ds := range descSources {\n\t\tt.Run(ds.name, func(t *testing.T) {\n\t\t\tdoTestDescribe(t, ds.source)\n\t\t})\n\t}\n}\n\nfunc doTestDescribe(t *testing.T, source DescriptorSource) {\n\tsym := \"testing.TestService.EmptyCall\"\n\tdsc, err := source.FindSymbol(sym)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get descriptor for %q: %v\", sym, err)\n\t}\n\tif _, ok := dsc.(*desc.MethodDescriptor); !ok {\n\t\tt.Fatalf(\"descriptor for %q was a %T (expecting a MethodDescriptor)\", sym, dsc)\n\t}\n\ttxt := proto.MarshalTextString(dsc.AsProto())\n\texpected :=\n\t\t`name: \"EmptyCall\"\ninput_type: \".testing.Empty\"\noutput_type: \".testing.Empty\"\n`\n\tif expected != txt {\n\t\tt.Errorf(\"descriptor mismatch: expected %s, got %s\", expected, txt)\n\t}\n\n\tsym = \"testing.StreamingOutputCallResponse\"\n\tdsc, err = source.FindSymbol(sym)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get descriptor for %q: %v\", sym, err)\n\t}\n\tif _, ok := dsc.(*desc.MessageDescriptor); !ok {\n\t\tt.Fatalf(\"descriptor for %q was a %T (expecting a MessageDescriptor)\", sym, dsc)\n\t}\n\ttxt = proto.MarshalTextString(dsc.AsProto())\n\texpected =\n\t\t`name: \"StreamingOutputCallResponse\"\nfield: <\n  name: \"payload\"\n  number: 1\n  label: LABEL_OPTIONAL\n  type: TYPE_MESSAGE\n  type_name: \".testing.Payload\"\n  json_name: \"payload\"\n>\n`\n\tif expected != txt {\n\t\tt.Errorf(\"descriptor mismatch: expected %s, got %s\", expected, txt)\n\t}\n\n\t_, err = source.FindSymbol(\"FooService\")\n\tif err != nil && !strings.Contains(err.Error(), \"Symbol not found: FooService\") {\n\t\tt.Errorf(\"FindSymbol should have returned 'not found' error but instead returned %v\", err)\n\t}\n}\n\nconst (\n\t// type == COMPRESSABLE, but that is default (since it has\n\t// numeric value == 0) and thus doesn't actually get included\n\t// on the wire\n\tpayload1 = `{\n  \"payload\": {\n    \"body\": \"SXQncyBCdXNpbmVzcyBUaW1l\"\n  }\n}`\n\tpayload2 = `{\n  \"payload\": {\n    \"type\": \"RANDOM\",\n    \"body\": \"Rm91eCBkdSBGYUZh\"\n  }\n}`\n\tpayload3 = `{\n  \"payload\": {\n    \"type\": \"UNCOMPRESSABLE\",\n    \"body\": \"SGlwaG9wb3BvdGFtdXMgdnMuIFJoeW1lbm9jZXJvcw==\"\n  }\n}`\n)\n\nfunc getCC(includeRefl bool) *grpc.ClientConn {\n\tif includeRefl {\n\t\treturn ccReflect\n\t} else {\n\t\treturn ccNoReflect\n\t}\n}\n\nfunc TestUnary(t *testing.T) {\n\tfor _, ds := range descSources {\n\t\tt.Run(ds.name, func(t *testing.T) {\n\t\t\tdoTestUnary(t, getCC(ds.includeRefl), ds.source)\n\t\t})\n\t}\n}\n\nfunc doTestUnary(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {\n\t// Success\n\th := &handler{reqMessages: []string{payload1}}\n\terr := InvokeRpc(context.Background(), source, cc, \"testing.TestService/UnaryCall\", makeHeaders(codes.OK), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\tif h.check(t, \"testing.TestService.UnaryCall\", codes.OK, 1, 1) {\n\t\tif h.respMessages[0] != payload1 {\n\t\t\tt.Errorf(\"unexpected response from RPC: expecting %s; got %s\", payload1, h.respMessages[0])\n\t\t}\n\t}\n\n\t// Failure\n\th = &handler{reqMessages: []string{payload1}}\n\terr = InvokeRpc(context.Background(), source, cc, \"testing.TestService/UnaryCall\", makeHeaders(codes.NotFound), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\th.check(t, \"testing.TestService.UnaryCall\", codes.NotFound, 1, 0)\n}\n\nfunc TestClientStream(t *testing.T) {\n\tfor _, ds := range descSources {\n\t\tt.Run(ds.name, func(t *testing.T) {\n\t\t\tdoTestClientStream(t, getCC(ds.includeRefl), ds.source)\n\t\t})\n\t}\n}\n\nfunc doTestClientStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {\n\t// Success\n\th := &handler{reqMessages: []string{payload1, payload2, payload3}}\n\terr := InvokeRpc(context.Background(), source, cc, \"testing.TestService/StreamingInputCall\", makeHeaders(codes.OK), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\tif h.check(t, \"testing.TestService.StreamingInputCall\", codes.OK, 3, 1) {\n\t\texpected :=\n\t\t\t`{\n  \"aggregatedPayloadSize\": 61\n}`\n\t\tif h.respMessages[0] != expected {\n\t\t\tt.Errorf(\"unexpected response from RPC: expecting %s; got %s\", expected, h.respMessages[0])\n\t\t}\n\t}\n\n\t// Fail fast (server rejects as soon as possible)\n\th = &handler{reqMessages: []string{payload1, payload2, payload3}}\n\terr = InvokeRpc(context.Background(), source, cc, \"testing.TestService/StreamingInputCall\", makeHeaders(codes.InvalidArgument), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\th.check(t, \"testing.TestService.StreamingInputCall\", codes.InvalidArgument, -3, 0)\n\n\t// Fail late (server waits until stream is complete to reject)\n\th = &handler{reqMessages: []string{payload1, payload2, payload3}}\n\terr = InvokeRpc(context.Background(), source, cc, \"testing.TestService/StreamingInputCall\", makeHeaders(codes.Internal, true), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\th.check(t, \"testing.TestService.StreamingInputCall\", codes.Internal, 3, 0)\n}\n\nfunc TestServerStream(t *testing.T) {\n\tfor _, ds := range descSources {\n\t\tt.Run(ds.name, func(t *testing.T) {\n\t\t\tdoTestServerStream(t, getCC(ds.includeRefl), ds.source)\n\t\t})\n\t}\n}\n\nfunc doTestServerStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {\n\treq := &grpcurl_testing.StreamingOutputCallRequest{\n\t\tResponseType: grpcurl_testing.PayloadType_COMPRESSABLE,\n\t\tResponseParameters: []*grpcurl_testing.ResponseParameters{\n\t\t\t{Size: 10}, {Size: 20}, {Size: 30}, {Size: 40}, {Size: 50},\n\t\t},\n\t}\n\tpayload, err := (&jsonpb.Marshaler{}).MarshalToString(req)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to construct request: %v\", err)\n\t}\n\n\t// Success\n\th := &handler{reqMessages: []string{payload}}\n\terr = InvokeRpc(context.Background(), source, cc, \"testing.TestService/StreamingOutputCall\", makeHeaders(codes.OK), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\tif h.check(t, \"testing.TestService.StreamingOutputCall\", codes.OK, 1, 5) {\n\t\tresp := &grpcurl_testing.StreamingOutputCallResponse{}\n\t\tfor i, msg := range h.respMessages {\n\t\t\tif err := jsonpb.UnmarshalString(msg, resp); err != nil {\n\t\t\t\tt.Errorf(\"failed to parse response %d: %v\", i+1, err)\n\t\t\t}\n\t\t\tif resp.Payload.GetType() != grpcurl_testing.PayloadType_COMPRESSABLE {\n\t\t\t\tt.Errorf(\"response %d has wrong payload type; expecting %v, got %v\", i, grpcurl_testing.PayloadType_COMPRESSABLE, resp.Payload.Type)\n\t\t\t}\n\t\t\tif len(resp.Payload.Body) != (i+1)*10 {\n\t\t\t\tt.Errorf(\"response %d has wrong payload size; expecting %d, got %d\", i, (i+1)*10, len(resp.Payload.Body))\n\t\t\t}\n\t\t\tresp.Reset()\n\t\t}\n\t}\n\n\t// Fail fast (server rejects as soon as possible)\n\th = &handler{reqMessages: []string{payload}}\n\terr = InvokeRpc(context.Background(), source, cc, \"testing.TestService/StreamingOutputCall\", makeHeaders(codes.Aborted), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\th.check(t, \"testing.TestService.StreamingOutputCall\", codes.Aborted, 1, 0)\n\n\t// Fail late (server waits until stream is complete to reject)\n\th = &handler{reqMessages: []string{payload}}\n\terr = InvokeRpc(context.Background(), source, cc, \"testing.TestService/StreamingOutputCall\", makeHeaders(codes.AlreadyExists, true), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\th.check(t, \"testing.TestService.StreamingOutputCall\", codes.AlreadyExists, 1, 5)\n}\n\nfunc TestHalfDuplexStream(t *testing.T) {\n\tfor _, ds := range descSources {\n\t\tt.Run(ds.name, func(t *testing.T) {\n\t\t\tdoTestHalfDuplexStream(t, getCC(ds.includeRefl), ds.source)\n\t\t})\n\t}\n}\n\nfunc doTestHalfDuplexStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {\n\treqs := []string{payload1, payload2, payload3}\n\n\t// Success\n\th := &handler{reqMessages: reqs}\n\terr := InvokeRpc(context.Background(), source, cc, \"testing.TestService/HalfDuplexCall\", makeHeaders(codes.OK), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\tif h.check(t, \"testing.TestService.HalfDuplexCall\", codes.OK, 3, 3) {\n\t\tfor i, resp := range h.respMessages {\n\t\t\tif resp != reqs[i] {\n\t\t\t\tt.Errorf(\"unexpected response %d from RPC:\\nexpecting %q\\ngot %q\", i, reqs[i], resp)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Fail fast (server rejects as soon as possible)\n\th = &handler{reqMessages: reqs}\n\terr = InvokeRpc(context.Background(), source, cc, \"testing.TestService/HalfDuplexCall\", makeHeaders(codes.Canceled), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\th.check(t, \"testing.TestService.HalfDuplexCall\", codes.Canceled, -3, 0)\n\n\t// Fail late (server waits until stream is complete to reject)\n\th = &handler{reqMessages: reqs}\n\terr = InvokeRpc(context.Background(), source, cc, \"testing.TestService/HalfDuplexCall\", makeHeaders(codes.DataLoss, true), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\th.check(t, \"testing.TestService.HalfDuplexCall\", codes.DataLoss, 3, 3)\n}\n\nfunc TestFullDuplexStream(t *testing.T) {\n\tfor _, ds := range descSources {\n\t\tt.Run(ds.name, func(t *testing.T) {\n\t\t\tdoTestFullDuplexStream(t, getCC(ds.includeRefl), ds.source)\n\t\t})\n\t}\n}\n\nfunc doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {\n\treqs := make([]string, 3)\n\treq := &grpcurl_testing.StreamingOutputCallRequest{\n\t\tResponseType: grpcurl_testing.PayloadType_RANDOM,\n\t}\n\tfor i := range reqs {\n\t\treq.ResponseParameters = append(req.ResponseParameters, &grpcurl_testing.ResponseParameters{Size: int32((i + 1) * 10)})\n\t\tpayload, err := (&jsonpb.Marshaler{}).MarshalToString(req)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to construct request %d: %v\", i, err)\n\t\t}\n\t\treqs[i] = payload\n\t}\n\n\t// Success\n\th := &handler{reqMessages: reqs}\n\terr := InvokeRpc(context.Background(), source, cc, \"testing.TestService/FullDuplexCall\", makeHeaders(codes.OK), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\tif h.check(t, \"testing.TestService.FullDuplexCall\", codes.OK, 3, 6) {\n\t\tresp := &grpcurl_testing.StreamingOutputCallResponse{}\n\t\ti := 0\n\t\tfor j := 1; j < 3; j++ {\n\t\t\t// three requests\n\t\t\tfor k := 0; k < j; k++ {\n\t\t\t\t// 1 response for first request, 2 for second, etc\n\t\t\t\tmsg := h.respMessages[i]\n\t\t\t\tif err := jsonpb.UnmarshalString(msg, resp); err != nil {\n\t\t\t\t\tt.Errorf(\"failed to parse response %d: %v\", i+1, err)\n\t\t\t\t}\n\t\t\t\tif resp.Payload.GetType() != grpcurl_testing.PayloadType_RANDOM {\n\t\t\t\t\tt.Errorf(\"response %d has wrong payload type; expecting %v, got %v\", i, grpcurl_testing.PayloadType_RANDOM, resp.Payload.Type)\n\t\t\t\t}\n\t\t\t\tif len(resp.Payload.Body) != (k+1)*10 {\n\t\t\t\t\tt.Errorf(\"response %d has wrong payload size; expecting %d, got %d\", i, (k+1)*10, len(resp.Payload.Body))\n\t\t\t\t}\n\t\t\t\tresp.Reset()\n\n\t\t\t\ti++\n\t\t\t}\n\t\t}\n\t}\n\n\t// Fail fast (server rejects as soon as possible)\n\th = &handler{reqMessages: reqs}\n\terr = InvokeRpc(context.Background(), source, cc, \"testing.TestService/FullDuplexCall\", makeHeaders(codes.PermissionDenied), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\th.check(t, \"testing.TestService.FullDuplexCall\", codes.PermissionDenied, -3, 0)\n\n\t// Fail late (server waits until stream is complete to reject)\n\th = &handler{reqMessages: reqs}\n\terr = InvokeRpc(context.Background(), source, cc, \"testing.TestService/FullDuplexCall\", makeHeaders(codes.ResourceExhausted, true), h, h.getRequestData)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error during RPC: %v\", err)\n\t}\n\n\th.check(t, \"testing.TestService.FullDuplexCall\", codes.ResourceExhausted, 3, 6)\n}\n\ntype handler struct {\n\tmethod            *desc.MethodDescriptor\n\tmethodCount       int\n\treqHeaders        metadata.MD\n\treqHeadersCount   int\n\treqMessages       []string\n\treqMessagesCount  int\n\trespHeaders       metadata.MD\n\trespHeadersCount  int\n\trespMessages      []string\n\trespTrailers      metadata.MD\n\trespStatus        *status.Status\n\trespTrailersCount int\n}\n\nfunc (h *handler) getRequestData() ([]byte, error) {\n\t// we don't use a mutex, though this method will be called from different goroutine\n\t// than other methods for bidi calls, because this method does not share any state\n\t// with the other methods.\n\th.reqMessagesCount++\n\tif h.reqMessagesCount > len(h.reqMessages) {\n\t\treturn nil, io.EOF\n\t}\n\tif h.reqMessagesCount > 1 {\n\t\t// insert delay between messages in request stream\n\t\ttime.Sleep(time.Millisecond * 50)\n\t}\n\treturn []byte(h.reqMessages[h.reqMessagesCount-1]), nil\n}\n\nfunc (h *handler) OnResolveMethod(md *desc.MethodDescriptor) {\n\th.methodCount++\n\th.method = md\n}\n\nfunc (h *handler) OnSendHeaders(md metadata.MD) {\n\th.reqHeadersCount++\n\th.reqHeaders = md\n}\n\nfunc (h *handler) OnReceiveHeaders(md metadata.MD) {\n\th.respHeadersCount++\n\th.respHeaders = md\n}\n\nfunc (h *handler) OnReceiveResponse(msg proto.Message) {\n\tjsm := jsonpb.Marshaler{Indent: \"  \"}\n\trespStr, err := jsm.MarshalToString(msg)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to generate JSON form of response message: %v\", err))\n\t}\n\th.respMessages = append(h.respMessages, respStr)\n}\n\nfunc (h *handler) OnReceiveTrailers(stat *status.Status, md metadata.MD) {\n\th.respTrailersCount++\n\th.respTrailers = md\n\th.respStatus = stat\n}\n\nfunc (h *handler) check(t *testing.T, expectedMethod string, expectedCode codes.Code, expectedRequestQueries, expectedResponses int) bool {\n\t// verify a few things were only ever called once\n\tif h.methodCount != 1 {\n\t\tt.Errorf(\"expected grpcurl to invoke OnResolveMethod once; was %d\", h.methodCount)\n\t}\n\tif h.reqHeadersCount != 1 {\n\t\tt.Errorf(\"expected grpcurl to invoke OnSendHeaders once; was %d\", h.reqHeadersCount)\n\t}\n\tif h.reqHeadersCount != 1 {\n\t\tt.Errorf(\"expected grpcurl to invoke OnSendHeaders once; was %d\", h.reqHeadersCount)\n\t}\n\tif h.respHeadersCount != 1 {\n\t\tt.Errorf(\"expected grpcurl to invoke OnReceiveHeaders once; was %d\", h.respHeadersCount)\n\t}\n\tif h.respTrailersCount != 1 {\n\t\tt.Errorf(\"expected grpcurl to invoke OnReceiveTrailers once; was %d\", h.respTrailersCount)\n\t}\n\n\t// check other stuff against given expectations\n\tif h.method.GetFullyQualifiedName() != expectedMethod {\n\t\tt.Errorf(\"wrong method: expecting %v, got %v\", expectedMethod, h.method.GetFullyQualifiedName())\n\t}\n\tif h.respStatus.Code() != expectedCode {\n\t\tt.Errorf(\"wrong code: expecting %v, got %v\", expectedCode, h.respStatus.Code())\n\t}\n\tif expectedRequestQueries < 0 {\n\t\t// negative expectation means \"negate and expect up to that number; could be fewer\"\n\t\tif h.reqMessagesCount > -expectedRequestQueries+1 {\n\t\t\t// the + 1 is because there will be an extra query that returns EOF\n\t\t\tt.Errorf(\"wrong number of messages queried: expecting no more than %v, got %v\", -expectedRequestQueries, h.reqMessagesCount-1)\n\t\t}\n\t} else {\n\t\tif h.reqMessagesCount != expectedRequestQueries+1 {\n\t\t\t// the + 1 is because there will be an extra query that returns EOF\n\t\t\tt.Errorf(\"wrong number of messages queried: expecting %v, got %v\", expectedRequestQueries, h.reqMessagesCount-1)\n\t\t}\n\t}\n\tif len(h.respMessages) != expectedResponses {\n\t\tt.Errorf(\"wrong number of messages received: expecting %v, got %v\", expectedResponses, len(h.respMessages))\n\t}\n\n\t// also check headers and trailers came through as expected\n\tv := h.respHeaders[\"some-fake-header-1\"]\n\tif len(v) != 1 || v[0] != \"val1\" {\n\t\tt.Errorf(\"wrong request header for %q: %v\", \"some-fake-header-1\", v)\n\t}\n\tv = h.respHeaders[\"some-fake-header-2\"]\n\tif len(v) != 1 || v[0] != \"val2\" {\n\t\tt.Errorf(\"wrong request header for %q: %v\", \"some-fake-header-2\", v)\n\t}\n\tv = h.respTrailers[\"some-fake-trailer-1\"]\n\tif len(v) != 1 || v[0] != \"valA\" {\n\t\tt.Errorf(\"wrong request header for %q: %v\", \"some-fake-trailer-1\", v)\n\t}\n\tv = h.respTrailers[\"some-fake-trailer-2\"]\n\tif len(v) != 1 || v[0] != \"valB\" {\n\t\tt.Errorf(\"wrong request header for %q: %v\", \"some-fake-trailer-2\", v)\n\t}\n\n\treturn len(h.respMessages) == expectedResponses\n}\n\nfunc makeHeaders(code codes.Code, failLate ...bool) []string {\n\tif len(failLate) > 1 {\n\t\tpanic(\"incorrect use of makeContext; should be at most one failLate flag\")\n\t}\n\n\thdrs := append(make([]string, 0, 5),\n\t\tfmt.Sprintf(\"%s: %s\", grpcurl_testing.MetadataReplyHeaders, \"some-fake-header-1: val1\"),\n\t\tfmt.Sprintf(\"%s: %s\", grpcurl_testing.MetadataReplyHeaders, \"some-fake-header-2: val2\"),\n\t\tfmt.Sprintf(\"%s: %s\", grpcurl_testing.MetadataReplyTrailers, \"some-fake-trailer-1: valA\"),\n\t\tfmt.Sprintf(\"%s: %s\", grpcurl_testing.MetadataReplyTrailers, \"some-fake-trailer-2: valB\"))\n\tif code != codes.OK {\n\t\tif len(failLate) > 0 && failLate[0] {\n\t\t\thdrs = append(hdrs, fmt.Sprintf(\"%s: %d\", grpcurl_testing.MetadataFailLate, code))\n\t\t} else {\n\t\t\thdrs = append(hdrs, fmt.Sprintf(\"%s: %d\", grpcurl_testing.MetadataFailEarly, code))\n\t\t}\n\t}\n\n\treturn hdrs\n}\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/README.md",
    "content": "# bankdemo\n\nThe `bankdemo` program is an example gRPC server that was used to demo `grpcurl` at Gophercon 2018.\n\nIt demonstrates interesting concepts for building a gRPC server, including chat functionality (that relies on full-duplex bidirectional streams). This code was written specifically to provide an interesting concrete demonstration and, as such, should not be considered in any way production-worthy.\n\nThe demo app tracks user accounts, transactions, and balances completely in memory. Every few seconds, as well as on graceful shutdown (like when the server receives a SIGTERM or SIGINT signal), this state is saved to a file named `accounts.json`, so that the data can be restored if the process restarts.\n\nIn addition to bank account data, the server also tracks \"chat sessions\", for demonstrating bidirectional streams in the form of an application where customers can chat with support agents.\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/auth.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"google.golang.org/grpc/metadata\"\n)\n\nfunc getCustomer(ctx context.Context) string {\n\t// we'll just treat the \"auth token\" as if it is a\n\t// customer ID, but reject tokens that begin with \"agent\"\n\t// (those are auth tokens for support agents, not customers)\n\tcust := getAuthCode(ctx)\n\tif strings.HasPrefix(cust, \"agent\") {\n\t\treturn \"\"\n\t}\n\treturn cust\n}\n\nfunc getAgent(ctx context.Context) string {\n\t// we'll just treat the \"auth token\" as if it is an agent's\n\t// user ID, but reject tokens that don't begin with \"agent\"\n\t// (those are auth tokens for customers, not support agents)\n\tagent := getAuthCode(ctx)\n\tif !strings.HasPrefix(agent, \"agent\") {\n\t\treturn \"\"\n\t}\n\treturn agent\n}\n\nfunc getAuthCode(ctx context.Context) string {\n\tmd, ok := metadata.FromIncomingContext(ctx)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\tvals := md.Get(\"authorization\")\n\tif len(vals) != 1 {\n\t\treturn \"\"\n\t}\n\tpieces := strings.SplitN(strings.ToLower(vals[0]), \" \", 2)\n\tif len(pieces) != 2 {\n\t\treturn \"\"\n\t}\n\tif pieces[0] != \"token\" {\n\t\treturn \"\"\n\t}\n\treturn pieces[1]\n}\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/bank.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/golang/protobuf/ptypes/empty\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n)\n\n// bankServer implements the Bank gRPC service.\ntype bankServer struct {\n\tUnimplementedBankServer\n\tallAccounts *accounts\n}\n\nfunc (s *bankServer) OpenAccount(ctx context.Context, req *OpenAccountRequest) (*Account, error) {\n\tcust := getCustomer(ctx)\n\tif cust == \"\" {\n\t\treturn nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())\n\t}\n\tswitch req.Type {\n\tcase Account_CHECKING, Account_SAVING, Account_MONEY_MARKET:\n\t\tif req.InitialDepositCents < 0 {\n\t\t\treturn nil, status.Errorf(codes.InvalidArgument, \"initial deposit amount cannot be negative: %s\", dollars(req.InitialDepositCents))\n\t\t}\n\tcase Account_LINE_OF_CREDIT, Account_LOAN, Account_EQUITIES:\n\t\tif req.InitialDepositCents != 0 {\n\t\t\treturn nil, status.Errorf(codes.InvalidArgument, \"initial deposit amount must be zero for account type %v: %s\", req.Type, dollars(req.InitialDepositCents))\n\t\t}\n\tdefault:\n\t\treturn nil, status.Errorf(codes.InvalidArgument, \"invalid account type: %v\", req.Type)\n\t}\n\n\treturn s.allAccounts.openAccount(cust, req.Type, req.InitialDepositCents), nil\n}\n\nfunc (s *bankServer) CloseAccount(ctx context.Context, req *CloseAccountRequest) (*empty.Empty, error) {\n\tcust := getCustomer(ctx)\n\tif cust == \"\" {\n\t\treturn nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())\n\t}\n\n\tif err := s.allAccounts.closeAccount(cust, req.AccountNumber); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &empty.Empty{}, nil\n}\n\nfunc (s *bankServer) GetAccounts(ctx context.Context, _ *empty.Empty) (*GetAccountsResponse, error) {\n\tcust := getCustomer(ctx)\n\tif cust == \"\" {\n\t\treturn nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())\n\t}\n\n\taccounts := s.allAccounts.getAllAccounts(cust)\n\treturn &GetAccountsResponse{Accounts: accounts}, nil\n}\n\nfunc (s *bankServer) GetTransactions(req *GetTransactionsRequest, stream Bank_GetTransactionsServer) error {\n\tcust := getCustomer(stream.Context())\n\tif cust == \"\" {\n\t\treturn status.Error(codes.Unauthenticated, codes.Unauthenticated.String())\n\t}\n\n\tacct, err := s.allAccounts.getAccount(cust, req.AccountNumber)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar start, end time.Time\n\tif req.Start != nil {\n\t\terr := req.Start.CheckValid()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tstart = req.Start.AsTime()\n\t}\n\tif req.End != nil {\n\t\terr := req.End.CheckValid()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tend = req.End.AsTime()\n\t} else {\n\t\tend = time.Date(9999, 12, 31, 23, 59, 59, 999999999, time.Local)\n\t}\n\n\ttxns := acct.getTransactions()\n\tfor _, txn := range txns {\n\t\terr := txn.Date.CheckValid()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tt := txn.Date.AsTime()\n\t\tif (t.After(start) || t.Equal(start)) &&\n\t\t\t(t.Before(end) || t.Equal(end)) {\n\n\t\t\tif err := stream.Send(txn); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (s *bankServer) Deposit(ctx context.Context, req *DepositRequest) (*BalanceResponse, error) {\n\tcust := getCustomer(ctx)\n\tif cust == \"\" {\n\t\treturn nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())\n\t}\n\n\tswitch req.Source {\n\tcase DepositRequest_ACH, DepositRequest_CASH, DepositRequest_CHECK, DepositRequest_WIRE:\n\t\t// ok\n\tdefault:\n\t\treturn nil, status.Errorf(codes.InvalidArgument, \"unknown deposit source: %v\", req.Source)\n\t}\n\n\tif req.AmountCents <= 0 {\n\t\treturn nil, status.Errorf(codes.InvalidArgument, \"deposit amount cannot be non-positive: %s\", dollars(req.AmountCents))\n\t}\n\n\tdesc := fmt.Sprintf(\"%v deposit\", req.Source)\n\tif req.Desc != \"\" {\n\t\tdesc = fmt.Sprintf(\"%s: %s\", desc, req.Desc)\n\t}\n\tacct, err := s.allAccounts.getAccount(cust, req.AccountNumber)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnewBalance, err := acct.newTransaction(req.AmountCents, desc)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &BalanceResponse{\n\t\tAccountNumber: req.AccountNumber,\n\t\tBalanceCents:  newBalance,\n\t}, nil\n}\n\nfunc (s *bankServer) Withdraw(ctx context.Context, req *WithdrawRequest) (*BalanceResponse, error) {\n\tcust := getCustomer(ctx)\n\tif cust == \"\" {\n\t\treturn nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())\n\t}\n\n\tif req.AmountCents >= 0 {\n\t\treturn nil, status.Errorf(codes.InvalidArgument, \"withdrawal amount cannot be non-negative: %s\", dollars(req.AmountCents))\n\t}\n\n\tacct, err := s.allAccounts.getAccount(cust, req.AccountNumber)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnewBalance, err := acct.newTransaction(req.AmountCents, req.Desc)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &BalanceResponse{\n\t\tAccountNumber: req.AccountNumber,\n\t\tBalanceCents:  newBalance,\n\t}, nil\n}\n\nfunc (s *bankServer) Transfer(ctx context.Context, req *TransferRequest) (*TransferResponse, error) {\n\tcust := getCustomer(ctx)\n\tif cust == \"\" {\n\t\treturn nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())\n\t}\n\n\tif req.AmountCents <= 0 {\n\t\treturn nil, status.Errorf(codes.InvalidArgument, \"transfer amount cannot be non-positive: %s\", dollars(req.AmountCents))\n\t}\n\n\tvar srcAcct *account\n\tvar srcDesc string\n\tswitch src := req.Source.(type) {\n\tcase *TransferRequest_ExternalSource:\n\t\tsrcDesc = fmt.Sprintf(\"ACH %09d:%06d\", src.ExternalSource.AchRoutingNumber, src.ExternalSource.AchAccountNumber)\n\t\tif src.ExternalSource.AchAccountNumber == 0 || src.ExternalSource.AchRoutingNumber == 0 {\n\t\t\treturn nil, status.Errorf(codes.InvalidArgument, \"external source routing and account numbers cannot be zero: %s\", srcDesc)\n\t\t}\n\tcase *TransferRequest_SourceAccountNumber:\n\t\tsrcDesc = fmt.Sprintf(\"account %06d\", src.SourceAccountNumber)\n\t\tvar err error\n\t\tif srcAcct, err = s.allAccounts.getAccount(cust, src.SourceAccountNumber); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tvar destAcct *account\n\tvar destDesc string\n\tswitch dest := req.Dest.(type) {\n\tcase *TransferRequest_ExternalDest:\n\t\tdestDesc = fmt.Sprintf(\"ACH %09d:%06d\", dest.ExternalDest.AchRoutingNumber, dest.ExternalDest.AchAccountNumber)\n\t\tif dest.ExternalDest.AchAccountNumber == 0 || dest.ExternalDest.AchRoutingNumber == 0 {\n\t\t\treturn nil, status.Errorf(codes.InvalidArgument, \"external source routing and account numbers cannot be zero: %s\", destDesc)\n\t\t}\n\tcase *TransferRequest_DestAccountNumber:\n\t\tdestDesc = fmt.Sprintf(\"account %06d\", dest.DestAccountNumber)\n\t\tvar err error\n\t\tif destAcct, err = s.allAccounts.getAccount(cust, dest.DestAccountNumber); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tvar srcBalance int32\n\tif srcAcct != nil {\n\t\tdesc := fmt.Sprintf(\"transfer to %s\", destDesc)\n\t\tif req.Desc != \"\" {\n\t\t\tdesc = fmt.Sprintf(\"%s: %s\", desc, req.Desc)\n\t\t}\n\t\tvar err error\n\t\tif srcBalance, err = srcAcct.newTransaction(-req.AmountCents, desc); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tvar destBalance int32\n\tif destAcct != nil {\n\t\tdesc := fmt.Sprintf(\"transfer from %s\", srcDesc)\n\t\tif req.Desc != \"\" {\n\t\t\tdesc = fmt.Sprintf(\"%s: %s\", desc, req.Desc)\n\t\t}\n\t\tvar err error\n\t\tif destBalance, err = destAcct.newTransaction(req.AmountCents, desc); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn &TransferResponse{\n\t\tSrcAccountNumber:  req.GetSourceAccountNumber(),\n\t\tSrcBalanceCents:   srcBalance,\n\t\tDestAccountNumber: req.GetDestAccountNumber(),\n\t\tDestBalanceCents:  destBalance,\n\t}, nil\n}\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/bank.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v4.22.0\n// source: bank.proto\n\npackage main\n\nimport (\n\tproto \"github.com/golang/protobuf/proto\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n\ttimestamppb \"google.golang.org/protobuf/types/known/timestamppb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// This is a compile-time assertion that a sufficiently up-to-date version\n// of the legacy proto package is being used.\nconst _ = proto.ProtoPackageIsVersion4\n\ntype Account_Type int32\n\nconst (\n\tAccount_UNKNOWN        Account_Type = 0\n\tAccount_CHECKING       Account_Type = 1\n\tAccount_SAVING         Account_Type = 2\n\tAccount_MONEY_MARKET   Account_Type = 3\n\tAccount_LINE_OF_CREDIT Account_Type = 4\n\tAccount_LOAN           Account_Type = 5\n\tAccount_EQUITIES       Account_Type = 6\n)\n\n// Enum value maps for Account_Type.\nvar (\n\tAccount_Type_name = map[int32]string{\n\t\t0: \"UNKNOWN\",\n\t\t1: \"CHECKING\",\n\t\t2: \"SAVING\",\n\t\t3: \"MONEY_MARKET\",\n\t\t4: \"LINE_OF_CREDIT\",\n\t\t5: \"LOAN\",\n\t\t6: \"EQUITIES\",\n\t}\n\tAccount_Type_value = map[string]int32{\n\t\t\"UNKNOWN\":        0,\n\t\t\"CHECKING\":       1,\n\t\t\"SAVING\":         2,\n\t\t\"MONEY_MARKET\":   3,\n\t\t\"LINE_OF_CREDIT\": 4,\n\t\t\"LOAN\":           5,\n\t\t\"EQUITIES\":       6,\n\t}\n)\n\nfunc (x Account_Type) Enum() *Account_Type {\n\tp := new(Account_Type)\n\t*p = x\n\treturn p\n}\n\nfunc (x Account_Type) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (Account_Type) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_bank_proto_enumTypes[0].Descriptor()\n}\n\nfunc (Account_Type) Type() protoreflect.EnumType {\n\treturn &file_bank_proto_enumTypes[0]\n}\n\nfunc (x Account_Type) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use Account_Type.Descriptor instead.\nfunc (Account_Type) EnumDescriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{3, 0}\n}\n\ntype DepositRequest_Source int32\n\nconst (\n\tDepositRequest_UNKNOWN DepositRequest_Source = 0\n\tDepositRequest_CASH    DepositRequest_Source = 1\n\tDepositRequest_CHECK   DepositRequest_Source = 2\n\tDepositRequest_ACH     DepositRequest_Source = 3\n\tDepositRequest_WIRE    DepositRequest_Source = 4\n)\n\n// Enum value maps for DepositRequest_Source.\nvar (\n\tDepositRequest_Source_name = map[int32]string{\n\t\t0: \"UNKNOWN\",\n\t\t1: \"CASH\",\n\t\t2: \"CHECK\",\n\t\t3: \"ACH\",\n\t\t4: \"WIRE\",\n\t}\n\tDepositRequest_Source_value = map[string]int32{\n\t\t\"UNKNOWN\": 0,\n\t\t\"CASH\":    1,\n\t\t\"CHECK\":   2,\n\t\t\"ACH\":     3,\n\t\t\"WIRE\":    4,\n\t}\n)\n\nfunc (x DepositRequest_Source) Enum() *DepositRequest_Source {\n\tp := new(DepositRequest_Source)\n\t*p = x\n\treturn p\n}\n\nfunc (x DepositRequest_Source) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (DepositRequest_Source) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_bank_proto_enumTypes[1].Descriptor()\n}\n\nfunc (DepositRequest_Source) Type() protoreflect.EnumType {\n\treturn &file_bank_proto_enumTypes[1]\n}\n\nfunc (x DepositRequest_Source) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use DepositRequest_Source.Descriptor instead.\nfunc (DepositRequest_Source) EnumDescriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{6, 0}\n}\n\ntype OpenAccountRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tInitialDepositCents int32        `protobuf:\"varint,1,opt,name=initial_deposit_cents,json=initialDepositCents,proto3\" json:\"initial_deposit_cents,omitempty\"`\n\tType                Account_Type `protobuf:\"varint,2,opt,name=type,proto3,enum=Account_Type\" json:\"type,omitempty\"`\n}\n\nfunc (x *OpenAccountRequest) Reset() {\n\t*x = OpenAccountRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OpenAccountRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OpenAccountRequest) ProtoMessage() {}\n\nfunc (x *OpenAccountRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OpenAccountRequest.ProtoReflect.Descriptor instead.\nfunc (*OpenAccountRequest) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *OpenAccountRequest) GetInitialDepositCents() int32 {\n\tif x != nil {\n\t\treturn x.InitialDepositCents\n\t}\n\treturn 0\n}\n\nfunc (x *OpenAccountRequest) GetType() Account_Type {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn Account_UNKNOWN\n}\n\ntype CloseAccountRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAccountNumber uint64 `protobuf:\"varint,1,opt,name=account_number,json=accountNumber,proto3\" json:\"account_number,omitempty\"`\n}\n\nfunc (x *CloseAccountRequest) Reset() {\n\t*x = CloseAccountRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *CloseAccountRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*CloseAccountRequest) ProtoMessage() {}\n\nfunc (x *CloseAccountRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use CloseAccountRequest.ProtoReflect.Descriptor instead.\nfunc (*CloseAccountRequest) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *CloseAccountRequest) GetAccountNumber() uint64 {\n\tif x != nil {\n\t\treturn x.AccountNumber\n\t}\n\treturn 0\n}\n\ntype GetAccountsResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAccounts []*Account `protobuf:\"bytes,1,rep,name=accounts,proto3\" json:\"accounts,omitempty\"`\n}\n\nfunc (x *GetAccountsResponse) Reset() {\n\t*x = GetAccountsResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetAccountsResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetAccountsResponse) ProtoMessage() {}\n\nfunc (x *GetAccountsResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GetAccountsResponse.ProtoReflect.Descriptor instead.\nfunc (*GetAccountsResponse) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *GetAccountsResponse) GetAccounts() []*Account {\n\tif x != nil {\n\t\treturn x.Accounts\n\t}\n\treturn nil\n}\n\ntype Account struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAccountNumber uint64       `protobuf:\"varint,1,opt,name=account_number,json=accountNumber,proto3\" json:\"account_number,omitempty\"`\n\tType          Account_Type `protobuf:\"varint,2,opt,name=type,proto3,enum=Account_Type\" json:\"type,omitempty\"`\n\tBalanceCents  int32        `protobuf:\"varint,3,opt,name=balance_cents,json=balanceCents,proto3\" json:\"balance_cents,omitempty\"`\n}\n\nfunc (x *Account) Reset() {\n\t*x = Account{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Account) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Account) ProtoMessage() {}\n\nfunc (x *Account) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[3]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Account.ProtoReflect.Descriptor instead.\nfunc (*Account) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *Account) GetAccountNumber() uint64 {\n\tif x != nil {\n\t\treturn x.AccountNumber\n\t}\n\treturn 0\n}\n\nfunc (x *Account) GetType() Account_Type {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn Account_UNKNOWN\n}\n\nfunc (x *Account) GetBalanceCents() int32 {\n\tif x != nil {\n\t\treturn x.BalanceCents\n\t}\n\treturn 0\n}\n\ntype GetTransactionsRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAccountNumber uint64                 `protobuf:\"varint,1,opt,name=account_number,json=accountNumber,proto3\" json:\"account_number,omitempty\"`\n\tStart         *timestamppb.Timestamp `protobuf:\"bytes,2,opt,name=start,proto3\" json:\"start,omitempty\"`\n\tEnd           *timestamppb.Timestamp `protobuf:\"bytes,3,opt,name=end,proto3\" json:\"end,omitempty\"`\n}\n\nfunc (x *GetTransactionsRequest) Reset() {\n\t*x = GetTransactionsRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetTransactionsRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetTransactionsRequest) ProtoMessage() {}\n\nfunc (x *GetTransactionsRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[4]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GetTransactionsRequest.ProtoReflect.Descriptor instead.\nfunc (*GetTransactionsRequest) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *GetTransactionsRequest) GetAccountNumber() uint64 {\n\tif x != nil {\n\t\treturn x.AccountNumber\n\t}\n\treturn 0\n}\n\nfunc (x *GetTransactionsRequest) GetStart() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.Start\n\t}\n\treturn nil\n}\n\nfunc (x *GetTransactionsRequest) GetEnd() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.End\n\t}\n\treturn nil\n}\n\ntype Transaction struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAccountNumber uint64                 `protobuf:\"varint,1,opt,name=account_number,json=accountNumber,proto3\" json:\"account_number,omitempty\"`\n\tSeqNumber     uint64                 `protobuf:\"varint,2,opt,name=seq_number,json=seqNumber,proto3\" json:\"seq_number,omitempty\"`\n\tDate          *timestamppb.Timestamp `protobuf:\"bytes,3,opt,name=date,proto3\" json:\"date,omitempty\"`\n\tAmountCents   int32                  `protobuf:\"varint,4,opt,name=amount_cents,json=amountCents,proto3\" json:\"amount_cents,omitempty\"`\n\tDesc          string                 `protobuf:\"bytes,5,opt,name=desc,proto3\" json:\"desc,omitempty\"`\n}\n\nfunc (x *Transaction) Reset() {\n\t*x = Transaction{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Transaction) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Transaction) ProtoMessage() {}\n\nfunc (x *Transaction) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[5]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Transaction.ProtoReflect.Descriptor instead.\nfunc (*Transaction) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *Transaction) GetAccountNumber() uint64 {\n\tif x != nil {\n\t\treturn x.AccountNumber\n\t}\n\treturn 0\n}\n\nfunc (x *Transaction) GetSeqNumber() uint64 {\n\tif x != nil {\n\t\treturn x.SeqNumber\n\t}\n\treturn 0\n}\n\nfunc (x *Transaction) GetDate() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.Date\n\t}\n\treturn nil\n}\n\nfunc (x *Transaction) GetAmountCents() int32 {\n\tif x != nil {\n\t\treturn x.AmountCents\n\t}\n\treturn 0\n}\n\nfunc (x *Transaction) GetDesc() string {\n\tif x != nil {\n\t\treturn x.Desc\n\t}\n\treturn \"\"\n}\n\ntype DepositRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAccountNumber uint64                `protobuf:\"varint,1,opt,name=account_number,json=accountNumber,proto3\" json:\"account_number,omitempty\"`\n\tAmountCents   int32                 `protobuf:\"varint,2,opt,name=amount_cents,json=amountCents,proto3\" json:\"amount_cents,omitempty\"`\n\tSource        DepositRequest_Source `protobuf:\"varint,3,opt,name=source,proto3,enum=DepositRequest_Source\" json:\"source,omitempty\"`\n\tDesc          string                `protobuf:\"bytes,4,opt,name=desc,proto3\" json:\"desc,omitempty\"`\n}\n\nfunc (x *DepositRequest) Reset() {\n\t*x = DepositRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DepositRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DepositRequest) ProtoMessage() {}\n\nfunc (x *DepositRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[6]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DepositRequest.ProtoReflect.Descriptor instead.\nfunc (*DepositRequest) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *DepositRequest) GetAccountNumber() uint64 {\n\tif x != nil {\n\t\treturn x.AccountNumber\n\t}\n\treturn 0\n}\n\nfunc (x *DepositRequest) GetAmountCents() int32 {\n\tif x != nil {\n\t\treturn x.AmountCents\n\t}\n\treturn 0\n}\n\nfunc (x *DepositRequest) GetSource() DepositRequest_Source {\n\tif x != nil {\n\t\treturn x.Source\n\t}\n\treturn DepositRequest_UNKNOWN\n}\n\nfunc (x *DepositRequest) GetDesc() string {\n\tif x != nil {\n\t\treturn x.Desc\n\t}\n\treturn \"\"\n}\n\ntype BalanceResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAccountNumber uint64 `protobuf:\"varint,1,opt,name=account_number,json=accountNumber,proto3\" json:\"account_number,omitempty\"`\n\tBalanceCents  int32  `protobuf:\"varint,2,opt,name=balance_cents,json=balanceCents,proto3\" json:\"balance_cents,omitempty\"`\n}\n\nfunc (x *BalanceResponse) Reset() {\n\t*x = BalanceResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *BalanceResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*BalanceResponse) ProtoMessage() {}\n\nfunc (x *BalanceResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[7]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use BalanceResponse.ProtoReflect.Descriptor instead.\nfunc (*BalanceResponse) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{7}\n}\n\nfunc (x *BalanceResponse) GetAccountNumber() uint64 {\n\tif x != nil {\n\t\treturn x.AccountNumber\n\t}\n\treturn 0\n}\n\nfunc (x *BalanceResponse) GetBalanceCents() int32 {\n\tif x != nil {\n\t\treturn x.BalanceCents\n\t}\n\treturn 0\n}\n\ntype WithdrawRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAccountNumber uint64 `protobuf:\"varint,1,opt,name=account_number,json=accountNumber,proto3\" json:\"account_number,omitempty\"`\n\tAmountCents   int32  `protobuf:\"varint,2,opt,name=amount_cents,json=amountCents,proto3\" json:\"amount_cents,omitempty\"`\n\tDesc          string `protobuf:\"bytes,3,opt,name=desc,proto3\" json:\"desc,omitempty\"`\n}\n\nfunc (x *WithdrawRequest) Reset() {\n\t*x = WithdrawRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *WithdrawRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*WithdrawRequest) ProtoMessage() {}\n\nfunc (x *WithdrawRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[8]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use WithdrawRequest.ProtoReflect.Descriptor instead.\nfunc (*WithdrawRequest) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{8}\n}\n\nfunc (x *WithdrawRequest) GetAccountNumber() uint64 {\n\tif x != nil {\n\t\treturn x.AccountNumber\n\t}\n\treturn 0\n}\n\nfunc (x *WithdrawRequest) GetAmountCents() int32 {\n\tif x != nil {\n\t\treturn x.AmountCents\n\t}\n\treturn 0\n}\n\nfunc (x *WithdrawRequest) GetDesc() string {\n\tif x != nil {\n\t\treturn x.Desc\n\t}\n\treturn \"\"\n}\n\ntype TransferRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Types that are assignable to Source:\n\t//\n\t//\t*TransferRequest_SourceAccountNumber\n\t//\t*TransferRequest_ExternalSource\n\tSource isTransferRequest_Source `protobuf_oneof:\"source\"`\n\t// Types that are assignable to Dest:\n\t//\n\t//\t*TransferRequest_DestAccountNumber\n\t//\t*TransferRequest_ExternalDest\n\tDest        isTransferRequest_Dest `protobuf_oneof:\"dest\"`\n\tAmountCents int32                  `protobuf:\"varint,5,opt,name=amount_cents,json=amountCents,proto3\" json:\"amount_cents,omitempty\"`\n\tDesc        string                 `protobuf:\"bytes,6,opt,name=desc,proto3\" json:\"desc,omitempty\"`\n}\n\nfunc (x *TransferRequest) Reset() {\n\t*x = TransferRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[9]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *TransferRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*TransferRequest) ProtoMessage() {}\n\nfunc (x *TransferRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[9]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use TransferRequest.ProtoReflect.Descriptor instead.\nfunc (*TransferRequest) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{9}\n}\n\nfunc (m *TransferRequest) GetSource() isTransferRequest_Source {\n\tif m != nil {\n\t\treturn m.Source\n\t}\n\treturn nil\n}\n\nfunc (x *TransferRequest) GetSourceAccountNumber() uint64 {\n\tif x, ok := x.GetSource().(*TransferRequest_SourceAccountNumber); ok {\n\t\treturn x.SourceAccountNumber\n\t}\n\treturn 0\n}\n\nfunc (x *TransferRequest) GetExternalSource() *TransferRequest_ExternalAccount {\n\tif x, ok := x.GetSource().(*TransferRequest_ExternalSource); ok {\n\t\treturn x.ExternalSource\n\t}\n\treturn nil\n}\n\nfunc (m *TransferRequest) GetDest() isTransferRequest_Dest {\n\tif m != nil {\n\t\treturn m.Dest\n\t}\n\treturn nil\n}\n\nfunc (x *TransferRequest) GetDestAccountNumber() uint64 {\n\tif x, ok := x.GetDest().(*TransferRequest_DestAccountNumber); ok {\n\t\treturn x.DestAccountNumber\n\t}\n\treturn 0\n}\n\nfunc (x *TransferRequest) GetExternalDest() *TransferRequest_ExternalAccount {\n\tif x, ok := x.GetDest().(*TransferRequest_ExternalDest); ok {\n\t\treturn x.ExternalDest\n\t}\n\treturn nil\n}\n\nfunc (x *TransferRequest) GetAmountCents() int32 {\n\tif x != nil {\n\t\treturn x.AmountCents\n\t}\n\treturn 0\n}\n\nfunc (x *TransferRequest) GetDesc() string {\n\tif x != nil {\n\t\treturn x.Desc\n\t}\n\treturn \"\"\n}\n\ntype isTransferRequest_Source interface {\n\tisTransferRequest_Source()\n}\n\ntype TransferRequest_SourceAccountNumber struct {\n\tSourceAccountNumber uint64 `protobuf:\"varint,1,opt,name=source_account_number,json=sourceAccountNumber,proto3,oneof\"`\n}\n\ntype TransferRequest_ExternalSource struct {\n\tExternalSource *TransferRequest_ExternalAccount `protobuf:\"bytes,2,opt,name=external_source,json=externalSource,proto3,oneof\"`\n}\n\nfunc (*TransferRequest_SourceAccountNumber) isTransferRequest_Source() {}\n\nfunc (*TransferRequest_ExternalSource) isTransferRequest_Source() {}\n\ntype isTransferRequest_Dest interface {\n\tisTransferRequest_Dest()\n}\n\ntype TransferRequest_DestAccountNumber struct {\n\tDestAccountNumber uint64 `protobuf:\"varint,3,opt,name=dest_account_number,json=destAccountNumber,proto3,oneof\"`\n}\n\ntype TransferRequest_ExternalDest struct {\n\tExternalDest *TransferRequest_ExternalAccount `protobuf:\"bytes,4,opt,name=external_dest,json=externalDest,proto3,oneof\"`\n}\n\nfunc (*TransferRequest_DestAccountNumber) isTransferRequest_Dest() {}\n\nfunc (*TransferRequest_ExternalDest) isTransferRequest_Dest() {}\n\ntype TransferResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSrcAccountNumber  uint64 `protobuf:\"varint,1,opt,name=src_account_number,json=srcAccountNumber,proto3\" json:\"src_account_number,omitempty\"`\n\tSrcBalanceCents   int32  `protobuf:\"varint,2,opt,name=src_balance_cents,json=srcBalanceCents,proto3\" json:\"src_balance_cents,omitempty\"`\n\tDestAccountNumber uint64 `protobuf:\"varint,3,opt,name=dest_account_number,json=destAccountNumber,proto3\" json:\"dest_account_number,omitempty\"`\n\tDestBalanceCents  int32  `protobuf:\"varint,4,opt,name=dest_balance_cents,json=destBalanceCents,proto3\" json:\"dest_balance_cents,omitempty\"`\n}\n\nfunc (x *TransferResponse) Reset() {\n\t*x = TransferResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[10]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *TransferResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*TransferResponse) ProtoMessage() {}\n\nfunc (x *TransferResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[10]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use TransferResponse.ProtoReflect.Descriptor instead.\nfunc (*TransferResponse) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{10}\n}\n\nfunc (x *TransferResponse) GetSrcAccountNumber() uint64 {\n\tif x != nil {\n\t\treturn x.SrcAccountNumber\n\t}\n\treturn 0\n}\n\nfunc (x *TransferResponse) GetSrcBalanceCents() int32 {\n\tif x != nil {\n\t\treturn x.SrcBalanceCents\n\t}\n\treturn 0\n}\n\nfunc (x *TransferResponse) GetDestAccountNumber() uint64 {\n\tif x != nil {\n\t\treturn x.DestAccountNumber\n\t}\n\treturn 0\n}\n\nfunc (x *TransferResponse) GetDestBalanceCents() int32 {\n\tif x != nil {\n\t\treturn x.DestBalanceCents\n\t}\n\treturn 0\n}\n\ntype TransferRequest_ExternalAccount struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAchRoutingNumber uint64 `protobuf:\"varint,1,opt,name=ach_routing_number,json=achRoutingNumber,proto3\" json:\"ach_routing_number,omitempty\"`\n\tAchAccountNumber uint64 `protobuf:\"varint,2,opt,name=ach_account_number,json=achAccountNumber,proto3\" json:\"ach_account_number,omitempty\"`\n}\n\nfunc (x *TransferRequest_ExternalAccount) Reset() {\n\t*x = TransferRequest_ExternalAccount{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_bank_proto_msgTypes[11]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *TransferRequest_ExternalAccount) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*TransferRequest_ExternalAccount) ProtoMessage() {}\n\nfunc (x *TransferRequest_ExternalAccount) ProtoReflect() protoreflect.Message {\n\tmi := &file_bank_proto_msgTypes[11]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use TransferRequest_ExternalAccount.ProtoReflect.Descriptor instead.\nfunc (*TransferRequest_ExternalAccount) Descriptor() ([]byte, []int) {\n\treturn file_bank_proto_rawDescGZIP(), []int{9, 0}\n}\n\nfunc (x *TransferRequest_ExternalAccount) GetAchRoutingNumber() uint64 {\n\tif x != nil {\n\t\treturn x.AchRoutingNumber\n\t}\n\treturn 0\n}\n\nfunc (x *TransferRequest_ExternalAccount) GetAchAccountNumber() uint64 {\n\tif x != nil {\n\t\treturn x.AchAccountNumber\n\t}\n\treturn 0\n}\n\nvar File_bank_proto protoreflect.FileDescriptor\n\nvar file_bank_proto_rawDesc = []byte{\n\t0x0a, 0x0a, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f,\n\t0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d,\n\t0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c,\n\t0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73,\n\t0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6b, 0x0a, 0x12, 0x4f, 0x70,\n\t0x65, 0x6e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,\n\t0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x70, 0x6f,\n\t0x73, 0x69, 0x74, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,\n\t0x13, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x43,\n\t0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x54, 0x79, 0x70,\n\t0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3c, 0x0a, 0x13, 0x43, 0x6c, 0x6f, 0x73, 0x65,\n\t0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25,\n\t0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e,\n\t0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3b, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f,\n\t0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x08,\n\t0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08,\n\t0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e,\n\t0x74, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25,\n\t0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e,\n\t0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,\n\t0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x54, 0x79,\n\t0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x6c, 0x61,\n\t0x6e, 0x63, 0x65, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,\n\t0x0c, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x6b, 0x0a,\n\t0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e,\n\t0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x49, 0x4e, 0x47, 0x10, 0x01,\n\t0x12, 0x0a, 0x0a, 0x06, 0x53, 0x41, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c,\n\t0x4d, 0x4f, 0x4e, 0x45, 0x59, 0x5f, 0x4d, 0x41, 0x52, 0x4b, 0x45, 0x54, 0x10, 0x03, 0x12, 0x12,\n\t0x0a, 0x0e, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4f, 0x46, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x49, 0x54,\n\t0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x4f, 0x41, 0x4e, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08,\n\t0x45, 0x51, 0x55, 0x49, 0x54, 0x49, 0x45, 0x53, 0x10, 0x06, 0x22, 0x9f, 0x01, 0x0a, 0x16, 0x47,\n\t0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,\n\t0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61,\n\t0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x05,\n\t0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,\n\t0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,\n\t0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c,\n\t0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,\n\t0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,\n\t0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0xba, 0x01, 0x0a,\n\t0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e,\n\t0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d,\n\t0x62, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x71, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65,\n\t0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x65, 0x71, 0x4e, 0x75, 0x6d, 0x62,\n\t0x65, 0x72, 0x12, 0x2e, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,\n\t0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,\n\t0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x64, 0x61,\n\t0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x6e,\n\t0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74,\n\t0x43, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x05, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x22, 0xdd, 0x01, 0x0a, 0x0e, 0x44, 0x65,\n\t0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e,\n\t0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d,\n\t0x62, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x65,\n\t0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x61, 0x6d, 0x6f, 0x75, 0x6e,\n\t0x74, 0x43, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,\n\t0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06,\n\t0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x04,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x22, 0x3d, 0x0a, 0x06, 0x53, 0x6f,\n\t0x75, 0x72, 0x63, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,\n\t0x00, 0x12, 0x08, 0x0a, 0x04, 0x43, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x43,\n\t0x48, 0x45, 0x43, 0x4b, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x43, 0x48, 0x10, 0x03, 0x12,\n\t0x08, 0x0a, 0x04, 0x57, 0x49, 0x52, 0x45, 0x10, 0x04, 0x22, 0x5d, 0x0a, 0x0f, 0x42, 0x61, 0x6c,\n\t0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e,\n\t0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d,\n\t0x62, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x63,\n\t0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x62, 0x61, 0x6c, 0x61,\n\t0x6e, 0x63, 0x65, 0x43, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x6f, 0x0a, 0x0f, 0x57, 0x69, 0x74, 0x68,\n\t0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x61,\n\t0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62,\n\t0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x6e,\n\t0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74,\n\t0x43, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x03, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x22, 0xc7, 0x03, 0x0a, 0x0f, 0x54, 0x72,\n\t0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a,\n\t0x15, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f,\n\t0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x13,\n\t0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d,\n\t0x62, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x0f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f,\n\t0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x54,\n\t0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45,\n\t0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x48, 0x00,\n\t0x52, 0x0e, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65,\n\t0x12, 0x30, 0x0a, 0x13, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,\n\t0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52,\n\t0x11, 0x64, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62,\n\t0x65, 0x72, 0x12, 0x47, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64,\n\t0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x54, 0x72, 0x61, 0x6e,\n\t0x73, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x65,\n\t0x72, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x48, 0x01, 0x52, 0x0c, 0x65,\n\t0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x61,\n\t0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28,\n\t0x05, 0x52, 0x0b, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x12,\n\t0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65,\n\t0x73, 0x63, 0x1a, 0x6d, 0x0a, 0x0f, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x63,\n\t0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x68, 0x5f, 0x72, 0x6f, 0x75,\n\t0x74, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x04, 0x52, 0x10, 0x61, 0x63, 0x68, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x4e, 0x75, 0x6d,\n\t0x62, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x68, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75,\n\t0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,\n\t0x10, 0x61, 0x63, 0x68, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65,\n\t0x72, 0x42, 0x08, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x64,\n\t0x65, 0x73, 0x74, 0x22, 0xca, 0x01, 0x0a, 0x10, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72,\n\t0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x72, 0x63, 0x5f,\n\t0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x73, 0x72, 0x63, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,\n\t0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x72, 0x63, 0x5f, 0x62, 0x61,\n\t0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x05, 0x52, 0x0f, 0x73, 0x72, 0x63, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x65, 0x6e,\n\t0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75,\n\t0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,\n\t0x11, 0x64, 0x65, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62,\n\t0x65, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e,\n\t0x63, 0x65, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10,\n\t0x64, 0x65, 0x73, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x65, 0x6e, 0x74, 0x73,\n\t0x32, 0xfa, 0x02, 0x0a, 0x04, 0x42, 0x61, 0x6e, 0x6b, 0x12, 0x2c, 0x0a, 0x0b, 0x4f, 0x70, 0x65,\n\t0x6e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x13, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x41,\n\t0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x08, 0x2e,\n\t0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65,\n\t0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x41,\n\t0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e,\n\t0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,\n\t0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3b, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f,\n\t0x75, 0x6e, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x47,\n\t0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,\n\t0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,\n\t0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73,\n\t0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c,\n\t0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x12, 0x2c,\n\t0x0a, 0x07, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x0f, 0x2e, 0x44, 0x65, 0x70, 0x6f,\n\t0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x42, 0x61, 0x6c,\n\t0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x08,\n\t0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x10, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64,\n\t0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x42, 0x61, 0x6c,\n\t0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x08,\n\t0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x10, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73,\n\t0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x54, 0x72, 0x61,\n\t0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x08, 0x5a,\n\t0x06, 0x2e, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_bank_proto_rawDescOnce sync.Once\n\tfile_bank_proto_rawDescData = file_bank_proto_rawDesc\n)\n\nfunc file_bank_proto_rawDescGZIP() []byte {\n\tfile_bank_proto_rawDescOnce.Do(func() {\n\t\tfile_bank_proto_rawDescData = protoimpl.X.CompressGZIP(file_bank_proto_rawDescData)\n\t})\n\treturn file_bank_proto_rawDescData\n}\n\nvar file_bank_proto_enumTypes = make([]protoimpl.EnumInfo, 2)\nvar file_bank_proto_msgTypes = make([]protoimpl.MessageInfo, 12)\nvar file_bank_proto_goTypes = []interface{}{\n\t(Account_Type)(0),                       // 0: Account.Type\n\t(DepositRequest_Source)(0),              // 1: DepositRequest.Source\n\t(*OpenAccountRequest)(nil),              // 2: OpenAccountRequest\n\t(*CloseAccountRequest)(nil),             // 3: CloseAccountRequest\n\t(*GetAccountsResponse)(nil),             // 4: GetAccountsResponse\n\t(*Account)(nil),                         // 5: Account\n\t(*GetTransactionsRequest)(nil),          // 6: GetTransactionsRequest\n\t(*Transaction)(nil),                     // 7: Transaction\n\t(*DepositRequest)(nil),                  // 8: DepositRequest\n\t(*BalanceResponse)(nil),                 // 9: BalanceResponse\n\t(*WithdrawRequest)(nil),                 // 10: WithdrawRequest\n\t(*TransferRequest)(nil),                 // 11: TransferRequest\n\t(*TransferResponse)(nil),                // 12: TransferResponse\n\t(*TransferRequest_ExternalAccount)(nil), // 13: TransferRequest.ExternalAccount\n\t(*timestamppb.Timestamp)(nil),           // 14: google.protobuf.Timestamp\n\t(*emptypb.Empty)(nil),                   // 15: google.protobuf.Empty\n}\nvar file_bank_proto_depIdxs = []int32{\n\t0,  // 0: OpenAccountRequest.type:type_name -> Account.Type\n\t5,  // 1: GetAccountsResponse.accounts:type_name -> Account\n\t0,  // 2: Account.type:type_name -> Account.Type\n\t14, // 3: GetTransactionsRequest.start:type_name -> google.protobuf.Timestamp\n\t14, // 4: GetTransactionsRequest.end:type_name -> google.protobuf.Timestamp\n\t14, // 5: Transaction.date:type_name -> google.protobuf.Timestamp\n\t1,  // 6: DepositRequest.source:type_name -> DepositRequest.Source\n\t13, // 7: TransferRequest.external_source:type_name -> TransferRequest.ExternalAccount\n\t13, // 8: TransferRequest.external_dest:type_name -> TransferRequest.ExternalAccount\n\t2,  // 9: Bank.OpenAccount:input_type -> OpenAccountRequest\n\t3,  // 10: Bank.CloseAccount:input_type -> CloseAccountRequest\n\t15, // 11: Bank.GetAccounts:input_type -> google.protobuf.Empty\n\t6,  // 12: Bank.GetTransactions:input_type -> GetTransactionsRequest\n\t8,  // 13: Bank.Deposit:input_type -> DepositRequest\n\t10, // 14: Bank.Withdraw:input_type -> WithdrawRequest\n\t11, // 15: Bank.Transfer:input_type -> TransferRequest\n\t5,  // 16: Bank.OpenAccount:output_type -> Account\n\t15, // 17: Bank.CloseAccount:output_type -> google.protobuf.Empty\n\t4,  // 18: Bank.GetAccounts:output_type -> GetAccountsResponse\n\t7,  // 19: Bank.GetTransactions:output_type -> Transaction\n\t9,  // 20: Bank.Deposit:output_type -> BalanceResponse\n\t9,  // 21: Bank.Withdraw:output_type -> BalanceResponse\n\t12, // 22: Bank.Transfer:output_type -> TransferResponse\n\t16, // [16:23] is the sub-list for method output_type\n\t9,  // [9:16] is the sub-list for method input_type\n\t9,  // [9:9] is the sub-list for extension type_name\n\t9,  // [9:9] is the sub-list for extension extendee\n\t0,  // [0:9] is the sub-list for field type_name\n}\n\nfunc init() { file_bank_proto_init() }\nfunc file_bank_proto_init() {\n\tif File_bank_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_bank_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*OpenAccountRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_bank_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*CloseAccountRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_bank_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetAccountsResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_bank_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Account); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_bank_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetTransactionsRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_bank_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Transaction); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_bank_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DepositRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_bank_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*BalanceResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_bank_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*WithdrawRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_bank_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*TransferRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_bank_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*TransferResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_bank_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*TransferRequest_ExternalAccount); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\tfile_bank_proto_msgTypes[9].OneofWrappers = []interface{}{\n\t\t(*TransferRequest_SourceAccountNumber)(nil),\n\t\t(*TransferRequest_ExternalSource)(nil),\n\t\t(*TransferRequest_DestAccountNumber)(nil),\n\t\t(*TransferRequest_ExternalDest)(nil),\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_bank_proto_rawDesc,\n\t\t\tNumEnums:      2,\n\t\t\tNumMessages:   12,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_bank_proto_goTypes,\n\t\tDependencyIndexes: file_bank_proto_depIdxs,\n\t\tEnumInfos:         file_bank_proto_enumTypes,\n\t\tMessageInfos:      file_bank_proto_msgTypes,\n\t}.Build()\n\tFile_bank_proto = out.File\n\tfile_bank_proto_rawDesc = nil\n\tfile_bank_proto_goTypes = nil\n\tfile_bank_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/bank.proto",
    "content": "syntax = \"proto3\";\n\noption go_package = \".;main\";\n\nimport \"google/protobuf/empty.proto\";\nimport \"google/protobuf/timestamp.proto\";\n\n// Bank provides operations for interacting with bank accounts. All\n// operations operate for the authenticated user (identified via an\n// \"authorization\" request header, where the type is \"token\" and the\n// credential is the customer's ID).\nservice Bank {\n    // OpenAccount creates an account with the type and given initial deposit\n    // as its balance.\n    rpc OpenAccount(OpenAccountRequest) returns (Account);\n    // CloseAccount closes the indicated account. An account can only be\n    // closed if its balance is zero.\n    rpc CloseAccount(CloseAccountRequest) returns (google.protobuf.Empty);\n    // GetAccounts lists all accounts for the current customer.\n    rpc GetAccounts(google.protobuf.Empty) returns (GetAccountsResponse);\n    // GetTransactions streams all transactions that match the given criteria.\n    // If the given start date is not specified, transactions since beginning\n    // of time are included. Similarly, if the given end date is not specified,\n    // transactions all the way to the presnet are included.\n    rpc GetTransactions(GetTransactionsRequest) returns (stream Transaction);\n    // Deposit increases the balance of an account by depositing funds into it.\n    rpc Deposit(DepositRequest) returns (BalanceResponse);\n    // Withdraw decreases the balance of an account by withdrawing funds from it.\n    rpc Withdraw(WithdrawRequest) returns (BalanceResponse);\n    // Transfer moves money from one account to another. The source and destination\n    // accounts can be with this bank (e.g. \"local\" account numbers) or can be\n    // external accounts, identified by their ACH routing and account numbers.\n    rpc Transfer(TransferRequest) returns (TransferResponse);\n}\n\nmessage OpenAccountRequest {\n    int32 initial_deposit_cents = 1;\n    Account.Type type = 2;\n}\n\nmessage CloseAccountRequest {\n    uint64 account_number = 1;\n}\n\nmessage GetAccountsResponse {\n    repeated Account accounts = 1;\n}\n\nmessage Account {\n    uint64 account_number = 1;\n    enum Type {\n        UNKNOWN = 0;\n        CHECKING = 1;\n        SAVING = 2;\n        MONEY_MARKET = 3;\n        LINE_OF_CREDIT = 4;\n        LOAN = 5;\n        EQUITIES = 6;\n    }\n    Type type = 2;\n    int32 balance_cents = 3;\n}\n\nmessage GetTransactionsRequest {\n    uint64 account_number = 1;\n    google.protobuf.Timestamp start = 2;\n    google.protobuf.Timestamp end = 3;\n}\n\nmessage Transaction {\n    uint64 account_number = 1;\n    uint64 seq_number = 2;\n    google.protobuf.Timestamp date = 3;\n    int32 amount_cents = 4;\n    string desc = 5;\n}\n\nmessage DepositRequest {\n    uint64 account_number = 1;\n    int32 amount_cents = 2;\n    enum Source {\n        UNKNOWN = 0;\n        CASH = 1;\n        CHECK = 2;\n        ACH = 3;\n        WIRE = 4;\n    }\n    Source source = 3;\n    string desc = 4;\n}\n\nmessage BalanceResponse {\n    uint64 account_number = 1;\n    int32 balance_cents = 2;\n}\n\nmessage WithdrawRequest {\n    uint64 account_number = 1;\n    int32 amount_cents = 2;\n    string desc = 3;\n}\n\nmessage TransferRequest {\n    message ExternalAccount {\n        uint64 ach_routing_number = 1;\n        uint64 ach_account_number = 2;\n    }\n    oneof source {\n        uint64 source_account_number = 1;\n        ExternalAccount external_source = 2;\n    }\n    oneof dest {\n        uint64 dest_account_number = 3;\n        ExternalAccount external_dest = 4;\n    }\n    int32 amount_cents = 5;\n    string desc = 6;\n}\n\nmessage TransferResponse {\n    uint64 src_account_number = 1;\n    int32 src_balance_cents = 2;\n    uint64 dest_account_number = 3;\n    int32 dest_balance_cents = 4;\n}\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/bank_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage main\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.32.0 or later.\nconst _ = grpc.SupportPackageIsVersion7\n\n// BankClient is the client API for Bank service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype BankClient interface {\n\t// OpenAccount creates an account with the type and given initial deposit\n\t// as its balance.\n\tOpenAccount(ctx context.Context, in *OpenAccountRequest, opts ...grpc.CallOption) (*Account, error)\n\t// CloseAccount closes the indicated account. An account can only be\n\t// closed if its balance is zero.\n\tCloseAccount(ctx context.Context, in *CloseAccountRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\t// GetAccounts lists all accounts for the current customer.\n\tGetAccounts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetAccountsResponse, error)\n\t// GetTransactions streams all transactions that match the given criteria.\n\t// If the given start date is not specified, transactions since beginning\n\t// of time are included. Similarly, if the given end date is not specified,\n\t// transactions all the way to the presnet are included.\n\tGetTransactions(ctx context.Context, in *GetTransactionsRequest, opts ...grpc.CallOption) (Bank_GetTransactionsClient, error)\n\t// Deposit increases the balance of an account by depositing funds into it.\n\tDeposit(ctx context.Context, in *DepositRequest, opts ...grpc.CallOption) (*BalanceResponse, error)\n\t// Withdraw decreases the balance of an account by withdrawing funds from it.\n\tWithdraw(ctx context.Context, in *WithdrawRequest, opts ...grpc.CallOption) (*BalanceResponse, error)\n\t// Transfer moves money from one account to another. The source and destination\n\t// accounts can be with this bank (e.g. \"local\" account numbers) or can be\n\t// external accounts, identified by their ACH routing and account numbers.\n\tTransfer(ctx context.Context, in *TransferRequest, opts ...grpc.CallOption) (*TransferResponse, error)\n}\n\ntype bankClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewBankClient(cc grpc.ClientConnInterface) BankClient {\n\treturn &bankClient{cc}\n}\n\nfunc (c *bankClient) OpenAccount(ctx context.Context, in *OpenAccountRequest, opts ...grpc.CallOption) (*Account, error) {\n\tout := new(Account)\n\terr := c.cc.Invoke(ctx, \"/Bank/OpenAccount\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *bankClient) CloseAccount(ctx context.Context, in *CloseAccountRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/Bank/CloseAccount\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *bankClient) GetAccounts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetAccountsResponse, error) {\n\tout := new(GetAccountsResponse)\n\terr := c.cc.Invoke(ctx, \"/Bank/GetAccounts\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *bankClient) GetTransactions(ctx context.Context, in *GetTransactionsRequest, opts ...grpc.CallOption) (Bank_GetTransactionsClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &Bank_ServiceDesc.Streams[0], \"/Bank/GetTransactions\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &bankGetTransactionsClient{stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\ntype Bank_GetTransactionsClient interface {\n\tRecv() (*Transaction, error)\n\tgrpc.ClientStream\n}\n\ntype bankGetTransactionsClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *bankGetTransactionsClient) Recv() (*Transaction, error) {\n\tm := new(Transaction)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc (c *bankClient) Deposit(ctx context.Context, in *DepositRequest, opts ...grpc.CallOption) (*BalanceResponse, error) {\n\tout := new(BalanceResponse)\n\terr := c.cc.Invoke(ctx, \"/Bank/Deposit\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *bankClient) Withdraw(ctx context.Context, in *WithdrawRequest, opts ...grpc.CallOption) (*BalanceResponse, error) {\n\tout := new(BalanceResponse)\n\terr := c.cc.Invoke(ctx, \"/Bank/Withdraw\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *bankClient) Transfer(ctx context.Context, in *TransferRequest, opts ...grpc.CallOption) (*TransferResponse, error) {\n\tout := new(TransferResponse)\n\terr := c.cc.Invoke(ctx, \"/Bank/Transfer\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// BankServer is the server API for Bank service.\n// All implementations must embed UnimplementedBankServer\n// for forward compatibility\ntype BankServer interface {\n\t// OpenAccount creates an account with the type and given initial deposit\n\t// as its balance.\n\tOpenAccount(context.Context, *OpenAccountRequest) (*Account, error)\n\t// CloseAccount closes the indicated account. An account can only be\n\t// closed if its balance is zero.\n\tCloseAccount(context.Context, *CloseAccountRequest) (*emptypb.Empty, error)\n\t// GetAccounts lists all accounts for the current customer.\n\tGetAccounts(context.Context, *emptypb.Empty) (*GetAccountsResponse, error)\n\t// GetTransactions streams all transactions that match the given criteria.\n\t// If the given start date is not specified, transactions since beginning\n\t// of time are included. Similarly, if the given end date is not specified,\n\t// transactions all the way to the presnet are included.\n\tGetTransactions(*GetTransactionsRequest, Bank_GetTransactionsServer) error\n\t// Deposit increases the balance of an account by depositing funds into it.\n\tDeposit(context.Context, *DepositRequest) (*BalanceResponse, error)\n\t// Withdraw decreases the balance of an account by withdrawing funds from it.\n\tWithdraw(context.Context, *WithdrawRequest) (*BalanceResponse, error)\n\t// Transfer moves money from one account to another. The source and destination\n\t// accounts can be with this bank (e.g. \"local\" account numbers) or can be\n\t// external accounts, identified by their ACH routing and account numbers.\n\tTransfer(context.Context, *TransferRequest) (*TransferResponse, error)\n\tmustEmbedUnimplementedBankServer()\n}\n\n// UnimplementedBankServer must be embedded to have forward compatible implementations.\ntype UnimplementedBankServer struct {\n}\n\nfunc (UnimplementedBankServer) OpenAccount(context.Context, *OpenAccountRequest) (*Account, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method OpenAccount not implemented\")\n}\nfunc (UnimplementedBankServer) CloseAccount(context.Context, *CloseAccountRequest) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method CloseAccount not implemented\")\n}\nfunc (UnimplementedBankServer) GetAccounts(context.Context, *emptypb.Empty) (*GetAccountsResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method GetAccounts not implemented\")\n}\nfunc (UnimplementedBankServer) GetTransactions(*GetTransactionsRequest, Bank_GetTransactionsServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method GetTransactions not implemented\")\n}\nfunc (UnimplementedBankServer) Deposit(context.Context, *DepositRequest) (*BalanceResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Deposit not implemented\")\n}\nfunc (UnimplementedBankServer) Withdraw(context.Context, *WithdrawRequest) (*BalanceResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Withdraw not implemented\")\n}\nfunc (UnimplementedBankServer) Transfer(context.Context, *TransferRequest) (*TransferResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Transfer not implemented\")\n}\nfunc (UnimplementedBankServer) mustEmbedUnimplementedBankServer() {}\n\n// UnsafeBankServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to BankServer will\n// result in compilation errors.\ntype UnsafeBankServer interface {\n\tmustEmbedUnimplementedBankServer()\n}\n\nfunc RegisterBankServer(s grpc.ServiceRegistrar, srv BankServer) {\n\ts.RegisterService(&Bank_ServiceDesc, srv)\n}\n\nfunc _Bank_OpenAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(OpenAccountRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BankServer).OpenAccount(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/Bank/OpenAccount\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BankServer).OpenAccount(ctx, req.(*OpenAccountRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Bank_CloseAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(CloseAccountRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BankServer).CloseAccount(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/Bank/CloseAccount\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BankServer).CloseAccount(ctx, req.(*CloseAccountRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Bank_GetAccounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(emptypb.Empty)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BankServer).GetAccounts(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/Bank/GetAccounts\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BankServer).GetAccounts(ctx, req.(*emptypb.Empty))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Bank_GetTransactions_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(GetTransactionsRequest)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(BankServer).GetTransactions(m, &bankGetTransactionsServer{stream})\n}\n\ntype Bank_GetTransactionsServer interface {\n\tSend(*Transaction) error\n\tgrpc.ServerStream\n}\n\ntype bankGetTransactionsServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *bankGetTransactionsServer) Send(m *Transaction) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc _Bank_Deposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DepositRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BankServer).Deposit(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/Bank/Deposit\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BankServer).Deposit(ctx, req.(*DepositRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Bank_Withdraw_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(WithdrawRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BankServer).Withdraw(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/Bank/Withdraw\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BankServer).Withdraw(ctx, req.(*WithdrawRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Bank_Transfer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(TransferRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BankServer).Transfer(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/Bank/Transfer\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BankServer).Transfer(ctx, req.(*TransferRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// Bank_ServiceDesc is the grpc.ServiceDesc for Bank service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar Bank_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"Bank\",\n\tHandlerType: (*BankServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"OpenAccount\",\n\t\t\tHandler:    _Bank_OpenAccount_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"CloseAccount\",\n\t\t\tHandler:    _Bank_CloseAccount_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"GetAccounts\",\n\t\t\tHandler:    _Bank_GetAccounts_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Deposit\",\n\t\t\tHandler:    _Bank_Deposit_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Withdraw\",\n\t\t\tHandler:    _Bank_Withdraw_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Transfer\",\n\t\t\tHandler:    _Bank_Transfer_Handler,\n\t\t},\n\t},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"GetTransactions\",\n\t\t\tHandler:       _Bank_GetTransactions_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t},\n\tMetadata: \"bank.proto\",\n}\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/chat.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"sync\"\n\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/types/known/timestamppb\"\n)\n\n// chatServer implements the Support gRPC service, for providing\n// a capability to connect customers and support agents in real-time\n// chat.\ntype chatServer struct {\n\tUnimplementedSupportServer\n\tchatsBySession     map[string]*session\n\tchatsAwaitingAgent []string\n\tlastSession        int32\n\tmu                 sync.RWMutex\n}\n\ntype session struct {\n\tSession\n\tactive bool\n\tcust   *listener\n\tagents map[string]*listener\n\tmu     sync.RWMutex\n}\n\ntype listener struct {\n\tch  chan<- *ChatEntry\n\tctx context.Context\n}\n\nfunc (l *listener) send(e *ChatEntry) {\n\tselect {\n\tcase l.ch <- e:\n\tcase <-l.ctx.Done():\n\t}\n}\n\nfunc (s *session) copySession() *Session {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn &Session{\n\t\tSessionId:    s.SessionId,\n\t\tCustomerName: s.Session.CustomerName,\n\t\tHistory:      s.Session.History,\n\t}\n}\n\nfunc (s *chatServer) ChatCustomer(stream Support_ChatCustomerServer) error {\n\tctx, cancel := context.WithCancel(stream.Context())\n\tdefer cancel()\n\n\tcust := getCustomer(ctx)\n\tif cust == \"\" {\n\t\treturn status.Error(codes.Unauthenticated, codes.Unauthenticated.String())\n\t}\n\n\tvar sess *session\n\tvar ch chan *ChatEntry\n\tvar chCancel context.CancelFunc\n\tcleanup := func() {\n\t\tif sess != nil {\n\t\t\tsess.mu.Lock()\n\t\t\tsess.cust = nil\n\t\t\tsess.mu.Unlock()\n\t\t\tchCancel()\n\t\t\tclose(ch)\n\t\t\tgo func() {\n\t\t\t\t// drain channel to prevent deadlock\n\t\t\t\tfor range ch {\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t}\n\tdefer cleanup()\n\tfor {\n\t\treq, err := stream.Recv()\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\treturn err\n\t\t}\n\n\t\tswitch req := req.Req.(type) {\n\t\tcase *ChatCustomerRequest_Init:\n\t\t\tif sess != nil {\n\t\t\t\treturn status.Errorf(codes.FailedPrecondition, \"already called init, currently in chat session %q\", sess.SessionId)\n\t\t\t}\n\t\t\tsessionID := req.Init.ResumeSessionId\n\t\t\tif sessionID == \"\" {\n\t\t\t\tsess, ch, chCancel = s.newSession(ctx, cust)\n\t\t\t} else if sess, ch, chCancel = s.resumeSession(ctx, cust, sessionID); sess == nil {\n\t\t\t\treturn status.Errorf(codes.FailedPrecondition, \"cannot resume session %q; it is not an open session\", sessionID)\n\t\t\t}\n\t\t\terr := stream.Send(&ChatCustomerResponse{\n\t\t\t\tResp: &ChatCustomerResponse_Session{\n\t\t\t\t\tSession: sess.copySession(),\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t// monitor the returned channel, sending incoming agent messages down the pipe\n\t\t\tgo func() {\n\t\t\t\tfor {\n\t\t\t\t\tselect {\n\t\t\t\t\tcase entry, ok := <-ch:\n\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif e, ok := entry.Entry.(*ChatEntry_AgentMsg); ok {\n\t\t\t\t\t\t\tstream.Send(&ChatCustomerResponse{\n\t\t\t\t\t\t\t\tResp: &ChatCustomerResponse_Msg{\n\t\t\t\t\t\t\t\t\tMsg: e.AgentMsg,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\n\t\tcase *ChatCustomerRequest_Msg:\n\t\t\tif sess == nil {\n\t\t\t\treturn status.Errorf(codes.FailedPrecondition, \"never called init, no chat session for message\")\n\t\t\t}\n\n\t\t\tentry := &ChatEntry{\n\t\t\t\tDate: timestamppb.Now(),\n\t\t\t\tEntry: &ChatEntry_CustomerMsg{\n\t\t\t\t\tCustomerMsg: req.Msg,\n\t\t\t\t},\n\t\t\t}\n\t\t\tfunc() {\n\t\t\t\tsess.mu.Lock()\n\t\t\t\tsess.Session.History = append(sess.Session.History, entry)\n\t\t\t\tsess.mu.Unlock()\n\n\t\t\t\tsess.mu.RLock()\n\t\t\t\tdefer sess.mu.RUnlock()\n\t\t\t\tfor _, l := range sess.agents {\n\t\t\t\t\tl.send(entry)\n\t\t\t\t}\n\t\t\t}()\n\n\t\tcase *ChatCustomerRequest_HangUp:\n\t\t\tif sess == nil {\n\t\t\t\treturn status.Errorf(codes.FailedPrecondition, \"never called init, no chat session to hang up\")\n\t\t\t}\n\t\t\ts.closeSession(sess)\n\t\t\tcleanup()\n\t\t\tsess = nil\n\n\t\tdefault:\n\t\t\treturn status.Error(codes.InvalidArgument, \"unknown request type\")\n\t\t}\n\t}\n}\n\nfunc (s *chatServer) ChatAgent(stream Support_ChatAgentServer) error {\n\tctx, cancel := context.WithCancel(stream.Context())\n\tdefer cancel()\n\n\tagent := getAgent(ctx)\n\tif agent == \"\" {\n\t\treturn status.Error(codes.Unauthenticated, codes.Unauthenticated.String())\n\t}\n\n\tvar sess *session\n\tvar ch chan *ChatEntry\n\tvar chCancel context.CancelFunc\n\tcleanup := func() {\n\t\tif sess != nil {\n\t\t\tsess.mu.Lock()\n\t\t\tdelete(sess.agents, agent)\n\t\t\tif len(sess.agents) == 0 {\n\t\t\t\ts.mu.Lock()\n\t\t\t\ts.chatsAwaitingAgent = append(s.chatsAwaitingAgent, sess.SessionId)\n\t\t\t\ts.mu.Unlock()\n\t\t\t}\n\t\t\tsess.mu.Unlock()\n\t\t\tchCancel()\n\t\t\tclose(ch)\n\t\t\tgo func() {\n\t\t\t\t// drain channel to prevent deadlock\n\t\t\t\tfor range ch {\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t}\n\tdefer cleanup()\n\n\tcheckSession := func() {\n\t\t// see if session was concurrently closed\n\t\tif sess != nil {\n\t\t\tsess.mu.RLock()\n\t\t\tactive := sess.active\n\t\t\tsess.mu.RUnlock()\n\t\t\tif !active {\n\t\t\t\tcleanup()\n\t\t\t\tsess = nil\n\t\t\t}\n\t\t}\n\t}\n\n\tfor {\n\t\treq, err := stream.Recv()\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\treturn err\n\t\t}\n\n\t\tcheckSession()\n\n\t\tswitch req := req.Req.(type) {\n\t\tcase *ChatAgentRequest_Accept:\n\t\t\tif sess != nil {\n\t\t\t\treturn status.Errorf(codes.FailedPrecondition, \"already called accept, currently in chat session %q\", sess.SessionId)\n\t\t\t}\n\t\t\tsess, ch, chCancel = s.acceptSession(ctx, agent, req.Accept.SessionId)\n\t\t\tif sess == nil {\n\t\t\t\treturn status.Errorf(codes.FailedPrecondition, \"no session to accept\")\n\t\t\t}\n\t\t\terr := stream.Send(&ChatAgentResponse{\n\t\t\t\tResp: &ChatAgentResponse_AcceptedSession{\n\t\t\t\t\tAcceptedSession: sess.copySession(),\n\t\t\t\t},\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t// monitor the returned channel, sending incoming agent messages down the pipe\n\t\t\tgo func() {\n\t\t\t\tfor {\n\t\t\t\t\tselect {\n\t\t\t\t\tcase entry, ok := <-ch:\n\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif entry == nil {\n\t\t\t\t\t\t\tstream.Send(&ChatAgentResponse{\n\t\t\t\t\t\t\t\tResp: &ChatAgentResponse_SessionEnded{\n\t\t\t\t\t\t\t\t\tSessionEnded: Void_VOID,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif agentMsg, ok := entry.Entry.(*ChatEntry_AgentMsg); ok {\n\t\t\t\t\t\t\tif agentMsg.AgentMsg.AgentName == agent {\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstream.Send(&ChatAgentResponse{\n\t\t\t\t\t\t\tResp: &ChatAgentResponse_Msg{\n\t\t\t\t\t\t\t\tMsg: entry,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\n\t\tcase *ChatAgentRequest_Msg:\n\t\t\tif sess == nil {\n\t\t\t\treturn status.Errorf(codes.FailedPrecondition, \"never called accept, no chat session for message\")\n\t\t\t}\n\n\t\t\tentry := &ChatEntry{\n\t\t\t\tDate: timestamppb.Now(),\n\t\t\t\tEntry: &ChatEntry_AgentMsg{\n\t\t\t\t\tAgentMsg: &AgentMessage{\n\t\t\t\t\t\tAgentName: agent,\n\t\t\t\t\t\tMsg:       req.Msg,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\tactive := true\n\t\t\tfunc() {\n\t\t\t\tsess.mu.Lock()\n\t\t\t\tactive = sess.active\n\t\t\t\tif active {\n\t\t\t\t\tsess.Session.History = append(sess.Session.History, entry)\n\t\t\t\t}\n\t\t\t\tsess.mu.Unlock()\n\n\t\t\t\tif !active {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tsess.mu.RLock()\n\t\t\t\tdefer sess.mu.RUnlock()\n\t\t\t\tif sess.cust != nil {\n\t\t\t\t\tsess.cust.send(entry)\n\t\t\t\t}\n\t\t\t\tfor otherAgent, l := range sess.agents {\n\t\t\t\t\tif otherAgent == agent {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tl.send(entry)\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif !active {\n\t\t\t\treturn status.Errorf(codes.FailedPrecondition, \"customer hung up on chat session %s\", sess.SessionId)\n\t\t\t}\n\n\t\tcase *ChatAgentRequest_LeaveSession:\n\t\t\tif sess == nil {\n\t\t\t\treturn status.Errorf(codes.FailedPrecondition, \"never called init, no chat session to hang up\")\n\t\t\t}\n\t\t\ts.closeSession(sess)\n\t\t\tcleanup()\n\t\t\tsess = nil\n\n\t\tdefault:\n\t\t\treturn status.Error(codes.InvalidArgument, \"unknown request type\")\n\t\t}\n\t}\n}\n\nfunc (s *chatServer) newSession(ctx context.Context, cust string) (*session, chan *ChatEntry, context.CancelFunc) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\ts.lastSession++\n\tid := fmt.Sprintf(\"%06d\", s.lastSession)\n\ts.chatsAwaitingAgent = append(s.chatsAwaitingAgent, id)\n\n\tch := make(chan *ChatEntry, 1)\n\tctx, cancel := context.WithCancel(ctx)\n\tl := &listener{\n\t\tch:  ch,\n\t\tctx: ctx,\n\t}\n\tsess := session{\n\t\tactive: true,\n\t\tSession: Session{\n\t\t\tSessionId:    id,\n\t\t\tCustomerName: cust,\n\t\t},\n\t\tcust: l,\n\t}\n\ts.chatsBySession[id] = &sess\n\n\treturn &sess, ch, cancel\n}\n\nfunc (s *chatServer) resumeSession(ctx context.Context, cust, sessionID string) (*session, chan *ChatEntry, context.CancelFunc) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tsess := s.chatsBySession[sessionID]\n\tif sess.CustomerName != cust {\n\t\t// customer cannot join chat that they did not start\n\t\treturn nil, nil, nil\n\t}\n\tif !sess.active {\n\t\t// chat has been closed\n\t\treturn nil, nil, nil\n\t}\n\tif sess.cust != nil {\n\t\t// customer is active in the chat in another stream!\n\t\treturn nil, nil, nil\n\t}\n\n\tch := make(chan *ChatEntry, 1)\n\tctx, cancel := context.WithCancel(ctx)\n\tl := &listener{\n\t\tch:  ch,\n\t\tctx: ctx,\n\t}\n\tsess.cust = l\n\treturn sess, ch, cancel\n}\n\nfunc (s *chatServer) closeSession(sess *session) {\n\tactive := true\n\tfunc() {\n\t\tsess.mu.Lock()\n\t\tactive = sess.active\n\t\tsess.active = false\n\t\tsess.mu.Unlock()\n\n\t\tif !active {\n\t\t\t// already closed\n\t\t\treturn\n\t\t}\n\n\t\tsess.mu.RLock()\n\t\tdefer sess.mu.RUnlock()\n\t\tfor _, l := range sess.agents {\n\t\t\tl.send(nil)\n\t\t}\n\t}()\n\n\tif !active {\n\t\t// already closed\n\t\treturn\n\t}\n\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tdelete(s.chatsBySession, sess.SessionId)\n\tfor i, id := range s.chatsAwaitingAgent {\n\t\tif id == sess.SessionId {\n\t\t\ts.chatsAwaitingAgent = append(s.chatsAwaitingAgent[:i], s.chatsAwaitingAgent[i+1:]...)\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc (s *chatServer) acceptSession(ctx context.Context, agent, sessionID string) (*session, chan *ChatEntry, context.CancelFunc) {\n\tvar sess *session\n\tfunc() {\n\t\ts.mu.Lock()\n\t\tdefer s.mu.Unlock()\n\n\t\tif len(s.chatsAwaitingAgent) == 0 {\n\t\t\treturn\n\t\t}\n\t\tif sessionID == \"\" {\n\t\t\tsessionID = s.chatsAwaitingAgent[0]\n\t\t\ts.chatsAwaitingAgent = s.chatsAwaitingAgent[1:]\n\t\t} else {\n\t\t\tfound := false\n\t\t\tfor i, id := range s.chatsAwaitingAgent {\n\t\t\t\tif id == sessionID {\n\t\t\t\t\tfound = true\n\t\t\t\t\ts.chatsAwaitingAgent = append(s.chatsAwaitingAgent[:i], s.chatsAwaitingAgent[i+1:]...)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !found {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tsess = s.chatsBySession[sessionID]\n\t}()\n\tif sess == nil {\n\t\treturn nil, nil, nil\n\t}\n\tch := make(chan *ChatEntry, 1)\n\tctx, cancel := context.WithCancel(ctx)\n\tl := &listener{\n\t\tch:  ch,\n\t\tctx: ctx,\n\t}\n\tsess.mu.Lock()\n\tif sess.agents == nil {\n\t\tsess.agents = map[string]*listener{}\n\t}\n\tsess.agents[agent] = l\n\tsess.mu.Unlock()\n\treturn sess, ch, cancel\n}\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/db.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/encoding/protojson\"\n\t\"google.golang.org/protobuf/types/known/timestamppb\"\n)\n\n// In-memory database that is periodically saved to a JSON file.\n\ntype accounts struct {\n\tAccountNumbersByCustomer map[string][]uint64\n\tAccountsByNumber         map[uint64]*account\n\tAccountNumbers           []uint64\n\tCustomers                []string\n\tLastAccountNum           uint64\n\tmu                       sync.RWMutex\n}\n\nfunc (a *accounts) openAccount(customer string, accountType Account_Type, initialBalanceCents int32) *Account {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\taccountNums, ok := a.AccountNumbersByCustomer[customer]\n\tif !ok {\n\t\t// no accounts for this customer? it's a new customer\n\t\ta.Customers = append(a.Customers, customer)\n\t}\n\tnum := a.LastAccountNum + 1\n\ta.LastAccountNum = num\n\ta.AccountNumbers = append(a.AccountNumbers, num)\n\taccountNums = append(accountNums, num)\n\ta.AccountNumbersByCustomer[customer] = accountNums\n\tvar acct account\n\tacct.AccountNumber = num\n\tacct.Type = accountType\n\tacct.BalanceCents = initialBalanceCents\n\tacct.Transactions = append(acct.Transactions, &Transaction{\n\t\tAccountNumber: num,\n\t\tSeqNumber:     1,\n\t\tDate:          timestamppb.Now(),\n\t\tAmountCents:   initialBalanceCents,\n\t\tDesc:          \"initial deposit\",\n\t})\n\ta.AccountsByNumber[num] = &acct\n\treturn &acct.Account\n}\n\nfunc (a *accounts) closeAccount(customer string, accountNumber uint64) error {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\tacctNums := a.AccountNumbersByCustomer[customer]\n\tfound := -1\n\tfor i, num := range acctNums {\n\t\tif num == accountNumber {\n\t\t\tfound = i\n\t\t\tbreak\n\t\t}\n\t}\n\tif found == -1 {\n\t\treturn status.Errorf(codes.NotFound, \"you have no account numbered %d\", accountNumber)\n\t}\n\n\tacct := a.AccountsByNumber[accountNumber]\n\tif acct.BalanceCents != 0 {\n\t\treturn status.Errorf(codes.FailedPrecondition, \"account %d cannot be closed because it has a non-zero balance: %s\", accountNumber, dollars(acct.BalanceCents))\n\t}\n\n\tfor i, num := range a.AccountNumbers {\n\t\tif num == accountNumber {\n\t\t\ta.AccountNumbers = append(a.AccountNumbers[:i], a.AccountNumbers[i+1:]...)\n\t\t\tbreak\n\t\t}\n\t}\n\n\ta.AccountNumbersByCustomer[customer] = append(acctNums[:found], acctNums[found+1:]...)\n\tdelete(a.AccountsByNumber, accountNumber)\n\treturn nil\n}\n\nfunc (a *accounts) getAccount(customer string, accountNumber uint64) (*account, error) {\n\ta.mu.RLock()\n\tdefer a.mu.RUnlock()\n\tacctNums := a.AccountNumbersByCustomer[customer]\n\tfor _, num := range acctNums {\n\t\tif num == accountNumber {\n\t\t\treturn a.AccountsByNumber[num], nil\n\t\t}\n\t}\n\treturn nil, status.Errorf(codes.NotFound, \"you have no account numbered %d\", accountNumber)\n}\n\nfunc (a *accounts) getAllAccounts(customer string) []*Account {\n\ta.mu.RLock()\n\tdefer a.mu.RUnlock()\n\n\taccountNums := a.AccountNumbersByCustomer[customer]\n\tvar accounts []*Account\n\tfor _, num := range accountNums {\n\t\taccounts = append(accounts, &a.AccountsByNumber[num].Account)\n\t}\n\treturn accounts\n}\n\ntype account struct {\n\tAccount\n\tTransactions []*Transaction\n\tmu           sync.RWMutex\n}\n\nfunc (a *account) getTransactions() []*Transaction {\n\ta.mu.RLock()\n\tdefer a.mu.RUnlock()\n\treturn a.Transactions\n}\n\nfunc (a *account) newTransaction(amountCents int32, desc string) (newBalance int32, err error) {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\tbal := a.BalanceCents + amountCents\n\tif bal < 0 {\n\t\treturn 0, status.Errorf(codes.FailedPrecondition, \"insufficient funds: cannot withdraw %s when balance is %s\", dollars(amountCents), dollars(a.BalanceCents))\n\t}\n\ta.BalanceCents = bal\n\ta.Transactions = append(a.Transactions, &Transaction{\n\t\tAccountNumber: a.AccountNumber,\n\t\tDate:          timestamppb.Now(),\n\t\tAmountCents:   amountCents,\n\t\tSeqNumber:     uint64(len(a.Transactions) + 1),\n\t\tDesc:          desc,\n\t})\n\treturn bal, nil\n}\n\nfunc (t *Transaction) MarshalJSON() ([]byte, error) {\n\treturn protojson.Marshal(t)\n}\n\nfunc (t *Transaction) UnmarshalJSON(b []byte) error {\n\treturn protojson.Unmarshal(b, t)\n}\n\nfunc (a *accounts) clone() *accounts {\n\tvar clone accounts\n\tclone.AccountNumbersByCustomer = map[string][]uint64{}\n\tclone.AccountsByNumber = map[uint64]*account{}\n\n\ta.mu.RLock()\n\tclone.Customers = a.Customers\n\ta.mu.RUnlock()\n\n\tfor _, cust := range clone.Customers {\n\t\tvar acctNums []uint64\n\t\ta.mu.RLock()\n\t\tacctNums = a.AccountNumbersByCustomer[cust]\n\t\ta.mu.RUnlock()\n\n\t\tclone.AccountNumbersByCustomer[cust] = acctNums\n\t\tclone.AccountNumbers = append(clone.AccountNumbers, acctNums...)\n\n\t\tfor _, acctNum := range acctNums {\n\t\t\ta.mu.RLock()\n\t\t\tacct := a.AccountsByNumber[acctNum]\n\t\t\ta.mu.RUnlock()\n\n\t\t\tacct.mu.RLock()\n\t\t\ttxns := acct.Transactions\n\t\t\tacct.mu.RUnlock()\n\n\t\t\tclone.AccountsByNumber[acctNum] = &account{Transactions: txns}\n\t\t}\n\t}\n\n\treturn &clone\n}\n\nfunc dollars(amountCents int32) string {\n\treturn fmt.Sprintf(\"$%02f\", float64(amountCents)/100)\n}\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/main.go",
    "content": "package main\n\n//go:generate protoc --go_out=. --go-grpc_out=. bank.proto support.proto\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"os/signal\"\n\t\"sync/atomic\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/grpclog\"\n\t\"google.golang.org/grpc/peer\"\n\t\"google.golang.org/grpc/reflection\"\n\t\"google.golang.org/grpc/status\"\n)\n\nfunc main() {\n\tgrpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stdout, os.Stderr))\n\n\tport := flag.Int(\"port\", 12345, \"The port on which bankdemo gRPC server will listen.\")\n\tdatafile := flag.String(\"datafile\", \"accounts.json\", \"The path and filename to which bank account data is saved and from which data will be loaded.\")\n\tflag.Parse()\n\n\t// create the server and load initial dataset\n\tctx, cancel := context.WithCancel(context.Background())\n\ts := &svr{\n\t\tdatafile: *datafile,\n\t\tctx:      ctx,\n\t\tcancel:   cancel,\n\t}\n\tif err := s.load(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tl, err := net.Listen(\"tcp\", fmt.Sprintf(\"127.0.0.1:%d\", *port))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tgrpcSvr := gRPCServer()\n\n\t// Register gRPC service implementations\n\tbankSvc := bankServer{\n\t\tallAccounts: &s.allAccounts,\n\t}\n\tRegisterBankServer(grpcSvr, &bankSvc)\n\n\tchatSvc := chatServer{\n\t\tchatsBySession: map[string]*session{},\n\t}\n\tRegisterSupportServer(grpcSvr, &chatSvc)\n\n\tgo s.bgSaver()\n\n\t// don't forget to include server reflection support!\n\treflection.Register(grpcSvr)\n\n\tdefer func() {\n\t\tcancel()\n\t\ts.flush()\n\t}()\n\n\t// trap SIGINT / SIGTERM to exit cleanly\n\tc := make(chan os.Signal, 1)\n\tsignal.Notify(c, syscall.SIGINT)\n\tsignal.Notify(c, syscall.SIGTERM)\n\tgo func() {\n\t\t<-c\n\t\tfmt.Println(\"Shutting down...\")\n\t\tgrpcSvr.GracefulStop()\n\t}()\n\n\tgrpclog.Infof(\"server starting, listening on %v\", l.Addr())\n\tif err := grpcSvr.Serve(l); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc gRPCServer() *grpc.Server {\n\tvar reqCounter uint64\n\treturn grpc.NewServer(\n\t\tgrpc.UnaryInterceptor(func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {\n\t\t\treqID := atomic.AddUint64(&reqCounter, 1)\n\t\t\tvar client string\n\t\t\tif p, ok := peer.FromContext(ctx); ok {\n\t\t\t\tclient = p.Addr.String()\n\t\t\t} else {\n\t\t\t\tclient = \"?\"\n\t\t\t}\n\t\t\tgrpclog.Infof(\"request %d started for %s from %s\", reqID, info.FullMethod, client)\n\n\t\t\trsp, err := handler(ctx, req)\n\n\t\t\tstat, _ := status.FromError(err)\n\t\t\tgrpclog.Infof(\"request %d completed for %s from %s: %v %s\", reqID, info.FullMethod, client, stat.Code(), stat.Message())\n\t\t\treturn rsp, err\n\n\t\t}),\n\t\tgrpc.StreamInterceptor(func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {\n\t\t\treqID := atomic.AddUint64(&reqCounter, 1)\n\t\t\tvar client string\n\t\t\tif p, ok := peer.FromContext(ss.Context()); ok {\n\t\t\t\tclient = p.Addr.String()\n\t\t\t} else {\n\t\t\t\tclient = \"?\"\n\t\t\t}\n\t\t\tgrpclog.Infof(\"request %d started for %s from %s\", reqID, info.FullMethod, client)\n\n\t\t\terr := handler(srv, ss)\n\n\t\t\tstat, _ := status.FromError(err)\n\t\t\tgrpclog.Infof(\"request %d completed for %s from %s: %v %s\", reqID, info.FullMethod, client, stat.Code(), stat.Message())\n\t\t\treturn err\n\t\t}))\n}\n\ntype svr struct {\n\tdatafile    string\n\tctx         context.Context\n\tcancel      context.CancelFunc\n\tallAccounts accounts\n}\n\nfunc (s *svr) load() error {\n\taccts, err := os.ReadFile(s.datafile)\n\tif err != nil && !os.IsNotExist(err) {\n\t\treturn err\n\t}\n\tif len(accts) == 0 {\n\t\ts.allAccounts.AccountNumbersByCustomer = map[string][]uint64{}\n\t\ts.allAccounts.AccountsByNumber = map[uint64]*account{}\n\t} else if err := json.Unmarshal(accts, &s.allAccounts); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *svr) bgSaver() {\n\tticker := time.NewTicker(5 * time.Second)\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\ts.flush()\n\t\tcase <-s.ctx.Done():\n\t\t\tticker.Stop()\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (s *svr) flush() {\n\taccounts := s.allAccounts.clone()\n\n\tif b, err := json.Marshal(accounts); err != nil {\n\t\tgrpclog.Errorf(\"failed to save data to %q\", s.datafile)\n\t} else if err := os.WriteFile(s.datafile, b, 0666); err != nil {\n\t\tgrpclog.Errorf(\"failed to save data to %q\", s.datafile)\n\t}\n}\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/support.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v4.22.0\n// source: support.proto\n\npackage main\n\nimport (\n\tproto \"github.com/golang/protobuf/proto\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\ttimestamppb \"google.golang.org/protobuf/types/known/timestamppb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// This is a compile-time assertion that a sufficiently up-to-date version\n// of the legacy proto package is being used.\nconst _ = proto.ProtoPackageIsVersion4\n\ntype Void int32\n\nconst (\n\tVoid_VOID Void = 0\n)\n\n// Enum value maps for Void.\nvar (\n\tVoid_name = map[int32]string{\n\t\t0: \"VOID\",\n\t}\n\tVoid_value = map[string]int32{\n\t\t\"VOID\": 0,\n\t}\n)\n\nfunc (x Void) Enum() *Void {\n\tp := new(Void)\n\t*p = x\n\treturn p\n}\n\nfunc (x Void) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (Void) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_support_proto_enumTypes[0].Descriptor()\n}\n\nfunc (Void) Type() protoreflect.EnumType {\n\treturn &file_support_proto_enumTypes[0]\n}\n\nfunc (x Void) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use Void.Descriptor instead.\nfunc (Void) EnumDescriptor() ([]byte, []int) {\n\treturn file_support_proto_rawDescGZIP(), []int{0}\n}\n\ntype ChatCustomerRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Types that are assignable to Req:\n\t//\n\t//\t*ChatCustomerRequest_Init\n\t//\t*ChatCustomerRequest_Msg\n\t//\t*ChatCustomerRequest_HangUp\n\tReq isChatCustomerRequest_Req `protobuf_oneof:\"req\"`\n}\n\nfunc (x *ChatCustomerRequest) Reset() {\n\t*x = ChatCustomerRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_support_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChatCustomerRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChatCustomerRequest) ProtoMessage() {}\n\nfunc (x *ChatCustomerRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_support_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ChatCustomerRequest.ProtoReflect.Descriptor instead.\nfunc (*ChatCustomerRequest) Descriptor() ([]byte, []int) {\n\treturn file_support_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (m *ChatCustomerRequest) GetReq() isChatCustomerRequest_Req {\n\tif m != nil {\n\t\treturn m.Req\n\t}\n\treturn nil\n}\n\nfunc (x *ChatCustomerRequest) GetInit() *InitiateChat {\n\tif x, ok := x.GetReq().(*ChatCustomerRequest_Init); ok {\n\t\treturn x.Init\n\t}\n\treturn nil\n}\n\nfunc (x *ChatCustomerRequest) GetMsg() string {\n\tif x, ok := x.GetReq().(*ChatCustomerRequest_Msg); ok {\n\t\treturn x.Msg\n\t}\n\treturn \"\"\n}\n\nfunc (x *ChatCustomerRequest) GetHangUp() Void {\n\tif x, ok := x.GetReq().(*ChatCustomerRequest_HangUp); ok {\n\t\treturn x.HangUp\n\t}\n\treturn Void_VOID\n}\n\ntype isChatCustomerRequest_Req interface {\n\tisChatCustomerRequest_Req()\n}\n\ntype ChatCustomerRequest_Init struct {\n\t// init is used when a chat stream is not part of a\n\t// chat session. This is a stream's initial state, as well as\n\t// the state after a \"hang_up\" request is sent. This creates\n\t// a new state session or resumes an existing one.\n\tInit *InitiateChat `protobuf:\"bytes,1,opt,name=init,proto3,oneof\"`\n}\n\ntype ChatCustomerRequest_Msg struct {\n\t// msg is used to send the customer's messages to support\n\t// agents.\n\tMsg string `protobuf:\"bytes,2,opt,name=msg,proto3,oneof\"`\n}\n\ntype ChatCustomerRequest_HangUp struct {\n\t// hang_up is used to terminate a chat session. If a stream\n\t// is broken, but the session was not terminated, the client\n\t// may initiate a new stream and use init to resume that\n\t// session. Sessions are not terminated unless done so\n\t// explicitly via sending this kind of request on the stream.\n\tHangUp Void `protobuf:\"varint,3,opt,name=hang_up,json=hangUp,proto3,enum=Void,oneof\"`\n}\n\nfunc (*ChatCustomerRequest_Init) isChatCustomerRequest_Req() {}\n\nfunc (*ChatCustomerRequest_Msg) isChatCustomerRequest_Req() {}\n\nfunc (*ChatCustomerRequest_HangUp) isChatCustomerRequest_Req() {}\n\ntype InitiateChat struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tResumeSessionId string `protobuf:\"bytes,1,opt,name=resume_session_id,json=resumeSessionId,proto3\" json:\"resume_session_id,omitempty\"`\n}\n\nfunc (x *InitiateChat) Reset() {\n\t*x = InitiateChat{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_support_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *InitiateChat) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*InitiateChat) ProtoMessage() {}\n\nfunc (x *InitiateChat) ProtoReflect() protoreflect.Message {\n\tmi := &file_support_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use InitiateChat.ProtoReflect.Descriptor instead.\nfunc (*InitiateChat) Descriptor() ([]byte, []int) {\n\treturn file_support_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *InitiateChat) GetResumeSessionId() string {\n\tif x != nil {\n\t\treturn x.ResumeSessionId\n\t}\n\treturn \"\"\n}\n\ntype AgentMessage struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAgentName string `protobuf:\"bytes,1,opt,name=agent_name,json=agentName,proto3\" json:\"agent_name,omitempty\"`\n\tMsg       string `protobuf:\"bytes,2,opt,name=msg,proto3\" json:\"msg,omitempty\"`\n}\n\nfunc (x *AgentMessage) Reset() {\n\t*x = AgentMessage{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_support_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AgentMessage) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AgentMessage) ProtoMessage() {}\n\nfunc (x *AgentMessage) ProtoReflect() protoreflect.Message {\n\tmi := &file_support_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AgentMessage.ProtoReflect.Descriptor instead.\nfunc (*AgentMessage) Descriptor() ([]byte, []int) {\n\treturn file_support_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *AgentMessage) GetAgentName() string {\n\tif x != nil {\n\t\treturn x.AgentName\n\t}\n\treturn \"\"\n}\n\nfunc (x *AgentMessage) GetMsg() string {\n\tif x != nil {\n\t\treturn x.Msg\n\t}\n\treturn \"\"\n}\n\ntype ChatCustomerResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Types that are assignable to Resp:\n\t//\n\t//\t*ChatCustomerResponse_Session\n\t//\t*ChatCustomerResponse_Msg\n\tResp isChatCustomerResponse_Resp `protobuf_oneof:\"resp\"`\n}\n\nfunc (x *ChatCustomerResponse) Reset() {\n\t*x = ChatCustomerResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_support_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChatCustomerResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChatCustomerResponse) ProtoMessage() {}\n\nfunc (x *ChatCustomerResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_support_proto_msgTypes[3]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ChatCustomerResponse.ProtoReflect.Descriptor instead.\nfunc (*ChatCustomerResponse) Descriptor() ([]byte, []int) {\n\treturn file_support_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (m *ChatCustomerResponse) GetResp() isChatCustomerResponse_Resp {\n\tif m != nil {\n\t\treturn m.Resp\n\t}\n\treturn nil\n}\n\nfunc (x *ChatCustomerResponse) GetSession() *Session {\n\tif x, ok := x.GetResp().(*ChatCustomerResponse_Session); ok {\n\t\treturn x.Session\n\t}\n\treturn nil\n}\n\nfunc (x *ChatCustomerResponse) GetMsg() *AgentMessage {\n\tif x, ok := x.GetResp().(*ChatCustomerResponse_Msg); ok {\n\t\treturn x.Msg\n\t}\n\treturn nil\n}\n\ntype isChatCustomerResponse_Resp interface {\n\tisChatCustomerResponse_Resp()\n}\n\ntype ChatCustomerResponse_Session struct {\n\t// session is sent from the server when the stream is connected\n\t// to a chat session. This happens after an init request is sent\n\t// and the stream is connected to either a new or resumed session.\n\tSession *Session `protobuf:\"bytes,1,opt,name=session,proto3,oneof\"`\n}\n\ntype ChatCustomerResponse_Msg struct {\n\t// msg is sent from the server to convey agents' messages to the\n\t// customer.\n\tMsg *AgentMessage `protobuf:\"bytes,2,opt,name=msg,proto3,oneof\"`\n}\n\nfunc (*ChatCustomerResponse_Session) isChatCustomerResponse_Resp() {}\n\nfunc (*ChatCustomerResponse_Msg) isChatCustomerResponse_Resp() {}\n\ntype ChatAgentRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Types that are assignable to Req:\n\t//\n\t//\t*ChatAgentRequest_Accept\n\t//\t*ChatAgentRequest_Msg\n\t//\t*ChatAgentRequest_LeaveSession\n\tReq isChatAgentRequest_Req `protobuf_oneof:\"req\"`\n}\n\nfunc (x *ChatAgentRequest) Reset() {\n\t*x = ChatAgentRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_support_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChatAgentRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChatAgentRequest) ProtoMessage() {}\n\nfunc (x *ChatAgentRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_support_proto_msgTypes[4]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ChatAgentRequest.ProtoReflect.Descriptor instead.\nfunc (*ChatAgentRequest) Descriptor() ([]byte, []int) {\n\treturn file_support_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (m *ChatAgentRequest) GetReq() isChatAgentRequest_Req {\n\tif m != nil {\n\t\treturn m.Req\n\t}\n\treturn nil\n}\n\nfunc (x *ChatAgentRequest) GetAccept() *AcceptChat {\n\tif x, ok := x.GetReq().(*ChatAgentRequest_Accept); ok {\n\t\treturn x.Accept\n\t}\n\treturn nil\n}\n\nfunc (x *ChatAgentRequest) GetMsg() string {\n\tif x, ok := x.GetReq().(*ChatAgentRequest_Msg); ok {\n\t\treturn x.Msg\n\t}\n\treturn \"\"\n}\n\nfunc (x *ChatAgentRequest) GetLeaveSession() Void {\n\tif x, ok := x.GetReq().(*ChatAgentRequest_LeaveSession); ok {\n\t\treturn x.LeaveSession\n\t}\n\treturn Void_VOID\n}\n\ntype isChatAgentRequest_Req interface {\n\tisChatAgentRequest_Req()\n}\n\ntype ChatAgentRequest_Accept struct {\n\t// accept is used when an agent wants to join a customer chat\n\t// session. It can be used to connect to a specific session (by\n\t// ID), or to just accept the session for which the customer has\n\t// been waiting the longest (e.g. poll a FIFO queue of sessions\n\t// awaiting a support agent). It is possible for multiple agents\n\t// to be connected to the same chat session.\n\tAccept *AcceptChat `protobuf:\"bytes,1,opt,name=accept,proto3,oneof\"`\n}\n\ntype ChatAgentRequest_Msg struct {\n\t// msg is used to send a message to the customer. It will also be\n\t// delivered to any other connected support agents.\n\tMsg string `protobuf:\"bytes,2,opt,name=msg,proto3,oneof\"`\n}\n\ntype ChatAgentRequest_LeaveSession struct {\n\t// leave_session allows an agent to exit a chat session. They can\n\t// always re-enter later by sending an accept message for that\n\t// session ID.\n\tLeaveSession Void `protobuf:\"varint,3,opt,name=leave_session,json=leaveSession,proto3,enum=Void,oneof\"`\n}\n\nfunc (*ChatAgentRequest_Accept) isChatAgentRequest_Req() {}\n\nfunc (*ChatAgentRequest_Msg) isChatAgentRequest_Req() {}\n\nfunc (*ChatAgentRequest_LeaveSession) isChatAgentRequest_Req() {}\n\ntype AcceptChat struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSessionId string `protobuf:\"bytes,1,opt,name=session_id,json=sessionId,proto3\" json:\"session_id,omitempty\"`\n}\n\nfunc (x *AcceptChat) Reset() {\n\t*x = AcceptChat{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_support_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AcceptChat) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AcceptChat) ProtoMessage() {}\n\nfunc (x *AcceptChat) ProtoReflect() protoreflect.Message {\n\tmi := &file_support_proto_msgTypes[5]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AcceptChat.ProtoReflect.Descriptor instead.\nfunc (*AcceptChat) Descriptor() ([]byte, []int) {\n\treturn file_support_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *AcceptChat) GetSessionId() string {\n\tif x != nil {\n\t\treturn x.SessionId\n\t}\n\treturn \"\"\n}\n\ntype ChatEntry struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tDate *timestamppb.Timestamp `protobuf:\"bytes,1,opt,name=date,proto3\" json:\"date,omitempty\"`\n\t// Types that are assignable to Entry:\n\t//\n\t//\t*ChatEntry_CustomerMsg\n\t//\t*ChatEntry_AgentMsg\n\tEntry isChatEntry_Entry `protobuf_oneof:\"entry\"`\n}\n\nfunc (x *ChatEntry) Reset() {\n\t*x = ChatEntry{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_support_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChatEntry) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChatEntry) ProtoMessage() {}\n\nfunc (x *ChatEntry) ProtoReflect() protoreflect.Message {\n\tmi := &file_support_proto_msgTypes[6]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ChatEntry.ProtoReflect.Descriptor instead.\nfunc (*ChatEntry) Descriptor() ([]byte, []int) {\n\treturn file_support_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *ChatEntry) GetDate() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.Date\n\t}\n\treturn nil\n}\n\nfunc (m *ChatEntry) GetEntry() isChatEntry_Entry {\n\tif m != nil {\n\t\treturn m.Entry\n\t}\n\treturn nil\n}\n\nfunc (x *ChatEntry) GetCustomerMsg() string {\n\tif x, ok := x.GetEntry().(*ChatEntry_CustomerMsg); ok {\n\t\treturn x.CustomerMsg\n\t}\n\treturn \"\"\n}\n\nfunc (x *ChatEntry) GetAgentMsg() *AgentMessage {\n\tif x, ok := x.GetEntry().(*ChatEntry_AgentMsg); ok {\n\t\treturn x.AgentMsg\n\t}\n\treturn nil\n}\n\ntype isChatEntry_Entry interface {\n\tisChatEntry_Entry()\n}\n\ntype ChatEntry_CustomerMsg struct {\n\tCustomerMsg string `protobuf:\"bytes,2,opt,name=customer_msg,json=customerMsg,proto3,oneof\"`\n}\n\ntype ChatEntry_AgentMsg struct {\n\tAgentMsg *AgentMessage `protobuf:\"bytes,3,opt,name=agent_msg,json=agentMsg,proto3,oneof\"`\n}\n\nfunc (*ChatEntry_CustomerMsg) isChatEntry_Entry() {}\n\nfunc (*ChatEntry_AgentMsg) isChatEntry_Entry() {}\n\ntype ChatAgentResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Types that are assignable to Resp:\n\t//\n\t//\t*ChatAgentResponse_AcceptedSession\n\t//\t*ChatAgentResponse_Msg\n\t//\t*ChatAgentResponse_SessionEnded\n\tResp isChatAgentResponse_Resp `protobuf_oneof:\"resp\"`\n}\n\nfunc (x *ChatAgentResponse) Reset() {\n\t*x = ChatAgentResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_support_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChatAgentResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChatAgentResponse) ProtoMessage() {}\n\nfunc (x *ChatAgentResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_support_proto_msgTypes[7]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ChatAgentResponse.ProtoReflect.Descriptor instead.\nfunc (*ChatAgentResponse) Descriptor() ([]byte, []int) {\n\treturn file_support_proto_rawDescGZIP(), []int{7}\n}\n\nfunc (m *ChatAgentResponse) GetResp() isChatAgentResponse_Resp {\n\tif m != nil {\n\t\treturn m.Resp\n\t}\n\treturn nil\n}\n\nfunc (x *ChatAgentResponse) GetAcceptedSession() *Session {\n\tif x, ok := x.GetResp().(*ChatAgentResponse_AcceptedSession); ok {\n\t\treturn x.AcceptedSession\n\t}\n\treturn nil\n}\n\nfunc (x *ChatAgentResponse) GetMsg() *ChatEntry {\n\tif x, ok := x.GetResp().(*ChatAgentResponse_Msg); ok {\n\t\treturn x.Msg\n\t}\n\treturn nil\n}\n\nfunc (x *ChatAgentResponse) GetSessionEnded() Void {\n\tif x, ok := x.GetResp().(*ChatAgentResponse_SessionEnded); ok {\n\t\treturn x.SessionEnded\n\t}\n\treturn Void_VOID\n}\n\ntype isChatAgentResponse_Resp interface {\n\tisChatAgentResponse_Resp()\n}\n\ntype ChatAgentResponse_AcceptedSession struct {\n\t// accepted_session provides the detail of a chat session. The server\n\t// sends this message after the agent has accepted a chat session.\n\tAcceptedSession *Session `protobuf:\"bytes,1,opt,name=accepted_session,json=acceptedSession,proto3,oneof\"`\n}\n\ntype ChatAgentResponse_Msg struct {\n\t// msg is sent by the server when the customer, or another support\n\t// agent, sends a message in stream's current session.\n\tMsg *ChatEntry `protobuf:\"bytes,2,opt,name=msg,proto3,oneof\"`\n}\n\ntype ChatAgentResponse_SessionEnded struct {\n\t// session_ended notifies the support agent that their currently\n\t// connected chat session has been terminated by the customer.\n\tSessionEnded Void `protobuf:\"varint,3,opt,name=session_ended,json=sessionEnded,proto3,enum=Void,oneof\"`\n}\n\nfunc (*ChatAgentResponse_AcceptedSession) isChatAgentResponse_Resp() {}\n\nfunc (*ChatAgentResponse_Msg) isChatAgentResponse_Resp() {}\n\nfunc (*ChatAgentResponse_SessionEnded) isChatAgentResponse_Resp() {}\n\ntype Session struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSessionId    string       `protobuf:\"bytes,1,opt,name=session_id,json=sessionId,proto3\" json:\"session_id,omitempty\"`\n\tCustomerName string       `protobuf:\"bytes,2,opt,name=customer_name,json=customerName,proto3\" json:\"customer_name,omitempty\"`\n\tHistory      []*ChatEntry `protobuf:\"bytes,3,rep,name=history,proto3\" json:\"history,omitempty\"`\n}\n\nfunc (x *Session) Reset() {\n\t*x = Session{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_support_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Session) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Session) ProtoMessage() {}\n\nfunc (x *Session) ProtoReflect() protoreflect.Message {\n\tmi := &file_support_proto_msgTypes[8]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Session.ProtoReflect.Descriptor instead.\nfunc (*Session) Descriptor() ([]byte, []int) {\n\treturn file_support_proto_rawDescGZIP(), []int{8}\n}\n\nfunc (x *Session) GetSessionId() string {\n\tif x != nil {\n\t\treturn x.SessionId\n\t}\n\treturn \"\"\n}\n\nfunc (x *Session) GetCustomerName() string {\n\tif x != nil {\n\t\treturn x.CustomerName\n\t}\n\treturn \"\"\n}\n\nfunc (x *Session) GetHistory() []*ChatEntry {\n\tif x != nil {\n\t\treturn x.History\n\t}\n\treturn nil\n}\n\nvar File_support_proto protoreflect.FileDescriptor\n\nvar file_support_proto_rawDesc = []byte{\n\t0x0a, 0x0d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,\n\t0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,\n\t0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x22, 0x77, 0x0a, 0x13, 0x43, 0x68, 0x61, 0x74, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65,\n\t0x43, 0x68, 0x61, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x03,\n\t0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x6d, 0x73, 0x67,\n\t0x12, 0x20, 0x0a, 0x07, 0x68, 0x61, 0x6e, 0x67, 0x5f, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28,\n\t0x0e, 0x32, 0x05, 0x2e, 0x56, 0x6f, 0x69, 0x64, 0x48, 0x00, 0x52, 0x06, 0x68, 0x61, 0x6e, 0x67,\n\t0x55, 0x70, 0x42, 0x05, 0x0a, 0x03, 0x72, 0x65, 0x71, 0x22, 0x3a, 0x0a, 0x0c, 0x49, 0x6e, 0x69,\n\t0x74, 0x69, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x73,\n\t0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73,\n\t0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x3f, 0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x4d, 0x65,\n\t0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6e,\n\t0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x67, 0x65, 0x6e, 0x74,\n\t0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x67, 0x0a, 0x14, 0x43, 0x68, 0x61, 0x74, 0x43, 0x75,\n\t0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24,\n\t0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,\n\t0x08, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x07, 0x73, 0x65, 0x73,\n\t0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x0d, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,\n\t0x48, 0x00, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x42, 0x06, 0x0a, 0x04, 0x72, 0x65, 0x73, 0x70, 0x22,\n\t0x82, 0x01, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x43, 0x68, 0x61,\n\t0x74, 0x48, 0x00, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x12, 0x0a, 0x03, 0x6d,\n\t0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12,\n\t0x2c, 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,\n\t0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x05, 0x2e, 0x56, 0x6f, 0x69, 0x64, 0x48, 0x00, 0x52,\n\t0x0c, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x05, 0x0a,\n\t0x03, 0x72, 0x65, 0x71, 0x22, 0x2b, 0x0a, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x43, 0x68,\n\t0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49,\n\t0x64, 0x22, 0x97, 0x01, 0x0a, 0x09, 0x43, 0x68, 0x61, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,\n\t0x2e, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,\n\t0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,\n\t0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x64, 0x61, 0x74, 0x65, 0x12,\n\t0x23, 0x0a, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x5f, 0x6d, 0x73, 0x67, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65,\n\t0x72, 0x4d, 0x73, 0x67, 0x12, 0x2c, 0x0a, 0x09, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x73,\n\t0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x4d,\n\t0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x4d,\n\t0x73, 0x67, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xa0, 0x01, 0x0a, 0x11,\n\t0x43, 0x68, 0x61, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,\n\t0x65, 0x12, 0x35, 0x0a, 0x10, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65,\n\t0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x53, 0x65,\n\t0x73, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65,\n\t0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x68, 0x61, 0x74, 0x45, 0x6e, 0x74, 0x72,\n\t0x79, 0x48, 0x00, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x2c, 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73,\n\t0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32,\n\t0x05, 0x2e, 0x56, 0x6f, 0x69, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f,\n\t0x6e, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x06, 0x0a, 0x04, 0x72, 0x65, 0x73, 0x70, 0x22, 0x73,\n\t0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73,\n\t0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73,\n\t0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74,\n\t0x6f, 0x6d, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a,\n\t0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a,\n\t0x2e, 0x43, 0x68, 0x61, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x69, 0x73, 0x74,\n\t0x6f, 0x72, 0x79, 0x2a, 0x10, 0x0a, 0x04, 0x56, 0x6f, 0x69, 0x64, 0x12, 0x08, 0x0a, 0x04, 0x56,\n\t0x4f, 0x49, 0x44, 0x10, 0x00, 0x32, 0x82, 0x01, 0x0a, 0x07, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72,\n\t0x74, 0x12, 0x3f, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x74, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65,\n\t0x72, 0x12, 0x14, 0x2e, 0x43, 0x68, 0x61, 0x74, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x43, 0x68, 0x61, 0x74, 0x43, 0x75,\n\t0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01,\n\t0x30, 0x01, 0x12, 0x36, 0x0a, 0x09, 0x43, 0x68, 0x61, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12,\n\t0x11, 0x2e, 0x43, 0x68, 0x61, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,\n\t0x73, 0x74, 0x1a, 0x12, 0x2e, 0x43, 0x68, 0x61, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65,\n\t0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x3b,\n\t0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_support_proto_rawDescOnce sync.Once\n\tfile_support_proto_rawDescData = file_support_proto_rawDesc\n)\n\nfunc file_support_proto_rawDescGZIP() []byte {\n\tfile_support_proto_rawDescOnce.Do(func() {\n\t\tfile_support_proto_rawDescData = protoimpl.X.CompressGZIP(file_support_proto_rawDescData)\n\t})\n\treturn file_support_proto_rawDescData\n}\n\nvar file_support_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_support_proto_msgTypes = make([]protoimpl.MessageInfo, 9)\nvar file_support_proto_goTypes = []interface{}{\n\t(Void)(0),                     // 0: Void\n\t(*ChatCustomerRequest)(nil),   // 1: ChatCustomerRequest\n\t(*InitiateChat)(nil),          // 2: InitiateChat\n\t(*AgentMessage)(nil),          // 3: AgentMessage\n\t(*ChatCustomerResponse)(nil),  // 4: ChatCustomerResponse\n\t(*ChatAgentRequest)(nil),      // 5: ChatAgentRequest\n\t(*AcceptChat)(nil),            // 6: AcceptChat\n\t(*ChatEntry)(nil),             // 7: ChatEntry\n\t(*ChatAgentResponse)(nil),     // 8: ChatAgentResponse\n\t(*Session)(nil),               // 9: Session\n\t(*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp\n}\nvar file_support_proto_depIdxs = []int32{\n\t2,  // 0: ChatCustomerRequest.init:type_name -> InitiateChat\n\t0,  // 1: ChatCustomerRequest.hang_up:type_name -> Void\n\t9,  // 2: ChatCustomerResponse.session:type_name -> Session\n\t3,  // 3: ChatCustomerResponse.msg:type_name -> AgentMessage\n\t6,  // 4: ChatAgentRequest.accept:type_name -> AcceptChat\n\t0,  // 5: ChatAgentRequest.leave_session:type_name -> Void\n\t10, // 6: ChatEntry.date:type_name -> google.protobuf.Timestamp\n\t3,  // 7: ChatEntry.agent_msg:type_name -> AgentMessage\n\t9,  // 8: ChatAgentResponse.accepted_session:type_name -> Session\n\t7,  // 9: ChatAgentResponse.msg:type_name -> ChatEntry\n\t0,  // 10: ChatAgentResponse.session_ended:type_name -> Void\n\t7,  // 11: Session.history:type_name -> ChatEntry\n\t1,  // 12: Support.ChatCustomer:input_type -> ChatCustomerRequest\n\t5,  // 13: Support.ChatAgent:input_type -> ChatAgentRequest\n\t4,  // 14: Support.ChatCustomer:output_type -> ChatCustomerResponse\n\t8,  // 15: Support.ChatAgent:output_type -> ChatAgentResponse\n\t14, // [14:16] is the sub-list for method output_type\n\t12, // [12:14] is the sub-list for method input_type\n\t12, // [12:12] is the sub-list for extension type_name\n\t12, // [12:12] is the sub-list for extension extendee\n\t0,  // [0:12] is the sub-list for field type_name\n}\n\nfunc init() { file_support_proto_init() }\nfunc file_support_proto_init() {\n\tif File_support_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_support_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChatCustomerRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_support_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*InitiateChat); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_support_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AgentMessage); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_support_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChatCustomerResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_support_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChatAgentRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_support_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AcceptChat); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_support_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChatEntry); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_support_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChatAgentResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_support_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Session); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\tfile_support_proto_msgTypes[0].OneofWrappers = []interface{}{\n\t\t(*ChatCustomerRequest_Init)(nil),\n\t\t(*ChatCustomerRequest_Msg)(nil),\n\t\t(*ChatCustomerRequest_HangUp)(nil),\n\t}\n\tfile_support_proto_msgTypes[3].OneofWrappers = []interface{}{\n\t\t(*ChatCustomerResponse_Session)(nil),\n\t\t(*ChatCustomerResponse_Msg)(nil),\n\t}\n\tfile_support_proto_msgTypes[4].OneofWrappers = []interface{}{\n\t\t(*ChatAgentRequest_Accept)(nil),\n\t\t(*ChatAgentRequest_Msg)(nil),\n\t\t(*ChatAgentRequest_LeaveSession)(nil),\n\t}\n\tfile_support_proto_msgTypes[6].OneofWrappers = []interface{}{\n\t\t(*ChatEntry_CustomerMsg)(nil),\n\t\t(*ChatEntry_AgentMsg)(nil),\n\t}\n\tfile_support_proto_msgTypes[7].OneofWrappers = []interface{}{\n\t\t(*ChatAgentResponse_AcceptedSession)(nil),\n\t\t(*ChatAgentResponse_Msg)(nil),\n\t\t(*ChatAgentResponse_SessionEnded)(nil),\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_support_proto_rawDesc,\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   9,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_support_proto_goTypes,\n\t\tDependencyIndexes: file_support_proto_depIdxs,\n\t\tEnumInfos:         file_support_proto_enumTypes,\n\t\tMessageInfos:      file_support_proto_msgTypes,\n\t}.Build()\n\tFile_support_proto = out.File\n\tfile_support_proto_rawDesc = nil\n\tfile_support_proto_goTypes = nil\n\tfile_support_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/support.proto",
    "content": "syntax = \"proto3\";\n\noption go_package = \".;main\";\n\nimport \"google/protobuf/timestamp.proto\";\n\n// Support provides an interactive chat service, for customers to interact with\n// the bank's support agents. A single stream, for either of the two methods, is\n// a stateful connection to a single \"chat session\". Streams are initially disconnected\n// (not part of any session). A stream must be disconnected from a session (via customer\n// hang up or via agent leaving a session) before it can be connected to a new one.\nservice Support {\n    // ChatCustomer is used by a customer-facing app to send the customer's messages\n    // to a chat session. The customer is how initiates and terminates (via \"hangup\")\n    // a chat session. Only customers may invoke this method (e.g. requests must\n    // include customer auth credentials).\n    rpc ChatCustomer(stream ChatCustomerRequest) returns (stream ChatCustomerResponse);\n    // ChatAgent is used by an agent-facing app to allow an agent to reply to a\n    // customer's messages in a chat session. The agent may accept a chat session,\n    // which defaults to the session awaiting an agent for the longest period of time\n    // (FIFO queue).\n    rpc ChatAgent(stream ChatAgentRequest) returns (stream ChatAgentResponse);\n}\n\nenum Void {\n    VOID = 0;\n}\n\nmessage ChatCustomerRequest {\n    oneof req {\n        // init is used when a chat stream is not part of a\n        // chat session. This is a stream's initial state, as well as\n        // the state after a \"hang_up\" request is sent. This creates\n        // a new state session or resumes an existing one.\n        InitiateChat init = 1;\n        // msg is used to send the customer's messages to support\n        // agents.\n        string msg = 2;\n        // hang_up is used to terminate a chat session. If a stream\n        // is broken, but the session was not terminated, the client\n        // may initiate a new stream and use init to resume that\n        // session. Sessions are not terminated unless done so\n        // explicitly via sending this kind of request on the stream.\n        Void hang_up = 3;\n    }\n}\n\nmessage InitiateChat {\n    string resume_session_id = 1;\n}\n\nmessage AgentMessage {\n    string agent_name = 1;\n    string msg = 2;\n}\n\nmessage ChatCustomerResponse {\n    oneof resp {\n        // session is sent from the server when the stream is connected\n        // to a chat session. This happens after an init request is sent\n        // and the stream is connected to either a new or resumed session.\n        Session session = 1;\n        // msg is sent from the server to convey agents' messages to the\n        // customer.\n        AgentMessage msg = 2;\n    }\n}\n\nmessage ChatAgentRequest {\n    oneof req {\n        // accept is used when an agent wants to join a customer chat\n        // session. It can be used to connect to a specific session (by\n        // ID), or to just accept the session for which the customer has\n        // been waiting the longest (e.g. poll a FIFO queue of sessions\n        // awaiting a support agent). It is possible for multiple agents\n        // to be connected to the same chat session.\n        AcceptChat accept = 1;\n        // msg is used to send a message to the customer. It will also be\n        // delivered to any other connected support agents.\n        string msg = 2;\n        // leave_session allows an agent to exit a chat session. They can\n        // always re-enter later by sending an accept message for that\n        // session ID.\n        Void leave_session = 3;\n    }\n}\n\nmessage AcceptChat {\n    string session_id = 1;\n}\n\nmessage ChatEntry {\n    google.protobuf.Timestamp date = 1;\n    oneof entry {\n        string customer_msg = 2;\n        AgentMessage agent_msg = 3;\n    }\n}\n\nmessage ChatAgentResponse {\n    oneof resp {\n        // accepted_session provides the detail of a chat session. The server\n        // sends this message after the agent has accepted a chat session.\n        Session accepted_session = 1;\n        // msg is sent by the server when the customer, or another support\n        // agent, sends a message in stream's current session.\n        ChatEntry msg = 2;\n        // session_ended notifies the support agent that their currently\n        // connected chat session has been terminated by the customer.\n        Void session_ended = 3;\n    }\n}\n\nmessage Session {\n    string session_id = 1;\n    string customer_name = 2;\n    repeated ChatEntry history = 3;\n}\n"
  },
  {
    "path": "internal/testing/cmd/bankdemo/support_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage main\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.32.0 or later.\nconst _ = grpc.SupportPackageIsVersion7\n\n// SupportClient is the client API for Support service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype SupportClient interface {\n\t// ChatCustomer is used by a customer-facing app to send the customer's messages\n\t// to a chat session. The customer is how initiates and terminates (via \"hangup\")\n\t// a chat session. Only customers may invoke this method (e.g. requests must\n\t// include customer auth credentials).\n\tChatCustomer(ctx context.Context, opts ...grpc.CallOption) (Support_ChatCustomerClient, error)\n\t// ChatAgent is used by an agent-facing app to allow an agent to reply to a\n\t// customer's messages in a chat session. The agent may accept a chat session,\n\t// which defaults to the session awaiting an agent for the longest period of time\n\t// (FIFO queue).\n\tChatAgent(ctx context.Context, opts ...grpc.CallOption) (Support_ChatAgentClient, error)\n}\n\ntype supportClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewSupportClient(cc grpc.ClientConnInterface) SupportClient {\n\treturn &supportClient{cc}\n}\n\nfunc (c *supportClient) ChatCustomer(ctx context.Context, opts ...grpc.CallOption) (Support_ChatCustomerClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &Support_ServiceDesc.Streams[0], \"/Support/ChatCustomer\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &supportChatCustomerClient{stream}\n\treturn x, nil\n}\n\ntype Support_ChatCustomerClient interface {\n\tSend(*ChatCustomerRequest) error\n\tRecv() (*ChatCustomerResponse, error)\n\tgrpc.ClientStream\n}\n\ntype supportChatCustomerClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *supportChatCustomerClient) Send(m *ChatCustomerRequest) error {\n\treturn x.ClientStream.SendMsg(m)\n}\n\nfunc (x *supportChatCustomerClient) Recv() (*ChatCustomerResponse, error) {\n\tm := new(ChatCustomerResponse)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc (c *supportClient) ChatAgent(ctx context.Context, opts ...grpc.CallOption) (Support_ChatAgentClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &Support_ServiceDesc.Streams[1], \"/Support/ChatAgent\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &supportChatAgentClient{stream}\n\treturn x, nil\n}\n\ntype Support_ChatAgentClient interface {\n\tSend(*ChatAgentRequest) error\n\tRecv() (*ChatAgentResponse, error)\n\tgrpc.ClientStream\n}\n\ntype supportChatAgentClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *supportChatAgentClient) Send(m *ChatAgentRequest) error {\n\treturn x.ClientStream.SendMsg(m)\n}\n\nfunc (x *supportChatAgentClient) Recv() (*ChatAgentResponse, error) {\n\tm := new(ChatAgentResponse)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\n// SupportServer is the server API for Support service.\n// All implementations must embed UnimplementedSupportServer\n// for forward compatibility\ntype SupportServer interface {\n\t// ChatCustomer is used by a customer-facing app to send the customer's messages\n\t// to a chat session. The customer is how initiates and terminates (via \"hangup\")\n\t// a chat session. Only customers may invoke this method (e.g. requests must\n\t// include customer auth credentials).\n\tChatCustomer(Support_ChatCustomerServer) error\n\t// ChatAgent is used by an agent-facing app to allow an agent to reply to a\n\t// customer's messages in a chat session. The agent may accept a chat session,\n\t// which defaults to the session awaiting an agent for the longest period of time\n\t// (FIFO queue).\n\tChatAgent(Support_ChatAgentServer) error\n\tmustEmbedUnimplementedSupportServer()\n}\n\n// UnimplementedSupportServer must be embedded to have forward compatible implementations.\ntype UnimplementedSupportServer struct {\n}\n\nfunc (UnimplementedSupportServer) ChatCustomer(Support_ChatCustomerServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method ChatCustomer not implemented\")\n}\nfunc (UnimplementedSupportServer) ChatAgent(Support_ChatAgentServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method ChatAgent not implemented\")\n}\nfunc (UnimplementedSupportServer) mustEmbedUnimplementedSupportServer() {}\n\n// UnsafeSupportServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to SupportServer will\n// result in compilation errors.\ntype UnsafeSupportServer interface {\n\tmustEmbedUnimplementedSupportServer()\n}\n\nfunc RegisterSupportServer(s grpc.ServiceRegistrar, srv SupportServer) {\n\ts.RegisterService(&Support_ServiceDesc, srv)\n}\n\nfunc _Support_ChatCustomer_Handler(srv interface{}, stream grpc.ServerStream) error {\n\treturn srv.(SupportServer).ChatCustomer(&supportChatCustomerServer{stream})\n}\n\ntype Support_ChatCustomerServer interface {\n\tSend(*ChatCustomerResponse) error\n\tRecv() (*ChatCustomerRequest, error)\n\tgrpc.ServerStream\n}\n\ntype supportChatCustomerServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *supportChatCustomerServer) Send(m *ChatCustomerResponse) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc (x *supportChatCustomerServer) Recv() (*ChatCustomerRequest, error) {\n\tm := new(ChatCustomerRequest)\n\tif err := x.ServerStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc _Support_ChatAgent_Handler(srv interface{}, stream grpc.ServerStream) error {\n\treturn srv.(SupportServer).ChatAgent(&supportChatAgentServer{stream})\n}\n\ntype Support_ChatAgentServer interface {\n\tSend(*ChatAgentResponse) error\n\tRecv() (*ChatAgentRequest, error)\n\tgrpc.ServerStream\n}\n\ntype supportChatAgentServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *supportChatAgentServer) Send(m *ChatAgentResponse) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc (x *supportChatAgentServer) Recv() (*ChatAgentRequest, error) {\n\tm := new(ChatAgentRequest)\n\tif err := x.ServerStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\n// Support_ServiceDesc is the grpc.ServiceDesc for Support service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar Support_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"Support\",\n\tHandlerType: (*SupportServer)(nil),\n\tMethods:     []grpc.MethodDesc{},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"ChatCustomer\",\n\t\t\tHandler:       _Support_ChatCustomer_Handler,\n\t\t\tServerStreams: true,\n\t\t\tClientStreams: true,\n\t\t},\n\t\t{\n\t\t\tStreamName:    \"ChatAgent\",\n\t\t\tHandler:       _Support_ChatAgent_Handler,\n\t\t\tServerStreams: true,\n\t\t\tClientStreams: true,\n\t\t},\n\t},\n\tMetadata: \"support.proto\",\n}\n"
  },
  {
    "path": "internal/testing/cmd/testserver/README.md",
    "content": "# testserver\n\nThe `testserver` program is a simple server that can be used for testing RPC clients such\nas `grpcurl`. It implements an RPC interface that is defined in `grpcurl`'s [testing package](https://github.com/fullstorydev/grpcurl/blob/master/testing/example.proto) and also exposes [the implementation](https://godoc.org/github.com/fullstorydev/grpcurl/testing#TestServer) that is defined in that same package. This is the same test interface and implementation that is used in unit tests for `grpcurl`.\n\nFor a possibly more interesting test server, take a look at `bankdemo`, which is a demo gRPC app that provides a more concrete RPC interface, including full-duplex bidirectional streaming methods, plus an example implementation."
  },
  {
    "path": "internal/testing/cmd/testserver/testserver.go",
    "content": "// Command testserver spins up a test GRPC server.\npackage main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/grpclog\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/reflection\"\n\t\"google.golang.org/grpc/status\"\n\n\t\"github.com/fullstorydev/grpcurl\"\n\tgrpcurl_testing \"github.com/fullstorydev/grpcurl/internal/testing\"\n)\n\nvar (\n\tgetUnixSocket func() string // nil when run on non-unix platforms\n\n\thelp   = flag.Bool(\"help\", false, \"Print usage instructions and exit.\")\n\tcacert = flag.String(\"cacert\", \"\",\n\t\t`File containing trusted root certificates for verifying  client certs. Ignored\n    \tif TLS is not in use (e.g. no -cert or -key specified).`)\n\tcert = flag.String(\"cert\", \"\",\n\t\t`File containing server certificate (public key). Must also provide -key option.\n    \tServer uses plain-text if no -cert and -key options are given.`)\n\tkey = flag.String(\"key\", \"\",\n\t\t`File containing server private key. Must also provide -cert option. Server uses\n    \tplain-text if no -cert and -key options are given.`)\n\trequirecert = flag.Bool(\"requirecert\", false,\n\t\t`Require clients to authenticate via client certs. Must be using TLS (e.g. must\n    \talso provide -cert and -key options).`)\n\tport      = flag.Int(\"p\", 0, \"Port on which to listen. Ephemeral port used if not specified.\")\n\tnoreflect = flag.Bool(\"noreflect\", false, \"Indicates that server should not support server reflection.\")\n\tquiet     = flag.Bool(\"q\", false, \"Suppresses server request and stream logging.\")\n)\n\nfunc main() {\n\tflag.Parse()\n\n\tif *help {\n\t\tflag.PrintDefaults()\n\t\tos.Exit(0)\n\t}\n\n\tgrpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stdout, os.Stderr))\n\n\tif len(flag.Args()) > 0 {\n\t\tfmt.Fprintln(os.Stderr, \"No arguments expected.\")\n\t\tos.Exit(2)\n\t}\n\tif (*cert == \"\") != (*key == \"\") {\n\t\tfmt.Fprintln(os.Stderr, \"The -cert and -key arguments must be used together and both be present.\")\n\t\tos.Exit(2)\n\t}\n\tif *requirecert && *cert == \"\" {\n\t\tfmt.Fprintln(os.Stderr, \"The -requirecert arg cannot be used without -cert and -key arguments.\")\n\t\tos.Exit(2)\n\t}\n\n\tvar opts []grpc.ServerOption\n\tif *cert != \"\" {\n\t\tcreds, err := grpcurl.ServerTransportCredentials(*cacert, *cert, *key, *requirecert)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Failed to configure transport credentials: %v\\n\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\topts = []grpc.ServerOption{grpc.Creds(creds)}\n\t}\n\tif !*quiet {\n\t\topts = append(opts, grpc.UnaryInterceptor(unaryLogger), grpc.StreamInterceptor(streamLogger))\n\t}\n\n\tvar network, addr string\n\tif getUnixSocket != nil && getUnixSocket() != \"\" {\n\t\tnetwork = \"unix\"\n\t\taddr = getUnixSocket()\n\t} else {\n\t\tnetwork = \"tcp\"\n\t\taddr = fmt.Sprintf(\"127.0.0.1:%d\", *port)\n\t}\n\tl, err := net.Listen(network, addr)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"Failed to listen on socket: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tfmt.Printf(\"Listening on %v\\n\", l.Addr())\n\n\tsvr := grpc.NewServer(opts...)\n\n\tgrpcurl_testing.RegisterTestServiceServer(svr, grpcurl_testing.TestServer{})\n\tif !*noreflect {\n\t\treflection.Register(svr)\n\t}\n\n\tif err := svr.Serve(l); err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"GRPC server returned error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n}\n\nvar id int32\n\nfunc unaryLogger(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {\n\ti := atomic.AddInt32(&id, 1) - 1\n\tgrpclog.Infof(\"start <%d>: %s\\n\", i, info.FullMethod)\n\tstart := time.Now()\n\trsp, err := handler(ctx, req)\n\tvar code codes.Code\n\tif stat, ok := status.FromError(err); ok {\n\t\tcode = stat.Code()\n\t} else {\n\t\tcode = codes.Unknown\n\t}\n\tgrpclog.Infof(\"completed <%d>: %v (%d) %v\\n\", i, code, code, time.Since(start))\n\treturn rsp, err\n}\n\nfunc streamLogger(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {\n\ti := atomic.AddInt32(&id, 1) - 1\n\tstart := time.Now()\n\tgrpclog.Infof(\"start <%d>: %s\\n\", i, info.FullMethod)\n\terr := handler(srv, loggingStream{ss: ss, id: i})\n\tvar code codes.Code\n\tif stat, ok := status.FromError(err); ok {\n\t\tcode = stat.Code()\n\t} else {\n\t\tcode = codes.Unknown\n\t}\n\tgrpclog.Infof(\"completed <%d>: %v(%d) %v\\n\", i, code, code, time.Since(start))\n\treturn err\n}\n\ntype loggingStream struct {\n\tss grpc.ServerStream\n\tid int32\n}\n\nfunc (l loggingStream) SetHeader(md metadata.MD) error {\n\treturn l.ss.SetHeader(md)\n}\n\nfunc (l loggingStream) SendHeader(md metadata.MD) error {\n\treturn l.ss.SendHeader(md)\n}\n\nfunc (l loggingStream) SetTrailer(md metadata.MD) {\n\tl.ss.SetTrailer(md)\n}\n\nfunc (l loggingStream) Context() context.Context {\n\treturn l.ss.Context()\n}\n\nfunc (l loggingStream) SendMsg(m interface{}) error {\n\terr := l.ss.SendMsg(m)\n\tif err == nil {\n\t\tgrpclog.Infof(\"stream <%d>: sent message\\n\", l.id)\n\t}\n\treturn err\n}\n\nfunc (l loggingStream) RecvMsg(m interface{}) error {\n\terr := l.ss.RecvMsg(m)\n\tif err == nil {\n\t\tgrpclog.Infof(\"stream <%d>: received message\\n\", l.id)\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "internal/testing/cmd/testserver/unix.go",
    "content": "//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris\n// +build darwin dragonfly freebsd linux netbsd openbsd solaris\n\npackage main\n\nimport \"flag\"\n\nvar (\n\tunix = flag.String(\"unix\", \"\",\n\t\t`Use instead of -p to indicate listening on a Unix domain socket instead of a\n    \tTCP port. If present, must be the path to a domain socket.`)\n)\n\nfunc init() {\n\tgetUnixSocket = func() string {\n\t\treturn *unix\n\t}\n}\n"
  },
  {
    "path": "internal/testing/example.proto",
    "content": "syntax = \"proto3\";\n\nimport \"google/protobuf/descriptor.proto\";\nimport \"google/protobuf/empty.proto\";\nimport \"google/protobuf/timestamp.proto\";\nimport \"example2.proto\";\n\nmessage TestRequest {\n\trepeated string file_names = 1;\n\trepeated Extension extensions = 2;\n}\n\nmessage TestResponse {\n\tmap<string, google.protobuf.FileDescriptorProto> file_protos = 1;\n\tgoogle.protobuf.Timestamp last_update_date = 2;\n}\n\nservice TestService {\n\trpc GetFiles (TestRequest) returns (TestResponse);\n\trpc Ping (google.protobuf.Empty) returns (google.protobuf.Empty);\n}"
  },
  {
    "path": "internal/testing/example2.proto",
    "content": "syntax = \"proto3\";\n\nimport \"google/protobuf/any.proto\";\n\nmessage Extension {\n    uint64 id = 1;\n    google.protobuf.Any data = 2;\n}\n"
  },
  {
    "path": "internal/testing/jsonpb_test_proto/test_objects.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.14.0\n// source: test_objects.proto\n\npackage jsonpb\n\nimport (\n\tproto \"github.com/golang/protobuf/proto\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\tanypb \"google.golang.org/protobuf/types/known/anypb\"\n\tdurationpb \"google.golang.org/protobuf/types/known/durationpb\"\n\tstructpb \"google.golang.org/protobuf/types/known/structpb\"\n\ttimestamppb \"google.golang.org/protobuf/types/known/timestamppb\"\n\twrapperspb \"google.golang.org/protobuf/types/known/wrapperspb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// This is a compile-time assertion that a sufficiently up-to-date version\n// of the legacy proto package is being used.\nconst _ = proto.ProtoPackageIsVersion4\n\ntype KnownTypes struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAn    *anypb.Any              `protobuf:\"bytes,14,opt,name=an\" json:\"an,omitempty\"`\n\tDur   *durationpb.Duration    `protobuf:\"bytes,1,opt,name=dur\" json:\"dur,omitempty\"`\n\tSt    *structpb.Struct        `protobuf:\"bytes,12,opt,name=st\" json:\"st,omitempty\"`\n\tTs    *timestamppb.Timestamp  `protobuf:\"bytes,2,opt,name=ts\" json:\"ts,omitempty\"`\n\tLv    *structpb.ListValue     `protobuf:\"bytes,15,opt,name=lv\" json:\"lv,omitempty\"`\n\tVal   *structpb.Value         `protobuf:\"bytes,16,opt,name=val\" json:\"val,omitempty\"`\n\tDbl   *wrapperspb.DoubleValue `protobuf:\"bytes,3,opt,name=dbl\" json:\"dbl,omitempty\"`\n\tFlt   *wrapperspb.FloatValue  `protobuf:\"bytes,4,opt,name=flt\" json:\"flt,omitempty\"`\n\tI64   *wrapperspb.Int64Value  `protobuf:\"bytes,5,opt,name=i64\" json:\"i64,omitempty\"`\n\tU64   *wrapperspb.UInt64Value `protobuf:\"bytes,6,opt,name=u64\" json:\"u64,omitempty\"`\n\tI32   *wrapperspb.Int32Value  `protobuf:\"bytes,7,opt,name=i32\" json:\"i32,omitempty\"`\n\tU32   *wrapperspb.UInt32Value `protobuf:\"bytes,8,opt,name=u32\" json:\"u32,omitempty\"`\n\tBool  *wrapperspb.BoolValue   `protobuf:\"bytes,9,opt,name=bool\" json:\"bool,omitempty\"`\n\tStr   *wrapperspb.StringValue `protobuf:\"bytes,10,opt,name=str\" json:\"str,omitempty\"`\n\tBytes *wrapperspb.BytesValue  `protobuf:\"bytes,11,opt,name=bytes\" json:\"bytes,omitempty\"`\n}\n\nfunc (x *KnownTypes) Reset() {\n\t*x = KnownTypes{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_objects_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *KnownTypes) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*KnownTypes) ProtoMessage() {}\n\nfunc (x *KnownTypes) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_objects_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use KnownTypes.ProtoReflect.Descriptor instead.\nfunc (*KnownTypes) Descriptor() ([]byte, []int) {\n\treturn file_test_objects_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *KnownTypes) GetAn() *anypb.Any {\n\tif x != nil {\n\t\treturn x.An\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetDur() *durationpb.Duration {\n\tif x != nil {\n\t\treturn x.Dur\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetSt() *structpb.Struct {\n\tif x != nil {\n\t\treturn x.St\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetTs() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.Ts\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetLv() *structpb.ListValue {\n\tif x != nil {\n\t\treturn x.Lv\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetVal() *structpb.Value {\n\tif x != nil {\n\t\treturn x.Val\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetDbl() *wrapperspb.DoubleValue {\n\tif x != nil {\n\t\treturn x.Dbl\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetFlt() *wrapperspb.FloatValue {\n\tif x != nil {\n\t\treturn x.Flt\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetI64() *wrapperspb.Int64Value {\n\tif x != nil {\n\t\treturn x.I64\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetU64() *wrapperspb.UInt64Value {\n\tif x != nil {\n\t\treturn x.U64\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetI32() *wrapperspb.Int32Value {\n\tif x != nil {\n\t\treturn x.I32\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetU32() *wrapperspb.UInt32Value {\n\tif x != nil {\n\t\treturn x.U32\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetBool() *wrapperspb.BoolValue {\n\tif x != nil {\n\t\treturn x.Bool\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetStr() *wrapperspb.StringValue {\n\tif x != nil {\n\t\treturn x.Str\n\t}\n\treturn nil\n}\n\nfunc (x *KnownTypes) GetBytes() *wrapperspb.BytesValue {\n\tif x != nil {\n\t\treturn x.Bytes\n\t}\n\treturn nil\n}\n\nvar File_test_objects_proto protoreflect.FileDescriptor\n\nvar file_test_objects_proto_rawDesc = []byte{\n\t0x0a, 0x12, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x62, 0x1a, 0x19, 0x67, 0x6f,\n\t0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e,\n\t0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,\n\t0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xba, 0x05, 0x0a, 0x0a, 0x4b, 0x6e, 0x6f, 0x77, 0x6e,\n\t0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x02, 0x61, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x02, 0x61, 0x6e, 0x12, 0x2b, 0x0a, 0x03, 0x64,\n\t0x75, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,\n\t0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x52, 0x03, 0x64, 0x75, 0x72, 0x12, 0x27, 0x0a, 0x02, 0x73, 0x74, 0x18, 0x0c,\n\t0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x02, 0x73,\n\t0x74, 0x12, 0x2a, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,\n\t0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,\n\t0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x02, 0x74, 0x73, 0x12, 0x2a, 0x0a,\n\t0x02, 0x6c, 0x76, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74,\n\t0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x02, 0x6c, 0x76, 0x12, 0x28, 0x0a, 0x03, 0x76, 0x61, 0x6c,\n\t0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03,\n\t0x76, 0x61, 0x6c, 0x12, 0x2e, 0x0a, 0x03, 0x64, 0x62, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,\n\t0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,\n\t0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03,\n\t0x64, 0x62, 0x6c, 0x12, 0x2d, 0x0a, 0x03, 0x66, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,\n\t0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,\n\t0x75, 0x66, 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x66,\n\t0x6c, 0x74, 0x12, 0x2d, 0x0a, 0x03, 0x69, 0x36, 0x34, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,\n\t0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,\n\t0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x69, 0x36,\n\t0x34, 0x12, 0x2e, 0x0a, 0x03, 0x75, 0x36, 0x34, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,\n\t0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,\n\t0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x75, 0x36,\n\t0x34, 0x12, 0x2d, 0x0a, 0x03, 0x69, 0x33, 0x32, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b,\n\t0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,\n\t0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x69, 0x33, 0x32,\n\t0x12, 0x2e, 0x0a, 0x03, 0x75, 0x33, 0x32, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,\n\t0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,\n\t0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x75, 0x33, 0x32,\n\t0x12, 0x2e, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,\n\t0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,\n\t0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x6f, 0x6c,\n\t0x12, 0x2e, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,\n\t0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,\n\t0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x73, 0x74, 0x72,\n\t0x12, 0x31, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32,\n\t0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,\n\t0x66, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x62, 0x79,\n\t0x74, 0x65, 0x73, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x3b, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x62,\n}\n\nvar (\n\tfile_test_objects_proto_rawDescOnce sync.Once\n\tfile_test_objects_proto_rawDescData = file_test_objects_proto_rawDesc\n)\n\nfunc file_test_objects_proto_rawDescGZIP() []byte {\n\tfile_test_objects_proto_rawDescOnce.Do(func() {\n\t\tfile_test_objects_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_objects_proto_rawDescData)\n\t})\n\treturn file_test_objects_proto_rawDescData\n}\n\nvar file_test_objects_proto_msgTypes = make([]protoimpl.MessageInfo, 1)\nvar file_test_objects_proto_goTypes = []interface{}{\n\t(*KnownTypes)(nil),             // 0: jsonpb.KnownTypes\n\t(*anypb.Any)(nil),              // 1: google.protobuf.Any\n\t(*durationpb.Duration)(nil),    // 2: google.protobuf.Duration\n\t(*structpb.Struct)(nil),        // 3: google.protobuf.Struct\n\t(*timestamppb.Timestamp)(nil),  // 4: google.protobuf.Timestamp\n\t(*structpb.ListValue)(nil),     // 5: google.protobuf.ListValue\n\t(*structpb.Value)(nil),         // 6: google.protobuf.Value\n\t(*wrapperspb.DoubleValue)(nil), // 7: google.protobuf.DoubleValue\n\t(*wrapperspb.FloatValue)(nil),  // 8: google.protobuf.FloatValue\n\t(*wrapperspb.Int64Value)(nil),  // 9: google.protobuf.Int64Value\n\t(*wrapperspb.UInt64Value)(nil), // 10: google.protobuf.UInt64Value\n\t(*wrapperspb.Int32Value)(nil),  // 11: google.protobuf.Int32Value\n\t(*wrapperspb.UInt32Value)(nil), // 12: google.protobuf.UInt32Value\n\t(*wrapperspb.BoolValue)(nil),   // 13: google.protobuf.BoolValue\n\t(*wrapperspb.StringValue)(nil), // 14: google.protobuf.StringValue\n\t(*wrapperspb.BytesValue)(nil),  // 15: google.protobuf.BytesValue\n}\nvar file_test_objects_proto_depIdxs = []int32{\n\t1,  // 0: jsonpb.KnownTypes.an:type_name -> google.protobuf.Any\n\t2,  // 1: jsonpb.KnownTypes.dur:type_name -> google.protobuf.Duration\n\t3,  // 2: jsonpb.KnownTypes.st:type_name -> google.protobuf.Struct\n\t4,  // 3: jsonpb.KnownTypes.ts:type_name -> google.protobuf.Timestamp\n\t5,  // 4: jsonpb.KnownTypes.lv:type_name -> google.protobuf.ListValue\n\t6,  // 5: jsonpb.KnownTypes.val:type_name -> google.protobuf.Value\n\t7,  // 6: jsonpb.KnownTypes.dbl:type_name -> google.protobuf.DoubleValue\n\t8,  // 7: jsonpb.KnownTypes.flt:type_name -> google.protobuf.FloatValue\n\t9,  // 8: jsonpb.KnownTypes.i64:type_name -> google.protobuf.Int64Value\n\t10, // 9: jsonpb.KnownTypes.u64:type_name -> google.protobuf.UInt64Value\n\t11, // 10: jsonpb.KnownTypes.i32:type_name -> google.protobuf.Int32Value\n\t12, // 11: jsonpb.KnownTypes.u32:type_name -> google.protobuf.UInt32Value\n\t13, // 12: jsonpb.KnownTypes.bool:type_name -> google.protobuf.BoolValue\n\t14, // 13: jsonpb.KnownTypes.str:type_name -> google.protobuf.StringValue\n\t15, // 14: jsonpb.KnownTypes.bytes:type_name -> google.protobuf.BytesValue\n\t15, // [15:15] is the sub-list for method output_type\n\t15, // [15:15] is the sub-list for method input_type\n\t15, // [15:15] is the sub-list for extension type_name\n\t15, // [15:15] is the sub-list for extension extendee\n\t0,  // [0:15] is the sub-list for field type_name\n}\n\nfunc init() { file_test_objects_proto_init() }\nfunc file_test_objects_proto_init() {\n\tif File_test_objects_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_test_objects_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*KnownTypes); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_test_objects_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   1,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_test_objects_proto_goTypes,\n\t\tDependencyIndexes: file_test_objects_proto_depIdxs,\n\t\tMessageInfos:      file_test_objects_proto_msgTypes,\n\t}.Build()\n\tFile_test_objects_proto = out.File\n\tfile_test_objects_proto_rawDesc = nil\n\tfile_test_objects_proto_goTypes = nil\n\tfile_test_objects_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "internal/testing/jsonpb_test_proto/test_objects.proto",
    "content": "syntax = \"proto2\";\n\nimport \"google/protobuf/any.proto\";\nimport \"google/protobuf/duration.proto\";\nimport \"google/protobuf/struct.proto\";\nimport \"google/protobuf/timestamp.proto\";\nimport \"google/protobuf/wrappers.proto\";\n\npackage jsonpb;\n\noption go_package=\".;jsonpb\";\n\nmessage KnownTypes {\n  optional google.protobuf.Any an = 14;\n  optional google.protobuf.Duration dur = 1;\n  optional google.protobuf.Struct st = 12;\n  optional google.protobuf.Timestamp ts = 2;\n  optional google.protobuf.ListValue lv = 15;\n  optional google.protobuf.Value val = 16;\n\n  optional google.protobuf.DoubleValue dbl = 3;\n  optional google.protobuf.FloatValue flt = 4;\n  optional google.protobuf.Int64Value i64 = 5;\n  optional google.protobuf.UInt64Value u64 = 6;\n  optional google.protobuf.Int32Value i32 = 7;\n  optional google.protobuf.UInt32Value u32 = 8;\n  optional google.protobuf.BoolValue bool = 9;\n  optional google.protobuf.StringValue str = 10;\n  optional google.protobuf.BytesValue bytes = 11;\n}\n"
  },
  {
    "path": "internal/testing/test.pb.go",
    "content": "// NB: Copied from the gRPC Go repo: google.golang.org/grpc/interop/grpc_testing/test.proto\n\n// Copyright 2017 gRPC authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// 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, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// An integration test service that covers all the method signature permutations\n// of unary/streaming requests/responses.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v4.22.0\n// source: test.proto\n\npackage testing\n\nimport (\n\tproto \"github.com/golang/protobuf/proto\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// This is a compile-time assertion that a sufficiently up-to-date version\n// of the legacy proto package is being used.\nconst _ = proto.ProtoPackageIsVersion4\n\n// The type of payload that should be returned.\ntype PayloadType int32\n\nconst (\n\t// Compressable text format.\n\tPayloadType_COMPRESSABLE PayloadType = 0\n\t// Uncompressable binary format.\n\tPayloadType_UNCOMPRESSABLE PayloadType = 1\n\t// Randomly chosen from all other formats defined in this enum.\n\tPayloadType_RANDOM PayloadType = 2\n)\n\n// Enum value maps for PayloadType.\nvar (\n\tPayloadType_name = map[int32]string{\n\t\t0: \"COMPRESSABLE\",\n\t\t1: \"UNCOMPRESSABLE\",\n\t\t2: \"RANDOM\",\n\t}\n\tPayloadType_value = map[string]int32{\n\t\t\"COMPRESSABLE\":   0,\n\t\t\"UNCOMPRESSABLE\": 1,\n\t\t\"RANDOM\":         2,\n\t}\n)\n\nfunc (x PayloadType) Enum() *PayloadType {\n\tp := new(PayloadType)\n\t*p = x\n\treturn p\n}\n\nfunc (x PayloadType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (PayloadType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_test_proto_enumTypes[0].Descriptor()\n}\n\nfunc (PayloadType) Type() protoreflect.EnumType {\n\treturn &file_test_proto_enumTypes[0]\n}\n\nfunc (x PayloadType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use PayloadType.Descriptor instead.\nfunc (PayloadType) EnumDescriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{0}\n}\n\ntype Empty struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *Empty) Reset() {\n\t*x = Empty{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Empty) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Empty) ProtoMessage() {}\n\nfunc (x *Empty) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Empty.ProtoReflect.Descriptor instead.\nfunc (*Empty) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{0}\n}\n\n// A block of data, to simply increase gRPC message size.\ntype Payload struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The type of data in body.\n\tType PayloadType `protobuf:\"varint,1,opt,name=type,proto3,enum=testing.PayloadType\" json:\"type,omitempty\"`\n\t// Primary contents of payload.\n\tBody []byte `protobuf:\"bytes,2,opt,name=body,proto3\" json:\"body,omitempty\"`\n}\n\nfunc (x *Payload) Reset() {\n\t*x = Payload{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Payload) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Payload) ProtoMessage() {}\n\nfunc (x *Payload) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Payload.ProtoReflect.Descriptor instead.\nfunc (*Payload) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Payload) GetType() PayloadType {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn PayloadType_COMPRESSABLE\n}\n\nfunc (x *Payload) GetBody() []byte {\n\tif x != nil {\n\t\treturn x.Body\n\t}\n\treturn nil\n}\n\n// A protobuf representation for grpc status. This is used by test\n// clients to specify a status that the server should attempt to return.\ntype EchoStatus struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCode    int32  `protobuf:\"varint,1,opt,name=code,proto3\" json:\"code,omitempty\"`\n\tMessage string `protobuf:\"bytes,2,opt,name=message,proto3\" json:\"message,omitempty\"`\n}\n\nfunc (x *EchoStatus) Reset() {\n\t*x = EchoStatus{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *EchoStatus) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*EchoStatus) ProtoMessage() {}\n\nfunc (x *EchoStatus) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use EchoStatus.ProtoReflect.Descriptor instead.\nfunc (*EchoStatus) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *EchoStatus) GetCode() int32 {\n\tif x != nil {\n\t\treturn x.Code\n\t}\n\treturn 0\n}\n\nfunc (x *EchoStatus) GetMessage() string {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn \"\"\n}\n\n// Unary request.\ntype SimpleRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Desired payload type in the response from the server.\n\t// If response_type is RANDOM, server randomly chooses one from other formats.\n\tResponseType PayloadType `protobuf:\"varint,1,opt,name=response_type,json=responseType,proto3,enum=testing.PayloadType\" json:\"response_type,omitempty\"`\n\t// Desired payload size in the response from the server.\n\t// If response_type is COMPRESSABLE, this denotes the size before compression.\n\tResponseSize int32 `protobuf:\"varint,2,opt,name=response_size,json=responseSize,proto3\" json:\"response_size,omitempty\"`\n\t// Optional input payload sent along with the request.\n\tPayload *Payload `protobuf:\"bytes,3,opt,name=payload,proto3\" json:\"payload,omitempty\"`\n\t// Whether SimpleResponse should include username.\n\tFillUsername bool `protobuf:\"varint,4,opt,name=fill_username,json=fillUsername,proto3\" json:\"fill_username,omitempty\"`\n\t// Whether SimpleResponse should include OAuth scope.\n\tFillOauthScope bool `protobuf:\"varint,5,opt,name=fill_oauth_scope,json=fillOauthScope,proto3\" json:\"fill_oauth_scope,omitempty\"`\n\t// Whether server should return a given status\n\tResponseStatus *EchoStatus `protobuf:\"bytes,7,opt,name=response_status,json=responseStatus,proto3\" json:\"response_status,omitempty\"`\n}\n\nfunc (x *SimpleRequest) Reset() {\n\t*x = SimpleRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SimpleRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SimpleRequest) ProtoMessage() {}\n\nfunc (x *SimpleRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[3]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SimpleRequest.ProtoReflect.Descriptor instead.\nfunc (*SimpleRequest) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *SimpleRequest) GetResponseType() PayloadType {\n\tif x != nil {\n\t\treturn x.ResponseType\n\t}\n\treturn PayloadType_COMPRESSABLE\n}\n\nfunc (x *SimpleRequest) GetResponseSize() int32 {\n\tif x != nil {\n\t\treturn x.ResponseSize\n\t}\n\treturn 0\n}\n\nfunc (x *SimpleRequest) GetPayload() *Payload {\n\tif x != nil {\n\t\treturn x.Payload\n\t}\n\treturn nil\n}\n\nfunc (x *SimpleRequest) GetFillUsername() bool {\n\tif x != nil {\n\t\treturn x.FillUsername\n\t}\n\treturn false\n}\n\nfunc (x *SimpleRequest) GetFillOauthScope() bool {\n\tif x != nil {\n\t\treturn x.FillOauthScope\n\t}\n\treturn false\n}\n\nfunc (x *SimpleRequest) GetResponseStatus() *EchoStatus {\n\tif x != nil {\n\t\treturn x.ResponseStatus\n\t}\n\treturn nil\n}\n\n// Unary response, as configured by the request.\ntype SimpleResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Payload to increase message size.\n\tPayload *Payload `protobuf:\"bytes,1,opt,name=payload,proto3\" json:\"payload,omitempty\"`\n\t// The user the request came from, for verifying authentication was\n\t// successful when the client expected it.\n\tUsername string `protobuf:\"bytes,2,opt,name=username,proto3\" json:\"username,omitempty\"`\n\t// OAuth scope.\n\tOauthScope string `protobuf:\"bytes,3,opt,name=oauth_scope,json=oauthScope,proto3\" json:\"oauth_scope,omitempty\"`\n}\n\nfunc (x *SimpleResponse) Reset() {\n\t*x = SimpleResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SimpleResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SimpleResponse) ProtoMessage() {}\n\nfunc (x *SimpleResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[4]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SimpleResponse.ProtoReflect.Descriptor instead.\nfunc (*SimpleResponse) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *SimpleResponse) GetPayload() *Payload {\n\tif x != nil {\n\t\treturn x.Payload\n\t}\n\treturn nil\n}\n\nfunc (x *SimpleResponse) GetUsername() string {\n\tif x != nil {\n\t\treturn x.Username\n\t}\n\treturn \"\"\n}\n\nfunc (x *SimpleResponse) GetOauthScope() string {\n\tif x != nil {\n\t\treturn x.OauthScope\n\t}\n\treturn \"\"\n}\n\n// Client-streaming request.\ntype StreamingInputCallRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Optional input payload sent along with the request.\n\tPayload *Payload `protobuf:\"bytes,1,opt,name=payload,proto3\" json:\"payload,omitempty\"`\n}\n\nfunc (x *StreamingInputCallRequest) Reset() {\n\t*x = StreamingInputCallRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *StreamingInputCallRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*StreamingInputCallRequest) ProtoMessage() {}\n\nfunc (x *StreamingInputCallRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[5]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use StreamingInputCallRequest.ProtoReflect.Descriptor instead.\nfunc (*StreamingInputCallRequest) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *StreamingInputCallRequest) GetPayload() *Payload {\n\tif x != nil {\n\t\treturn x.Payload\n\t}\n\treturn nil\n}\n\n// Client-streaming response.\ntype StreamingInputCallResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Aggregated size of payloads received from the client.\n\tAggregatedPayloadSize int32 `protobuf:\"varint,1,opt,name=aggregated_payload_size,json=aggregatedPayloadSize,proto3\" json:\"aggregated_payload_size,omitempty\"`\n}\n\nfunc (x *StreamingInputCallResponse) Reset() {\n\t*x = StreamingInputCallResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *StreamingInputCallResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*StreamingInputCallResponse) ProtoMessage() {}\n\nfunc (x *StreamingInputCallResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[6]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use StreamingInputCallResponse.ProtoReflect.Descriptor instead.\nfunc (*StreamingInputCallResponse) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *StreamingInputCallResponse) GetAggregatedPayloadSize() int32 {\n\tif x != nil {\n\t\treturn x.AggregatedPayloadSize\n\t}\n\treturn 0\n}\n\n// Configuration for a particular response.\ntype ResponseParameters struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Desired payload sizes in responses from the server.\n\t// If response_type is COMPRESSABLE, this denotes the size before compression.\n\tSize int32 `protobuf:\"varint,1,opt,name=size,proto3\" json:\"size,omitempty\"`\n\t// Desired interval between consecutive responses in the response stream in\n\t// microseconds.\n\tIntervalUs int32 `protobuf:\"varint,2,opt,name=interval_us,json=intervalUs,proto3\" json:\"interval_us,omitempty\"`\n}\n\nfunc (x *ResponseParameters) Reset() {\n\t*x = ResponseParameters{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ResponseParameters) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ResponseParameters) ProtoMessage() {}\n\nfunc (x *ResponseParameters) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[7]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ResponseParameters.ProtoReflect.Descriptor instead.\nfunc (*ResponseParameters) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{7}\n}\n\nfunc (x *ResponseParameters) GetSize() int32 {\n\tif x != nil {\n\t\treturn x.Size\n\t}\n\treturn 0\n}\n\nfunc (x *ResponseParameters) GetIntervalUs() int32 {\n\tif x != nil {\n\t\treturn x.IntervalUs\n\t}\n\treturn 0\n}\n\n// Server-streaming request.\ntype StreamingOutputCallRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Desired payload type in the response from the server.\n\t// If response_type is RANDOM, the payload from each response in the stream\n\t// might be of different types. This is to simulate a mixed type of payload\n\t// stream.\n\tResponseType PayloadType `protobuf:\"varint,1,opt,name=response_type,json=responseType,proto3,enum=testing.PayloadType\" json:\"response_type,omitempty\"`\n\t// Configuration for each expected response message.\n\tResponseParameters []*ResponseParameters `protobuf:\"bytes,2,rep,name=response_parameters,json=responseParameters,proto3\" json:\"response_parameters,omitempty\"`\n\t// Optional input payload sent along with the request.\n\tPayload *Payload `protobuf:\"bytes,3,opt,name=payload,proto3\" json:\"payload,omitempty\"`\n\t// Whether server should return a given status\n\tResponseStatus *EchoStatus `protobuf:\"bytes,7,opt,name=response_status,json=responseStatus,proto3\" json:\"response_status,omitempty\"`\n}\n\nfunc (x *StreamingOutputCallRequest) Reset() {\n\t*x = StreamingOutputCallRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *StreamingOutputCallRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*StreamingOutputCallRequest) ProtoMessage() {}\n\nfunc (x *StreamingOutputCallRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[8]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use StreamingOutputCallRequest.ProtoReflect.Descriptor instead.\nfunc (*StreamingOutputCallRequest) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{8}\n}\n\nfunc (x *StreamingOutputCallRequest) GetResponseType() PayloadType {\n\tif x != nil {\n\t\treturn x.ResponseType\n\t}\n\treturn PayloadType_COMPRESSABLE\n}\n\nfunc (x *StreamingOutputCallRequest) GetResponseParameters() []*ResponseParameters {\n\tif x != nil {\n\t\treturn x.ResponseParameters\n\t}\n\treturn nil\n}\n\nfunc (x *StreamingOutputCallRequest) GetPayload() *Payload {\n\tif x != nil {\n\t\treturn x.Payload\n\t}\n\treturn nil\n}\n\nfunc (x *StreamingOutputCallRequest) GetResponseStatus() *EchoStatus {\n\tif x != nil {\n\t\treturn x.ResponseStatus\n\t}\n\treturn nil\n}\n\n// Server-streaming response, as configured by the request and parameters.\ntype StreamingOutputCallResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Payload to increase response size.\n\tPayload *Payload `protobuf:\"bytes,1,opt,name=payload,proto3\" json:\"payload,omitempty\"`\n}\n\nfunc (x *StreamingOutputCallResponse) Reset() {\n\t*x = StreamingOutputCallResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[9]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *StreamingOutputCallResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*StreamingOutputCallResponse) ProtoMessage() {}\n\nfunc (x *StreamingOutputCallResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[9]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use StreamingOutputCallResponse.ProtoReflect.Descriptor instead.\nfunc (*StreamingOutputCallResponse) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{9}\n}\n\nfunc (x *StreamingOutputCallResponse) GetPayload() *Payload {\n\tif x != nil {\n\t\treturn x.Payload\n\t}\n\treturn nil\n}\n\nvar File_test_proto protoreflect.FileDescriptor\n\nvar file_test_proto_rawDesc = []byte{\n\t0x0a, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x74, 0x65,\n\t0x73, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x47,\n\t0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x79, 0x70,\n\t0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e,\n\t0x67, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74,\n\t0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x3a, 0x0a, 0x0a, 0x45, 0x63, 0x68, 0x6f, 0x53,\n\t0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73,\n\t0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73,\n\t0x61, 0x67, 0x65, 0x22, 0xa8, 0x02, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,\n\t0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74,\n\t0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79,\n\t0x70, 0x65, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65,\n\t0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x69, 0x7a,\n\t0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,\n\t0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,\n\t0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67,\n\t0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61,\n\t0x64, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x6c, 0x6c, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61,\n\t0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x69, 0x6c, 0x6c, 0x55, 0x73,\n\t0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x6c, 0x5f, 0x6f,\n\t0x61, 0x75, 0x74, 0x68, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,\n\t0x52, 0x0e, 0x66, 0x69, 0x6c, 0x6c, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x53, 0x63, 0x6f, 0x70, 0x65,\n\t0x12, 0x3c, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x74, 0x61,\n\t0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74,\n\t0x69, 0x6e, 0x67, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0e,\n\t0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x79,\n\t0x0a, 0x0e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,\n\t0x12, 0x2a, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x61, 0x79, 0x6c,\n\t0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1a, 0x0a, 0x08,\n\t0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,\n\t0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x61, 0x75, 0x74,\n\t0x68, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f,\n\t0x61, 0x75, 0x74, 0x68, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x22, 0x47, 0x0a, 0x19, 0x53, 0x74, 0x72,\n\t0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x52,\n\t0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61,\n\t0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e,\n\t0x67, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f,\n\t0x61, 0x64, 0x22, 0x54, 0x0a, 0x1a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x49,\n\t0x6e, 0x70, 0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,\n\t0x12, 0x36, 0x0a, 0x17, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70,\n\t0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x05, 0x52, 0x15, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x50, 0x61, 0x79,\n\t0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x49, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x70,\n\t0x6f, 0x6e, 0x73, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x12,\n\t0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69,\n\t0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x75,\n\t0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,\n\t0x6c, 0x55, 0x73, 0x22, 0x8f, 0x02, 0x0a, 0x1a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e,\n\t0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65,\n\t0x73, 0x74, 0x12, 0x39, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x74,\n\t0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x65, 0x73, 0x74,\n\t0x69, 0x6e, 0x67, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52,\n\t0x0c, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x4c, 0x0a,\n\t0x13, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65,\n\t0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x73,\n\t0x74, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x50, 0x61, 0x72,\n\t0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x12, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,\n\t0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x70,\n\t0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74,\n\t0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07,\n\t0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3c, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x70, 0x6f,\n\t0x6e, 0x73, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,\n\t0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x53,\n\t0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53,\n\t0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x49, 0x0a, 0x1b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69,\n\t0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70,\n\t0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e,\n\t0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,\n\t0x2a, 0x3f, 0x0a, 0x0b, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12,\n\t0x10, 0x0a, 0x0c, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x10,\n\t0x00, 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x4e, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x41,\n\t0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x10,\n\t0x02, 0x32, 0xff, 0x03, 0x0a, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,\n\t0x65, 0x12, 0x2b, 0x0a, 0x09, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x0e,\n\t0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e,\n\t0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3c,\n\t0x0a, 0x09, 0x55, 0x6e, 0x61, 0x72, 0x79, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x16, 0x2e, 0x74, 0x65,\n\t0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75,\n\t0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x69,\n\t0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x13,\n\t0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43,\n\t0x61, 0x6c, 0x6c, 0x12, 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74,\n\t0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x61, 0x6c,\n\t0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69,\n\t0x6e, 0x67, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70,\n\t0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01,\n\t0x12, 0x5f, 0x0a, 0x12, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70,\n\t0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67,\n\t0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x43,\n\t0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x74, 0x65, 0x73,\n\t0x74, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x49, 0x6e,\n\t0x70, 0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28,\n\t0x01, 0x12, 0x5f, 0x0a, 0x0e, 0x46, 0x75, 0x6c, 0x6c, 0x44, 0x75, 0x70, 0x6c, 0x65, 0x78, 0x43,\n\t0x61, 0x6c, 0x6c, 0x12, 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74,\n\t0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x61, 0x6c,\n\t0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69,\n\t0x6e, 0x67, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70,\n\t0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01,\n\t0x30, 0x01, 0x12, 0x5f, 0x0a, 0x0e, 0x48, 0x61, 0x6c, 0x66, 0x44, 0x75, 0x70, 0x6c, 0x65, 0x78,\n\t0x43, 0x61, 0x6c, 0x6c, 0x12, 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x53,\n\t0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x61,\n\t0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x74, 0x65, 0x73, 0x74,\n\t0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74,\n\t0x70, 0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28,\n\t0x01, 0x30, 0x01, 0x32, 0x4b, 0x0a, 0x14, 0x55, 0x6e, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65,\n\t0x6e, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x11, 0x55,\n\t0x6e, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x43, 0x61, 0x6c, 0x6c,\n\t0x12, 0x0e, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,\n\t0x1a, 0x0e, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,\n\t0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x3b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x62, 0x06, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_test_proto_rawDescOnce sync.Once\n\tfile_test_proto_rawDescData = file_test_proto_rawDesc\n)\n\nfunc file_test_proto_rawDescGZIP() []byte {\n\tfile_test_proto_rawDescOnce.Do(func() {\n\t\tfile_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_proto_rawDescData)\n\t})\n\treturn file_test_proto_rawDescData\n}\n\nvar file_test_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 10)\nvar file_test_proto_goTypes = []interface{}{\n\t(PayloadType)(0),                    // 0: testing.PayloadType\n\t(*Empty)(nil),                       // 1: testing.Empty\n\t(*Payload)(nil),                     // 2: testing.Payload\n\t(*EchoStatus)(nil),                  // 3: testing.EchoStatus\n\t(*SimpleRequest)(nil),               // 4: testing.SimpleRequest\n\t(*SimpleResponse)(nil),              // 5: testing.SimpleResponse\n\t(*StreamingInputCallRequest)(nil),   // 6: testing.StreamingInputCallRequest\n\t(*StreamingInputCallResponse)(nil),  // 7: testing.StreamingInputCallResponse\n\t(*ResponseParameters)(nil),          // 8: testing.ResponseParameters\n\t(*StreamingOutputCallRequest)(nil),  // 9: testing.StreamingOutputCallRequest\n\t(*StreamingOutputCallResponse)(nil), // 10: testing.StreamingOutputCallResponse\n}\nvar file_test_proto_depIdxs = []int32{\n\t0,  // 0: testing.Payload.type:type_name -> testing.PayloadType\n\t0,  // 1: testing.SimpleRequest.response_type:type_name -> testing.PayloadType\n\t2,  // 2: testing.SimpleRequest.payload:type_name -> testing.Payload\n\t3,  // 3: testing.SimpleRequest.response_status:type_name -> testing.EchoStatus\n\t2,  // 4: testing.SimpleResponse.payload:type_name -> testing.Payload\n\t2,  // 5: testing.StreamingInputCallRequest.payload:type_name -> testing.Payload\n\t0,  // 6: testing.StreamingOutputCallRequest.response_type:type_name -> testing.PayloadType\n\t8,  // 7: testing.StreamingOutputCallRequest.response_parameters:type_name -> testing.ResponseParameters\n\t2,  // 8: testing.StreamingOutputCallRequest.payload:type_name -> testing.Payload\n\t3,  // 9: testing.StreamingOutputCallRequest.response_status:type_name -> testing.EchoStatus\n\t2,  // 10: testing.StreamingOutputCallResponse.payload:type_name -> testing.Payload\n\t1,  // 11: testing.TestService.EmptyCall:input_type -> testing.Empty\n\t4,  // 12: testing.TestService.UnaryCall:input_type -> testing.SimpleRequest\n\t9,  // 13: testing.TestService.StreamingOutputCall:input_type -> testing.StreamingOutputCallRequest\n\t6,  // 14: testing.TestService.StreamingInputCall:input_type -> testing.StreamingInputCallRequest\n\t9,  // 15: testing.TestService.FullDuplexCall:input_type -> testing.StreamingOutputCallRequest\n\t9,  // 16: testing.TestService.HalfDuplexCall:input_type -> testing.StreamingOutputCallRequest\n\t1,  // 17: testing.UnimplementedService.UnimplementedCall:input_type -> testing.Empty\n\t1,  // 18: testing.TestService.EmptyCall:output_type -> testing.Empty\n\t5,  // 19: testing.TestService.UnaryCall:output_type -> testing.SimpleResponse\n\t10, // 20: testing.TestService.StreamingOutputCall:output_type -> testing.StreamingOutputCallResponse\n\t7,  // 21: testing.TestService.StreamingInputCall:output_type -> testing.StreamingInputCallResponse\n\t10, // 22: testing.TestService.FullDuplexCall:output_type -> testing.StreamingOutputCallResponse\n\t10, // 23: testing.TestService.HalfDuplexCall:output_type -> testing.StreamingOutputCallResponse\n\t1,  // 24: testing.UnimplementedService.UnimplementedCall:output_type -> testing.Empty\n\t18, // [18:25] is the sub-list for method output_type\n\t11, // [11:18] is the sub-list for method input_type\n\t11, // [11:11] is the sub-list for extension type_name\n\t11, // [11:11] is the sub-list for extension extendee\n\t0,  // [0:11] is the sub-list for field type_name\n}\n\nfunc init() { file_test_proto_init() }\nfunc file_test_proto_init() {\n\tif File_test_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Empty); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Payload); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*EchoStatus); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*SimpleRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*SimpleResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*StreamingInputCallRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*StreamingInputCallResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ResponseParameters); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*StreamingOutputCallRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*StreamingOutputCallResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_test_proto_rawDesc,\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   10,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   2,\n\t\t},\n\t\tGoTypes:           file_test_proto_goTypes,\n\t\tDependencyIndexes: file_test_proto_depIdxs,\n\t\tEnumInfos:         file_test_proto_enumTypes,\n\t\tMessageInfos:      file_test_proto_msgTypes,\n\t}.Build()\n\tFile_test_proto = out.File\n\tfile_test_proto_rawDesc = nil\n\tfile_test_proto_goTypes = nil\n\tfile_test_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "internal/testing/test.proto",
    "content": "// NB: Copied from the gRPC Go repo: google.golang.org/grpc/interop/grpc_testing/test.proto\n\n// Copyright 2017 gRPC authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// 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, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// An integration test service that covers all the method signature permutations\n// of unary/streaming requests/responses.\nsyntax = \"proto3\";\n\npackage testing;\n\noption go_package = \".;testing\";\n\nmessage Empty {}\n\n// The type of payload that should be returned.\nenum PayloadType {\n  // Compressable text format.\n  COMPRESSABLE = 0;\n\n  // Uncompressable binary format.\n  UNCOMPRESSABLE = 1;\n\n  // Randomly chosen from all other formats defined in this enum.\n  RANDOM = 2;\n}\n\n// A block of data, to simply increase gRPC message size.\nmessage Payload {\n  // The type of data in body.\n  PayloadType type = 1;\n  // Primary contents of payload.\n  bytes body = 2;\n}\n\n// A protobuf representation for grpc status. This is used by test\n// clients to specify a status that the server should attempt to return.\nmessage EchoStatus {\n  int32 code = 1;\n  string message = 2;\n}\n\n// Unary request.\nmessage SimpleRequest {\n  // Desired payload type in the response from the server.\n  // If response_type is RANDOM, server randomly chooses one from other formats.\n  PayloadType response_type = 1;\n\n  // Desired payload size in the response from the server.\n  // If response_type is COMPRESSABLE, this denotes the size before compression.\n  int32 response_size = 2;\n\n  // Optional input payload sent along with the request.\n  Payload payload = 3;\n\n  // Whether SimpleResponse should include username.\n  bool fill_username = 4;\n\n  // Whether SimpleResponse should include OAuth scope.\n  bool fill_oauth_scope = 5;\n\n  // Whether server should return a given status\n  EchoStatus response_status = 7;\n}\n\n// Unary response, as configured by the request.\nmessage SimpleResponse {\n  // Payload to increase message size.\n  Payload payload = 1;\n\n  // The user the request came from, for verifying authentication was\n  // successful when the client expected it.\n  string username = 2;\n  \n  // OAuth scope.\n  string oauth_scope = 3;\n}\n\n// Client-streaming request.\nmessage StreamingInputCallRequest {\n  // Optional input payload sent along with the request.\n  Payload payload = 1;\n\n  // Not expecting any payload from the response.\n}\n\n// Client-streaming response.\nmessage StreamingInputCallResponse {\n  // Aggregated size of payloads received from the client.\n  int32 aggregated_payload_size = 1;\n}\n\n// Configuration for a particular response.\nmessage ResponseParameters {\n  // Desired payload sizes in responses from the server.\n  // If response_type is COMPRESSABLE, this denotes the size before compression.\n  int32 size = 1;\n\n  // Desired interval between consecutive responses in the response stream in\n  // microseconds.\n  int32 interval_us = 2;\n}\n\n// Server-streaming request.\nmessage StreamingOutputCallRequest {\n  // Desired payload type in the response from the server.\n  // If response_type is RANDOM, the payload from each response in the stream\n  // might be of different types. This is to simulate a mixed type of payload\n  // stream.\n  PayloadType response_type = 1;\n\n  // Configuration for each expected response message.\n  repeated ResponseParameters response_parameters = 2;\n\n  // Optional input payload sent along with the request.\n  Payload payload = 3;\n\n  // Whether server should return a given status\n  EchoStatus response_status = 7;\n}\n\n// Server-streaming response, as configured by the request and parameters.\nmessage StreamingOutputCallResponse {\n  // Payload to increase response size.\n  Payload payload = 1;\n}\n\n// A simple service to test the various types of RPCs and experiment with\n// performance with various types of payload.\nservice TestService {\n  // One empty request followed by one empty response.\n  rpc EmptyCall(Empty) returns (Empty);\n\n  // One request followed by one response.\n  // The server returns the client payload as-is.\n  rpc UnaryCall(SimpleRequest) returns (SimpleResponse);\n\n  // One request followed by a sequence of responses (streamed download).\n  // The server returns the payload with client desired type and sizes.\n  rpc StreamingOutputCall(StreamingOutputCallRequest)\n      returns (stream StreamingOutputCallResponse);\n\n  // A sequence of requests followed by one response (streamed upload).\n  // The server returns the aggregated size of client payload as the result.\n  rpc StreamingInputCall(stream StreamingInputCallRequest)\n      returns (StreamingInputCallResponse);\n\n  // A sequence of requests with each request served by the server immediately.\n  // As one request could lead to multiple responses, this interface\n  // demonstrates the idea of full duplexing.\n  rpc FullDuplexCall(stream StreamingOutputCallRequest)\n      returns (stream StreamingOutputCallResponse);\n\n  // A sequence of requests followed by a sequence of responses.\n  // The server buffers all the client requests and then serves them in order. A\n  // stream of responses are returned to the client when the server starts with\n  // first request.\n  rpc HalfDuplexCall(stream StreamingOutputCallRequest)\n      returns (stream StreamingOutputCallResponse);\n}\n\n// A simple service NOT implemented at servers so clients can test for\n// that case.\nservice UnimplementedService {\n  // A call that no server should implement\n  rpc UnimplementedCall(Empty) returns (Empty);\n}\n"
  },
  {
    "path": "internal/testing/test_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage testing\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.32.0 or later.\nconst _ = grpc.SupportPackageIsVersion7\n\n// TestServiceClient is the client API for TestService service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype TestServiceClient interface {\n\t// One empty request followed by one empty response.\n\tEmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)\n\t// One request followed by one response.\n\t// The server returns the client payload as-is.\n\tUnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error)\n\t// One request followed by a sequence of responses (streamed download).\n\t// The server returns the payload with client desired type and sizes.\n\tStreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error)\n\t// A sequence of requests followed by one response (streamed upload).\n\t// The server returns the aggregated size of client payload as the result.\n\tStreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error)\n\t// A sequence of requests with each request served by the server immediately.\n\t// As one request could lead to multiple responses, this interface\n\t// demonstrates the idea of full duplexing.\n\tFullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error)\n\t// A sequence of requests followed by a sequence of responses.\n\t// The server buffers all the client requests and then serves them in order. A\n\t// stream of responses are returned to the client when the server starts with\n\t// first request.\n\tHalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error)\n}\n\ntype testServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewTestServiceClient(cc grpc.ClientConnInterface) TestServiceClient {\n\treturn &testServiceClient{cc}\n}\n\nfunc (c *testServiceClient) EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {\n\tout := new(Empty)\n\terr := c.cc.Invoke(ctx, \"/testing.TestService/EmptyCall\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *testServiceClient) UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error) {\n\tout := new(SimpleResponse)\n\terr := c.cc.Invoke(ctx, \"/testing.TestService/UnaryCall\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *testServiceClient) StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &TestService_ServiceDesc.Streams[0], \"/testing.TestService/StreamingOutputCall\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &testServiceStreamingOutputCallClient{stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\ntype TestService_StreamingOutputCallClient interface {\n\tRecv() (*StreamingOutputCallResponse, error)\n\tgrpc.ClientStream\n}\n\ntype testServiceStreamingOutputCallClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *testServiceStreamingOutputCallClient) Recv() (*StreamingOutputCallResponse, error) {\n\tm := new(StreamingOutputCallResponse)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc (c *testServiceClient) StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &TestService_ServiceDesc.Streams[1], \"/testing.TestService/StreamingInputCall\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &testServiceStreamingInputCallClient{stream}\n\treturn x, nil\n}\n\ntype TestService_StreamingInputCallClient interface {\n\tSend(*StreamingInputCallRequest) error\n\tCloseAndRecv() (*StreamingInputCallResponse, error)\n\tgrpc.ClientStream\n}\n\ntype testServiceStreamingInputCallClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *testServiceStreamingInputCallClient) Send(m *StreamingInputCallRequest) error {\n\treturn x.ClientStream.SendMsg(m)\n}\n\nfunc (x *testServiceStreamingInputCallClient) CloseAndRecv() (*StreamingInputCallResponse, error) {\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\tm := new(StreamingInputCallResponse)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc (c *testServiceClient) FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &TestService_ServiceDesc.Streams[2], \"/testing.TestService/FullDuplexCall\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &testServiceFullDuplexCallClient{stream}\n\treturn x, nil\n}\n\ntype TestService_FullDuplexCallClient interface {\n\tSend(*StreamingOutputCallRequest) error\n\tRecv() (*StreamingOutputCallResponse, error)\n\tgrpc.ClientStream\n}\n\ntype testServiceFullDuplexCallClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *testServiceFullDuplexCallClient) Send(m *StreamingOutputCallRequest) error {\n\treturn x.ClientStream.SendMsg(m)\n}\n\nfunc (x *testServiceFullDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {\n\tm := new(StreamingOutputCallResponse)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc (c *testServiceClient) HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &TestService_ServiceDesc.Streams[3], \"/testing.TestService/HalfDuplexCall\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &testServiceHalfDuplexCallClient{stream}\n\treturn x, nil\n}\n\ntype TestService_HalfDuplexCallClient interface {\n\tSend(*StreamingOutputCallRequest) error\n\tRecv() (*StreamingOutputCallResponse, error)\n\tgrpc.ClientStream\n}\n\ntype testServiceHalfDuplexCallClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *testServiceHalfDuplexCallClient) Send(m *StreamingOutputCallRequest) error {\n\treturn x.ClientStream.SendMsg(m)\n}\n\nfunc (x *testServiceHalfDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {\n\tm := new(StreamingOutputCallResponse)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\n// TestServiceServer is the server API for TestService service.\n// All implementations must embed UnimplementedTestServiceServer\n// for forward compatibility\ntype TestServiceServer interface {\n\t// One empty request followed by one empty response.\n\tEmptyCall(context.Context, *Empty) (*Empty, error)\n\t// One request followed by one response.\n\t// The server returns the client payload as-is.\n\tUnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error)\n\t// One request followed by a sequence of responses (streamed download).\n\t// The server returns the payload with client desired type and sizes.\n\tStreamingOutputCall(*StreamingOutputCallRequest, TestService_StreamingOutputCallServer) error\n\t// A sequence of requests followed by one response (streamed upload).\n\t// The server returns the aggregated size of client payload as the result.\n\tStreamingInputCall(TestService_StreamingInputCallServer) error\n\t// A sequence of requests with each request served by the server immediately.\n\t// As one request could lead to multiple responses, this interface\n\t// demonstrates the idea of full duplexing.\n\tFullDuplexCall(TestService_FullDuplexCallServer) error\n\t// A sequence of requests followed by a sequence of responses.\n\t// The server buffers all the client requests and then serves them in order. A\n\t// stream of responses are returned to the client when the server starts with\n\t// first request.\n\tHalfDuplexCall(TestService_HalfDuplexCallServer) error\n\tmustEmbedUnimplementedTestServiceServer()\n}\n\n// UnimplementedTestServiceServer must be embedded to have forward compatible implementations.\ntype UnimplementedTestServiceServer struct {\n}\n\nfunc (UnimplementedTestServiceServer) EmptyCall(context.Context, *Empty) (*Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method EmptyCall not implemented\")\n}\nfunc (UnimplementedTestServiceServer) UnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method UnaryCall not implemented\")\n}\nfunc (UnimplementedTestServiceServer) StreamingOutputCall(*StreamingOutputCallRequest, TestService_StreamingOutputCallServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method StreamingOutputCall not implemented\")\n}\nfunc (UnimplementedTestServiceServer) StreamingInputCall(TestService_StreamingInputCallServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method StreamingInputCall not implemented\")\n}\nfunc (UnimplementedTestServiceServer) FullDuplexCall(TestService_FullDuplexCallServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method FullDuplexCall not implemented\")\n}\nfunc (UnimplementedTestServiceServer) HalfDuplexCall(TestService_HalfDuplexCallServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method HalfDuplexCall not implemented\")\n}\nfunc (UnimplementedTestServiceServer) mustEmbedUnimplementedTestServiceServer() {}\n\n// UnsafeTestServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to TestServiceServer will\n// result in compilation errors.\ntype UnsafeTestServiceServer interface {\n\tmustEmbedUnimplementedTestServiceServer()\n}\n\nfunc RegisterTestServiceServer(s grpc.ServiceRegistrar, srv TestServiceServer) {\n\ts.RegisterService(&TestService_ServiceDesc, srv)\n}\n\nfunc _TestService_EmptyCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(Empty)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(TestServiceServer).EmptyCall(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/testing.TestService/EmptyCall\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(TestServiceServer).EmptyCall(ctx, req.(*Empty))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _TestService_UnaryCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(SimpleRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(TestServiceServer).UnaryCall(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/testing.TestService/UnaryCall\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(TestServiceServer).UnaryCall(ctx, req.(*SimpleRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _TestService_StreamingOutputCall_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(StreamingOutputCallRequest)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(TestServiceServer).StreamingOutputCall(m, &testServiceStreamingOutputCallServer{stream})\n}\n\ntype TestService_StreamingOutputCallServer interface {\n\tSend(*StreamingOutputCallResponse) error\n\tgrpc.ServerStream\n}\n\ntype testServiceStreamingOutputCallServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *testServiceStreamingOutputCallServer) Send(m *StreamingOutputCallResponse) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc _TestService_StreamingInputCall_Handler(srv interface{}, stream grpc.ServerStream) error {\n\treturn srv.(TestServiceServer).StreamingInputCall(&testServiceStreamingInputCallServer{stream})\n}\n\ntype TestService_StreamingInputCallServer interface {\n\tSendAndClose(*StreamingInputCallResponse) error\n\tRecv() (*StreamingInputCallRequest, error)\n\tgrpc.ServerStream\n}\n\ntype testServiceStreamingInputCallServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *testServiceStreamingInputCallServer) SendAndClose(m *StreamingInputCallResponse) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc (x *testServiceStreamingInputCallServer) Recv() (*StreamingInputCallRequest, error) {\n\tm := new(StreamingInputCallRequest)\n\tif err := x.ServerStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc _TestService_FullDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {\n\treturn srv.(TestServiceServer).FullDuplexCall(&testServiceFullDuplexCallServer{stream})\n}\n\ntype TestService_FullDuplexCallServer interface {\n\tSend(*StreamingOutputCallResponse) error\n\tRecv() (*StreamingOutputCallRequest, error)\n\tgrpc.ServerStream\n}\n\ntype testServiceFullDuplexCallServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *testServiceFullDuplexCallServer) Send(m *StreamingOutputCallResponse) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc (x *testServiceFullDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {\n\tm := new(StreamingOutputCallRequest)\n\tif err := x.ServerStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc _TestService_HalfDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {\n\treturn srv.(TestServiceServer).HalfDuplexCall(&testServiceHalfDuplexCallServer{stream})\n}\n\ntype TestService_HalfDuplexCallServer interface {\n\tSend(*StreamingOutputCallResponse) error\n\tRecv() (*StreamingOutputCallRequest, error)\n\tgrpc.ServerStream\n}\n\ntype testServiceHalfDuplexCallServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *testServiceHalfDuplexCallServer) Send(m *StreamingOutputCallResponse) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc (x *testServiceHalfDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {\n\tm := new(StreamingOutputCallRequest)\n\tif err := x.ServerStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\n// TestService_ServiceDesc is the grpc.ServiceDesc for TestService service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar TestService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"testing.TestService\",\n\tHandlerType: (*TestServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"EmptyCall\",\n\t\t\tHandler:    _TestService_EmptyCall_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"UnaryCall\",\n\t\t\tHandler:    _TestService_UnaryCall_Handler,\n\t\t},\n\t},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"StreamingOutputCall\",\n\t\t\tHandler:       _TestService_StreamingOutputCall_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t\t{\n\t\t\tStreamName:    \"StreamingInputCall\",\n\t\t\tHandler:       _TestService_StreamingInputCall_Handler,\n\t\t\tClientStreams: true,\n\t\t},\n\t\t{\n\t\t\tStreamName:    \"FullDuplexCall\",\n\t\t\tHandler:       _TestService_FullDuplexCall_Handler,\n\t\t\tServerStreams: true,\n\t\t\tClientStreams: true,\n\t\t},\n\t\t{\n\t\t\tStreamName:    \"HalfDuplexCall\",\n\t\t\tHandler:       _TestService_HalfDuplexCall_Handler,\n\t\t\tServerStreams: true,\n\t\t\tClientStreams: true,\n\t\t},\n\t},\n\tMetadata: \"test.proto\",\n}\n\n// UnimplementedServiceClient is the client API for UnimplementedService service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype UnimplementedServiceClient interface {\n\t// A call that no server should implement\n\tUnimplementedCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)\n}\n\ntype unimplementedServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewUnimplementedServiceClient(cc grpc.ClientConnInterface) UnimplementedServiceClient {\n\treturn &unimplementedServiceClient{cc}\n}\n\nfunc (c *unimplementedServiceClient) UnimplementedCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {\n\tout := new(Empty)\n\terr := c.cc.Invoke(ctx, \"/testing.UnimplementedService/UnimplementedCall\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// UnimplementedServiceServer is the server API for UnimplementedService service.\n// All implementations must embed UnimplementedUnimplementedServiceServer\n// for forward compatibility\ntype UnimplementedServiceServer interface {\n\t// A call that no server should implement\n\tUnimplementedCall(context.Context, *Empty) (*Empty, error)\n\tmustEmbedUnimplementedUnimplementedServiceServer()\n}\n\n// UnimplementedUnimplementedServiceServer must be embedded to have forward compatible implementations.\ntype UnimplementedUnimplementedServiceServer struct {\n}\n\nfunc (UnimplementedUnimplementedServiceServer) UnimplementedCall(context.Context, *Empty) (*Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method UnimplementedCall not implemented\")\n}\nfunc (UnimplementedUnimplementedServiceServer) mustEmbedUnimplementedUnimplementedServiceServer() {}\n\n// UnsafeUnimplementedServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to UnimplementedServiceServer will\n// result in compilation errors.\ntype UnsafeUnimplementedServiceServer interface {\n\tmustEmbedUnimplementedUnimplementedServiceServer()\n}\n\nfunc RegisterUnimplementedServiceServer(s grpc.ServiceRegistrar, srv UnimplementedServiceServer) {\n\ts.RegisterService(&UnimplementedService_ServiceDesc, srv)\n}\n\nfunc _UnimplementedService_UnimplementedCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(Empty)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(UnimplementedServiceServer).UnimplementedCall(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/testing.UnimplementedService/UnimplementedCall\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(UnimplementedServiceServer).UnimplementedCall(ctx, req.(*Empty))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// UnimplementedService_ServiceDesc is the grpc.ServiceDesc for UnimplementedService service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar UnimplementedService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"testing.UnimplementedService\",\n\tHandlerType: (*UnimplementedServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"UnimplementedCall\",\n\t\t\tHandler:    _UnimplementedService_UnimplementedCall_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"test.proto\",\n}\n"
  },
  {
    "path": "internal/testing/test_server.go",
    "content": "package testing\n\n//go:generate protoc --go_out=. --go-grpc_out=. test.proto\n//go:generate protoc --descriptor_set_out=./test.protoset test.proto\n//go:generate protoc --descriptor_set_out=./example.protoset --include_imports example.proto\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/status\"\n\n\t\"github.com/fullstorydev/grpcurl\"\n)\n\n// TestServer implements the TestService interface defined in example.proto.\ntype TestServer struct {\n\tUnimplementedTestServiceServer\n}\n\n// EmptyCall accepts one empty request and issues one empty response.\nfunc (TestServer) EmptyCall(ctx context.Context, req *Empty) (*Empty, error) {\n\theaders, trailers, failEarly, failLate := processMetadata(ctx)\n\tgrpc.SetHeader(ctx, headers)\n\tgrpc.SetTrailer(ctx, trailers)\n\tif failEarly != codes.OK {\n\t\treturn nil, status.Error(failEarly, \"fail\")\n\t}\n\tif failLate != codes.OK {\n\t\treturn nil, status.Error(failLate, \"fail\")\n\t}\n\n\treturn req, nil\n}\n\n// UnaryCall accepts one request and issues one response. The response includes\n// the client's payload as-is.\nfunc (TestServer) UnaryCall(ctx context.Context, req *SimpleRequest) (*SimpleResponse, error) {\n\theaders, trailers, failEarly, failLate := processMetadata(ctx)\n\tgrpc.SetHeader(ctx, headers)\n\tgrpc.SetTrailer(ctx, trailers)\n\tif failEarly != codes.OK {\n\t\treturn nil, status.Error(failEarly, \"fail\")\n\t}\n\tif failLate != codes.OK {\n\t\treturn nil, status.Error(failLate, \"fail\")\n\t}\n\n\treturn &SimpleResponse{\n\t\tPayload: req.Payload,\n\t}, nil\n}\n\n// StreamingOutputCall accepts one request and issues a sequence of responses\n// (streamed download). The server returns the payload with client desired type\n// and sizes as specified in the request's ResponseParameters.\nfunc (TestServer) StreamingOutputCall(req *StreamingOutputCallRequest, str TestService_StreamingOutputCallServer) error {\n\theaders, trailers, failEarly, failLate := processMetadata(str.Context())\n\tstr.SetHeader(headers)\n\tstr.SetTrailer(trailers)\n\tif failEarly != codes.OK {\n\t\treturn status.Error(failEarly, \"fail\")\n\t}\n\n\trsp := &StreamingOutputCallResponse{Payload: &Payload{}}\n\tfor _, param := range req.ResponseParameters {\n\t\tif str.Context().Err() != nil {\n\t\t\treturn str.Context().Err()\n\t\t}\n\t\tdelayMicros := int64(param.GetIntervalUs()) * int64(time.Microsecond)\n\t\tif delayMicros > 0 {\n\t\t\ttime.Sleep(time.Duration(delayMicros))\n\t\t}\n\t\tsz := int(param.GetSize())\n\t\tbuf := make([]byte, sz)\n\t\tfor i := 0; i < sz; i++ {\n\t\t\tbuf[i] = byte(i)\n\t\t}\n\t\trsp.Payload.Type = req.ResponseType\n\t\trsp.Payload.Body = buf\n\t\tif err := str.Send(rsp); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif failLate != codes.OK {\n\t\treturn status.Error(failLate, \"fail\")\n\t}\n\treturn nil\n}\n\n// StreamingInputCall accepts a sequence of requests and issues one response\n// (streamed upload). The server returns the aggregated size of client payloads\n// as the result.\nfunc (TestServer) StreamingInputCall(str TestService_StreamingInputCallServer) error {\n\theaders, trailers, failEarly, failLate := processMetadata(str.Context())\n\tstr.SetHeader(headers)\n\tstr.SetTrailer(trailers)\n\tif failEarly != codes.OK {\n\t\treturn status.Error(failEarly, \"fail\")\n\t}\n\n\tsz := 0\n\tfor {\n\t\tif str.Context().Err() != nil {\n\t\t\treturn str.Context().Err()\n\t\t}\n\t\tif req, err := str.Recv(); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn err\n\t\t} else {\n\t\t\tsz += len(req.Payload.Body)\n\t\t}\n\t}\n\tif err := str.SendAndClose(&StreamingInputCallResponse{AggregatedPayloadSize: int32(sz)}); err != nil {\n\t\treturn err\n\t}\n\n\tif failLate != codes.OK {\n\t\treturn status.Error(failLate, \"fail\")\n\t}\n\treturn nil\n}\n\n// FullDuplexCall accepts a sequence of requests with each request served by the\n// server immediately. As one request could lead to multiple responses, this\n// interface demonstrates the idea of full duplexing.\nfunc (TestServer) FullDuplexCall(str TestService_FullDuplexCallServer) error {\n\theaders, trailers, failEarly, failLate := processMetadata(str.Context())\n\tstr.SetHeader(headers)\n\tstr.SetTrailer(trailers)\n\tif failEarly != codes.OK {\n\t\treturn status.Error(failEarly, \"fail\")\n\t}\n\n\trsp := &StreamingOutputCallResponse{Payload: &Payload{}}\n\tfor {\n\t\tif str.Context().Err() != nil {\n\t\t\treturn str.Context().Err()\n\t\t}\n\t\treq, err := str.Recv()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, param := range req.ResponseParameters {\n\t\t\tsz := int(param.GetSize())\n\t\t\tbuf := make([]byte, sz)\n\t\t\tfor i := 0; i < sz; i++ {\n\t\t\t\tbuf[i] = byte(i)\n\t\t\t}\n\t\t\trsp.Payload.Type = req.ResponseType\n\t\t\trsp.Payload.Body = buf\n\t\t\tif err := str.Send(rsp); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tif failLate != codes.OK {\n\t\treturn status.Error(failLate, \"fail\")\n\t}\n\treturn nil\n}\n\n// HalfDuplexCall accepts a sequence of requests and issues a sequence of\n// responses. The server buffers all the client requests and then serves them\n// in order. A stream of responses is returned to the client once the client\n// half-closes the stream.\nfunc (TestServer) HalfDuplexCall(str TestService_HalfDuplexCallServer) error {\n\theaders, trailers, failEarly, failLate := processMetadata(str.Context())\n\tstr.SetHeader(headers)\n\tstr.SetTrailer(trailers)\n\tif failEarly != codes.OK {\n\t\treturn status.Error(failEarly, \"fail\")\n\t}\n\n\tvar reqs []*StreamingOutputCallRequest\n\tfor {\n\t\tif str.Context().Err() != nil {\n\t\t\treturn str.Context().Err()\n\t\t}\n\t\tif req, err := str.Recv(); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn err\n\t\t} else {\n\t\t\treqs = append(reqs, req)\n\t\t}\n\t}\n\trsp := &StreamingOutputCallResponse{}\n\tfor _, req := range reqs {\n\t\trsp.Payload = req.Payload\n\t\tif err := str.Send(rsp); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif failLate != codes.OK {\n\t\treturn status.Error(failLate, \"fail\")\n\t}\n\treturn nil\n}\n\nconst (\n\t// MetadataReplyHeaders is a request header that contains values that will\n\t// be echoed back to the client as response headers. The format of the value\n\t// is \"key: val\". To have the server reply with more than one response\n\t// header, supply multiple values in request metadata.\n\tMetadataReplyHeaders = \"reply-with-headers\"\n\t// MetadataReplyTrailers is a request header that contains values that will\n\t// be echoed back to the client as response trailers. Its format its the\n\t// same as MetadataReplyHeaders.\n\tMetadataReplyTrailers = \"reply-with-trailers\"\n\t// MetadataFailEarly is a request header that, if present and not zero,\n\t// indicates that the RPC should fail immediately with that code.\n\tMetadataFailEarly = \"fail-early\"\n\t// MetadataFailLate is a request header that, if present and not zero,\n\t// indicates that the RPC should fail at the end with that code. This is\n\t// different from MetadataFailEarly only for streaming calls. An early\n\t// failure means the call to fail before any request stream is read or any\n\t// response stream is generated. A late failure means the entire request and\n\t// response streams will be consumed/processed and only then will the error\n\t// code be sent.\n\tMetadataFailLate = \"fail-late\"\n)\n\nfunc processMetadata(ctx context.Context) (metadata.MD, metadata.MD, codes.Code, codes.Code) {\n\tmd, ok := metadata.FromIncomingContext(ctx)\n\tif !ok {\n\t\treturn nil, nil, codes.OK, codes.OK\n\t}\n\treturn grpcurl.MetadataFromHeaders(md[MetadataReplyHeaders]),\n\t\tgrpcurl.MetadataFromHeaders(md[MetadataReplyTrailers]),\n\t\ttoCode(md[MetadataFailEarly]),\n\t\ttoCode(md[MetadataFailLate])\n}\n\nfunc toCode(vals []string) codes.Code {\n\tif len(vals) == 0 {\n\t\treturn codes.OK\n\t}\n\ti, err := strconv.Atoi(vals[len(vals)-1])\n\tif err != nil {\n\t\treturn codes.Code(i)\n\t}\n\treturn codes.Code(i)\n}\n\nvar _ TestServiceServer = TestServer{}\n"
  },
  {
    "path": "internal/testing/tls/ca.crl",
    "content": "-----BEGIN X509 CRL-----\nMIICfDBmAgEBMA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNVBAMTAmNhFw0xNzA4MjUx\nNTQ1NTNaFw0yNzA4MjUxNTQ1NTNaMACgIzAhMB8GA1UdIwQYMBaAFM0FLuuYBwuA\nJ+tocRlu+xUuOw6FMA0GCSqGSIb3DQEBCwUAA4ICAQCcN8WJKbvGrunXgRBjSnsM\nj/sejaX3CCZPmrXeditekSNMatO0JDXOjyoEvv7s9aZrAf3eFOU3Vr5N7PlbLRdj\ntovuKTeVp3ungqMoT70cFEf/7eMlpWMB2GkfpV9LtF5Tb8dOYT3kllqtMKv4TeZo\n2adu+GXdeQsqlz9fDEi0ZV4RBruuO0QyLWXpNrUB6fznUDfE4KVBsAIadjsg+Aew\n6jeTkYuUILWMwBM6MzOG/InTKqXpe4ghMufI9fO+phxY10gz4QQ44ZNOa18OuiJw\nIH8MoKzhrgUAPLs135hpdGbDePVw5SIKMHUAU2UEKtozAMVfCW45MZHREDdMV3NA\nw5QWDoBYl4jol08Orbccmhu4fbauXmB5Id4IPVgGEGFPpiH/QVyJgZIv1AD2dlRg\nTd26iz9I25hyrpEfF1gJMtOsDOklDsUiMo8ncQ3CL+pkKnMjhm54k6OFe0qlGsdO\nKSavNlEmW/F9h/gs5kaLeFv0v4JxLh12TY28pCE60yoB/UkuB1a+VTHcP3Fa6uUC\nuyv0T0f5yHujaM1isGjI3XGgVgLyJiFxtKMPsMEwRrsrEafqp7JCeLpnIWt1J0C/\nZz3roGcCGj86Oq5zUjdguHS6Ra+uaX+IMJGohWq1cndzVzNfUNkRIyl8IdkCv9o3\nJ8fVwBzN6sAu6zWd3BqT4A==\n-----END X509 CRL-----\n"
  },
  {
    "path": "internal/testing/tls/ca.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIE2jCCAsKgAwIBAgIBATANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJjYTAe\nFw0xNzA4MjUxNTQ1NTJaFw0yNzA4MjUxNTQ1NTNaMA0xCzAJBgNVBAMTAmNhMIIC\nIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnjw7iZyn9EtjtK7zT+M59OxS\nJ43a3kMm11Vnh1Fw8oQ7tH0kQW6COyAwlBhAzWGtfDC6jG7A2n+8mPWoinsxoLA5\nviSZrEkWZ4tGxWWZ5y/xWh5NBrHa3Jsg1rZ5dstAl08gJPwl0v32aYkMdtk16K0k\njtdPpKtO98v1N6ea7fvQKdyrAaUHY/BY+onoqmSBGPX6vVV7FGybWDf72J5vcwyA\n07wdaZ7TrUYbD28nhXuc4Dt2KbZbWvkT4OYZ+4c+eiehRFVGtuXXi0cKG0xF516b\nDnrSO1/2ZbIB2xmcgKvcay0jLzWqhnsSc+qTqODVLODQAMvrMlY0RUXQPn/WTfAw\naQ+u/j7qIR1KFZcLn7uVq8bCM9g+VeyLjq+6XUvgGL9KhvrR/FA4R4DUka+ZVQqh\ns262Qs7pNFdoIIrTsJyPd7/UYWCcQbkCKw0aRoUfBeZgkg4bylcABygZqY9+apRF\nNBEhpycAEvWFarr6rosqII9kLm1LpnPNEgSvQ/CIRIHq5z5iKeSvHFYOVxLS44HH\nM16Mry5UF/jW7Vg8JY5Jrg5YwyOhdGoOSJ3+c/pbLq1TRkwK9bwEJwGr7FdkrjPZ\nuNJ7HQiFn2IaeLlbtzwJ+q3vGSFEjlujOigwUJVzXz16Q93vYIuK3FcyDuOMzpwW\nHHgSLH/+rdtd+7hoLwMCAwEAAaNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB\n/wQIMAYBAf8CAQAwHQYDVR0OBBYEFM0FLuuYBwuAJ+tocRlu+xUuOw6FMA0GCSqG\nSIb3DQEBCwUAA4ICAQBLNuP8ovTgFcYf7Ydgc+aDB2v+qMYVIqJMrnp8DuYy20yv\n64jYcIxh35IbQWOZxZshJsebRKM9vr6huEo2c/SuHLQ5HZGPxSt++aG+iY4Y1zL5\nKHtG558lK4S5VsXymMkUjGZtm+ZuJida9ZcV+jz/kePMHpErWPeMvH2jDmD4mWgA\nYdjipD4cxEn+9O3lBSCkeSjaAd5rQeD9XomV4a2/uL4Y7RDbn9BNt+jdLvfu2pmo\nO1zcp0f578oFlUIg0H9fb6YzL3MKOXiuh7KE1/W9el5zsN/kLlyWFbopN34A6PlO\nZHEvZZcQW06bmy2FRWgqkqWMqBwzWk7JKGp+ozv8IBvimhgjNun068FQAZV9nfKU\n6U728P6T1USDhgwtpX7/2IaukXcmO2FE9XzKZyYAbmAcOhPLzFO4pdwapU2lPbFE\nl2HLkYaHLXzMxB30kQQHW2l8+8xr+MAa+bBcD9Jaxaz/t3ZpLt62/1nxT7SWNwH4\nSa83BaG3EHBotlBc18hqrFWEKR4KYenqY8xa7kblDI0rXqlXBblUXp0TwIctOmzR\ncoqR8q6/R4VXhD9FZBIW1/uX2KKEPfTM46aQdaTtdzjd3UzwTP0SRwkvZ4oFftW6\ns1GljfCGsrOpi6O/Uy/IVTE7Xn/oVnlJvGbaP+AHexLytBiBVUBukLBwvpJ8bg==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "internal/testing/tls/ca.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKAIBAAKCAgEAnjw7iZyn9EtjtK7zT+M59OxSJ43a3kMm11Vnh1Fw8oQ7tH0k\nQW6COyAwlBhAzWGtfDC6jG7A2n+8mPWoinsxoLA5viSZrEkWZ4tGxWWZ5y/xWh5N\nBrHa3Jsg1rZ5dstAl08gJPwl0v32aYkMdtk16K0kjtdPpKtO98v1N6ea7fvQKdyr\nAaUHY/BY+onoqmSBGPX6vVV7FGybWDf72J5vcwyA07wdaZ7TrUYbD28nhXuc4Dt2\nKbZbWvkT4OYZ+4c+eiehRFVGtuXXi0cKG0xF516bDnrSO1/2ZbIB2xmcgKvcay0j\nLzWqhnsSc+qTqODVLODQAMvrMlY0RUXQPn/WTfAwaQ+u/j7qIR1KFZcLn7uVq8bC\nM9g+VeyLjq+6XUvgGL9KhvrR/FA4R4DUka+ZVQqhs262Qs7pNFdoIIrTsJyPd7/U\nYWCcQbkCKw0aRoUfBeZgkg4bylcABygZqY9+apRFNBEhpycAEvWFarr6rosqII9k\nLm1LpnPNEgSvQ/CIRIHq5z5iKeSvHFYOVxLS44HHM16Mry5UF/jW7Vg8JY5Jrg5Y\nwyOhdGoOSJ3+c/pbLq1TRkwK9bwEJwGr7FdkrjPZuNJ7HQiFn2IaeLlbtzwJ+q3v\nGSFEjlujOigwUJVzXz16Q93vYIuK3FcyDuOMzpwWHHgSLH/+rdtd+7hoLwMCAwEA\nAQKCAgBvitoVWX7zsKkqVyFhMTZLtsL66v5cK04YATYnp3tNGXXU91o1Xacj8r8L\nxkT4AmD+6IK4N+JupBjYYmNaqxkCwvcRWE+TqTnH5+ANil+BHsSt2CpIC9vSIvB1\nKtBYs1Jm1vo72Br5rtii8F7+8IMV7+eTYafc1n2mI/pKLzYBiL7mo41QbXrWMjkm\n80w1wP9YDx2flcBbV2vyNhSsUJMTsL6ngzXgnHtu67prmNltOQQO9RuIr+maKXaf\n1NSAAIhEJ+eAefSNPVxB6+Pt9khYntIC1QWZoT3Z1i+EuXsfIQcR7hGdV+FLRzps\nx/Eq3MKpDhjSVu0G4MmcA2iWhhsUXbOihpXKWnAdLv0cUP0tbbxcUsEa4igAruBW\nn6+hYrVkbD5sZuGoMdzKvnGkqRTf/ragdcFlfty7KaAUBFr5V0fzsaW53+/5zG5o\neSRoyCNLQSbBh2TVVpnNIbXELuYmwoDvnPNup4G7ITFsUQZEW2Tm3LHQt7EAi/wn\nhJU/21rI46ubB3r5wJZ+6JOK4PiqPeSIokyVfFyPk2ny4LT7ne5MtPHF+wAVAOYj\n0wcLEyh2s1b0VSlko6GnPjbLi8eAtOAk2ggVK5GofnkhsPohA+yoNvcDDLbcdQ8v\n9Q/nbL2dENce6HZEBSi5RlElU9+BbOJc4qFM2o9mzrWuOpHRsQKCAQEA0j+OhUag\nqbu7tNcWvE8w0I9vt1CXuZrS2ypKpuaaMYknb2Lo4YtuV9+bHx1isGHnAc2RAbUB\n23mLANhquRIOo7u3NTvtRsvyzrRuuFviQZ5p1b8MHfqpg+/mA+yve8J3zyMMNC5f\nc7m/13J+dsQNf/WWhqbnWU3wOoRa0NQBtOw7UhVFl+1JeBfSiVmYQXH1n5VnOWs8\nVab5kGkeYpUssgMFJG07qgdX5Ux3KzAQm7Onvsn4tT5UMtHt67f/wWzlP/xcoBJW\n67clhCuO2Jiojo4jSNko0PGgTmFPmF3EOW+zd+iEYP3LF+S4uTR7yz4+foUNa9e2\nxf6XwMp3ymjbfwKCAQEAwKsnMt0KErZ1/iRbN6U5Rcgxcq6FxRivXyzQX/3QrWc0\nr6H4WWk5+gwy/Fb1CpQyiJkXG7PpVysdWaWF5S3NRVL3Ixuyp1R10DmPYch/4Pn6\n4BD9UgKUhS2nxBVcMyyM/mN0W1Img22tCaJhaI+/raYf61JxgWmUJmUq8k8Xzgfv\nndEYQGgf62jG35aopkqfwiC8+rApgbiLoN1mGiusyJUcZmYLLYp2ao/xHc7UtjMP\nN5tQeE0aZgSaBBwDAMQxMdWovo5qThvpJdy8q8EVq6sCO1G1MzLzIMxd/4asVzLc\nwUHSG/8c9qdgxBGGhYAbTSVegWTaqznDrlFB8RP+fQKCAQBOx+vyeqWHFEZgm9v0\nEcRb0fNtgDBqJt5tqyov4ebTOu5g6XIT2XguSyZIAW3SY8z4uvtj5VxdzexNE8rh\nsCd2KMeclejyB0fjNm7qe9uK9P35Ts4OibdtLb5FqDGVMShNoHdZMisoJOkCpO9I\nN2xLj02pBO9ZYj/q3V9eMqK1FXOg7UGXjR1jd6G3P7Ayja4Y7xWvyUPhYGDRQOJW\n1EjcJw+NN7UMoBXKYN2ifC8s+KOZdPrRhxprtIfvNJIL+27ni/t1K4oQZx8SqHOt\nK361dAM6r8yAhpmn5QS7Nh9p2jYobyLzaQXp3RVuqIDehmNKaza9OyZMiHp6jiNW\n3/WnAoIBAQCTeK3ZRc03A4gPDd7wGbxbyF7o6+KiOUHKtK+OOeWnRI7UPEKulVd2\nKC5CbYDEJykC20MPxka9nNerTYHOKJ+tB1L5AXNelsxSpCw2aVRQbKb1KKvtQOJT\nid2Wvc7DsL7+3DssxxWJlcJT1IGAmj7Z+IUIByOwLZLjTJ5xt859uh9Tib9pVQnR\nk3Jdo6DVH9tmqM5dh8dNbmcZqz1CnNl08oU5b7PwmMII0MJ60VyJVU25f105p7Kk\nEbOdn59Az+rjvSmbKcD+pmhvvaSARpuCubNMmj76wG3OVf9A3eE+IUVNe0cKfNu7\ng+QST2PK/YJoK0lJ+1tQojdATxwNHgO1AoIBAHjpZ9Px6L3Ek6O3ZxRfuDTkmoVB\nAPmvm4IcvajB0BPd1mdcf4sWYmGhNqf+xajwsKB8jIkd4LYfINjfIZOdnYgq0oQY\ncM7K4+b8gkLKsIV2gFI4b95TYcbmanxdTDdbERGTJPsIBajXO5XapAswAJfllSDH\npUvLb2CUgLhMhR9SFZAjyRo0HV++jMqxWJKzhlTOkoPzBY5xAleft+hzVch3WuvP\nzZn/NrpzTEpslV7dZ05Wuh8E+vJMoQNCReGlmAwNlrt/vxDuyv6ibNPxBHax82On\nyo6EP59d7OE0951FruUIITUgzKG2jIqeR/e5Yb0LJusXnj4RPuvfRULFD00=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "internal/testing/tls/client.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEGjCCAgKgAwIBAgIRAPt0KCF12GYbCoUj7klj5/AwDQYJKoZIhvcNAQELBQAw\nDTELMAkGA1UEAxMCY2EwHhcNMTcwODI1MTU0NTUzWhcNMjcwODI1MTU0NTUyWjAR\nMQ8wDQYDVQQDEwZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC1JxEPOsZyf8883tlPBEajotyECtrYMZ48FsYEmQ1XvKPoH3eb7+Ev7tRBVAup\nyB87XQ5PU/oNqAtpo/6WD5JGnKSVs+EAMESXmzEF04T9hK8uSd0cVEEkd0tbVNpX\nbWMbivHnx5Vp8o2mIx0sVrgGsJW3t+cYbNTp3bOTdmz7LKbiQN2Ix0wH+2/sPXYa\ncZsgbI0Ydo9KnqykPm2TqBYCL1kzhGlvaAotjdDIm7OgnaGCFe4CbK4QZB4uFw3e\nM+PmLG0TsaH9CT/ZRrE21iBfg0rqgpKZKMcqYQftXdLqlikuV69F+0L84xRfeVqB\n1E4j0RwBGWW8EwY4WHK3VE25AgMBAAGjcTBvMA4GA1UdDwEB/wQEAwIDuDAdBgNV\nHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFMs+/QF/ZJaRu8Wv\nvcaMC7jGmPwxMB8GA1UdIwQYMBaAFM0FLuuYBwuAJ+tocRlu+xUuOw6FMA0GCSqG\nSIb3DQEBCwUAA4ICAQB0gPDs86Rjy/O2+l8QyaYfwmmyTMPjNVqKgVP1uuikWErN\n5hTAlwtDI9FuiMFBqeBdeiT8IQvzEEQPYu69kAX2XYBWBMWDa85co5fJztAzV7Yz\nVL1byhxd2jgM14usyx6PbzkhYKBNesujHj7wQ0ur+85Kp66HqKCuNCvbj0zv58PH\nRWkojRPgyTpbLdXXCOWJXp62XfddL1Bf7NJCW5QTyHoHoOsOeoPajb4OOmQehzqv\nb9FPAHVFBPrU53Xn1CURAzTeBQ2T/OK4nx6EdQgxP9+VVurBQ9N2YBM9VEJmfQK8\nLf5/+EJHe5ctOy1Xm4A3A52zZ1kGjfvWUtGJUSnJ5ahhMm6Dx63wk7oYNCTXnPup\naVtINWygNlS/dQsWubHaWSFwB9/QwK074+H/4EpDq9HCMMl8yPMktOmv69Hyaju3\nMvGshz/DLNZf9oYpO+lbU8X124Z6XifEztMiBlUPW75KYv9X4CTbKTdE45QaRMiO\nZXcH4HE1/iQ9IOGg7CplMlMcHg+lQ7CpXQjtUUjCEpkj8BAs8YLDodLnjigs56/8\n75+3cVZu0+dY+9eNt/EIqzjaFwEx72hbLyhk2IeS++7QloJDhYqlq+ng54Ul957A\n8e7TsSVHlLZVGXw8yqjyxxOwWaFx6mvFzGrcBtvCgK2HwEiYQ9qXJ5VPkdo42w==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "internal/testing/tls/client.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICVjCCAT4CAQAwETEPMA0GA1UEAxMGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAtScRDzrGcn/PPN7ZTwRGo6LchAra2DGePBbGBJkNV7yj\n6B93m+/hL+7UQVQLqcgfO10OT1P6DagLaaP+lg+SRpyklbPhADBEl5sxBdOE/YSv\nLkndHFRBJHdLW1TaV21jG4rx58eVafKNpiMdLFa4BrCVt7fnGGzU6d2zk3Zs+yym\n4kDdiMdMB/tv7D12GnGbIGyNGHaPSp6spD5tk6gWAi9ZM4Rpb2gKLY3QyJuzoJ2h\nghXuAmyuEGQeLhcN3jPj5ixtE7Gh/Qk/2UaxNtYgX4NK6oKSmSjHKmEH7V3S6pYp\nLlevRftC/OMUX3lagdROI9EcARllvBMGOFhyt1RNuQIDAQABoAAwDQYJKoZIhvcN\nAQELBQADggEBAFnxmVCuM3J2bt79JcFOqsXNsvGUUT+4kMl3BcfSWaf1pviuhiXT\nfsKkk1WItvaRQvpNdQoFQDjKHGcd6+0vCz+Q6Nni2Vniz3+f3+h/rOzWGA656Xxm\nlgByryixnngWZBNLZkLWCz/H1MAlQYu8PTdy0N+JBF/E5SAGfaaXtfTC6tjnnZIm\n3rjxC7C3EyELpo3X3erTcHpnFvhl6ZSkViVWfhOjxU0n+TGGohczesbHZc8YC37y\nJrkrnRDrNKnca1XkXWUnbV6rH8cVDnJ0Fvs54RI686Tlv+LxW2xa3D2+pV7Koduj\nRu+PguJ3BbaRpieGTxHg7hH/1T5HsZnD2E0=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "internal/testing/tls/client.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAtScRDzrGcn/PPN7ZTwRGo6LchAra2DGePBbGBJkNV7yj6B93\nm+/hL+7UQVQLqcgfO10OT1P6DagLaaP+lg+SRpyklbPhADBEl5sxBdOE/YSvLknd\nHFRBJHdLW1TaV21jG4rx58eVafKNpiMdLFa4BrCVt7fnGGzU6d2zk3Zs+yym4kDd\niMdMB/tv7D12GnGbIGyNGHaPSp6spD5tk6gWAi9ZM4Rpb2gKLY3QyJuzoJ2hghXu\nAmyuEGQeLhcN3jPj5ixtE7Gh/Qk/2UaxNtYgX4NK6oKSmSjHKmEH7V3S6pYpLlev\nRftC/OMUX3lagdROI9EcARllvBMGOFhyt1RNuQIDAQABAoIBACE5jRNyADu33VaY\nuNqZOiuBD1jYdNL6Jr92ndLyD1RsMNO+Eb3z/SVBdISW2ZzGK5RDuQArss0WaSFz\nBpqXOIji6fzbBQV31NzJhfA/n0CwOUEQIxGzEk+R4axan8ExOuAuV7ffDzRjXD+A\naTVcolv3vz326Ne9/j72fp0pN0vJ0b8mk1xmDWNOHhfoWmIGrUZAjqAkA1kh5aLk\nQ8MCjVyjT+KYDkFT6NscFVxKslDVhb2OFC7oy+9l/hBru12bsi9eBdYpPT9E1cpR\nU9N8+9XS9d7wgVnmVh8CIrFToLsvSrwD8SG0Indot0C6dsy0PkoMUekVxvM5/wXm\nYLZnZEECgYEA28JfZxFxO+bjd+zBC+yrusHCVfZK6MZZV0u/V82Bn+gftwgxagI7\nq2h7m56WBtq9MeLlhicZ+em4BtA3yHwVGhqr+d5CaXjkYype1EditqGx8JYRUwlG\n9z7W6jCEDJsjrzvGgua1qsyCZFePG78i4rLumK7UVvWEK/OaiZu3Pr0CgYEA0wbT\n3STBc4THLXR8nx39b6RP+qH8jO9UcD/V7Hi/SWTCcGB8IIlTV2EJVndKHPregcmI\ndN61uH3d+3UtI/WxEPMcfrSlEwVrjF2m5szYjLIAeFynw7pQY95qIhgKi6OH0Yn6\n9OCmieL0x1ez5zOXiv+GVjmn9tDCxXvqfsW9CK0CgYEApOd0Y4kpKUQWyPT135bX\nPqsKwyqwB4BfpiwHB0IE1ROASP5y5hOK5xLePmaAOeCGPBsBFOveiDQjjalNUroZ\ns570EeoAd9jpuKggxLZUkqs/NUPG+EJr6DhVWSLS1ArOej4mti+dfu87oUQ69R02\ndlrCw/vdBuvxJHIGMuCQXxkCgYEAinSFVygBgQCSCkHObjuoB7LgAsp7QCDa3tcT\nTZafstDYPhEf/9z6AG+bR872onL6wF7xF/Tzd7ulhJGJ73kJFtzbSkrNr+AzgyID\nGpU2U4GKi24HaIT6r7vDGOF7Mck2mIWWUUqAGiH9hjkFwWD5QeqLQlGL4YVw9U9r\nOIgWkfUCgYAoMub8wHJe9zhq7UCBCa4zPXqWVQcN7ANjL6fESvyK+A6TfwL5j782\nCNIVl8ewU9TthEY+AdbJeiAevz+pIazSqi0ln1JKO5YpCOC1Y0UxcEpghplBTlPU\nyoQyTJP81iLynwOM4pC322ptJISXIndL7Ig/9AoRZtAJV4Ot6z9b1Q==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "internal/testing/tls/expired.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEODCCAiCgAwIBAgIQP6vqM4GEKFdwrZvr/s/5KTANBgkqhkiG9w0BAQsFADAN\nMQswCQYDVQQDEwJjYTAeFw0xNzA4MjUxNTQ1NThaFw0xNzA4MjUxNTQ1NThaMBIx\nEDAOBgNVBAMTB2V4cGlyZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQDDlGu08Ye7IGMnh6zyiVbW64btT2BUfj4fZlyTraLNzVPfULOXezgJRAwAXEnY\neYA5j6e7iAO/yGXq+Pxh9rbGlXTVC2BsPIfKzTOcCTxgO4tfrdfSWFRDGgcFtpAA\n/4b8fYQ8BMw7khVpOqj9n8TyqsN8DGyZTyNhQuc23zmmY1eVYtDIwVvVBp8/YzDT\n3RzO+FyoRpKgdngX6kdqqZ+vaPxP078JM5zAMnysjSkpQCdAdu4EupvA5xRtOrDt\njJoAi4iUf4sG20RiF9XFpiDkyUiOa0ysFC4wrWbXkaT1xHZJJPwwJZ8VyGDRFlFa\nPOEaM9dtLPKtEV2af3G9Q9s/AgMBAAGjgY4wgYswDgYDVR0PAQH/BAQDAgO4MB0G\nA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUNX8nKbJ+G0bv\nk88bTHkULUX1U5AwHwYDVR0jBBgwFoAUzQUu65gHC4An62hxGW77FS47DoUwGgYD\nVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4ICAQBHZsxC\nGGOUhNi1b0xUYmKwFaRP641R69NydzVMma+2rpLZtL8MQq7lWAgWGE8EiKfTewk8\n2Gm+rNwReBRkw56zka8WtLomO4GKKJHvfRsbSempsp9MShc3vcsEtzXGjDQi9c4Y\nBJjWbh666jCKPRJ09RewcTyTYPtZ96lVeYRj9HhXF1EGnURG5sM/zCantlZXxInk\n1FIOKDuawMbgf3GR6n/bM2oybYCs4Skv3yOp8x3lyhlJ/zmSPGVVkPa7Vki5+sPh\n/eIZJ9mzEXsu7IXfg1isZ01iB28+6UgpZt/3017PvgopiYrL0gMKO3EL8UqJDweN\nGzJh5VYOqsjTbsrYYsWGqM66vJmfPvqDyYA1jj/EH+q6TBz0s99rzF44bBKKie6T\n0KrZT7ohXQ18Vhl2UpqahMqxMeAW/QP5asGuzS5EalUir5mNcOljtq1NnfmYvqok\naDC7rURoANwZ2L1oKN0oB6jn36g791pFdKycw+HdsrGgQGPOMak9P32z5kmDsODH\n6aVrfio2WwSGg+1CIBH0QsclHAUgLsAXjyGbRWxPsvMcsLB6OOymuTP2UfriT05X\nduabvbEP5IIRehVUfrP5uvoo29xnoPL4UB0C8gwr21IDn7Zew5/ALekN+s6IgsfL\n9yKTGSD+6Ir3NqBgL8T+uhOAekyLE5S4CcwCHw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "internal/testing/tls/expired.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIChDCCAWwCAQAwEjEQMA4GA1UEAxMHZXhwaXJlZDCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggEBAMOUa7Txh7sgYyeHrPKJVtbrhu1PYFR+Ph9mXJOtos3N\nU99Qs5d7OAlEDABcSdh5gDmPp7uIA7/IZer4/GH2tsaVdNULYGw8h8rNM5wJPGA7\ni1+t19JYVEMaBwW2kAD/hvx9hDwEzDuSFWk6qP2fxPKqw3wMbJlPI2FC5zbfOaZj\nV5Vi0MjBW9UGnz9jMNPdHM74XKhGkqB2eBfqR2qpn69o/E/TvwkznMAyfKyNKSlA\nJ0B27gS6m8DnFG06sO2MmgCLiJR/iwbbRGIX1cWmIOTJSI5rTKwULjCtZteRpPXE\ndkkk/DAlnxXIYNEWUVo84Roz120s8q0RXZp/cb1D2z8CAwEAAaAtMCsGCSqGSIb3\nDQEJDjEeMBwwGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEB\nCwUAA4IBAQA0GTIB6PxgmHBa234rSYqIew4qRfY9MeUkVQEFRDwodqxa+LWvZx2T\n5JmTZYyXBfQwnSye18fDjQuHv1KaI7bnJuMRv9KU8L6ynLkAqrFWRSBjt3eCum01\nIWZFyWu+dUN2c12C79zUQh8uZc15oDNFrD8ivBbGRpWvR1CSG/DH52kJ8nckgEsh\nSwxbzSPOXBgLH6ke5z9QGHJMK2rhRFutFOecAId7VBiWqfZJv15+P2ZcyJNQGThs\nV2sT974YGFkc0Y1MWlCgi3XhyQzIzqV1tEILGSTDE8biuKlm1nX3H0K8oI3hoGcM\nCBjE3HQ1/rs10IY/WvkpIfAU71D/ExMc\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "internal/testing/tls/expired.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAw5RrtPGHuyBjJ4es8olW1uuG7U9gVH4+H2Zck62izc1T31Cz\nl3s4CUQMAFxJ2HmAOY+nu4gDv8hl6vj8Yfa2xpV01QtgbDyHys0znAk8YDuLX63X\n0lhUQxoHBbaQAP+G/H2EPATMO5IVaTqo/Z/E8qrDfAxsmU8jYULnNt85pmNXlWLQ\nyMFb1QafP2Mw090czvhcqEaSoHZ4F+pHaqmfr2j8T9O/CTOcwDJ8rI0pKUAnQHbu\nBLqbwOcUbTqw7YyaAIuIlH+LBttEYhfVxaYg5MlIjmtMrBQuMK1m15Gk9cR2SST8\nMCWfFchg0RZRWjzhGjPXbSzyrRFdmn9xvUPbPwIDAQABAoIBADa1IZuvpCP330SD\ncyE0wZHEuC1RcsSvu3jVDThR7aRbtwZUcKgC053j5ueC6TUgZ3mycVzHoyTWTYv4\nscBFXsMVs2SUlhgwpltYIwOWocjZXxcYbbJs+sT6VtSGSKm+0Gd4RLD1NpvDNTIG\nMpcfRdwLYDsmzonj1SWzrTFwJ5Qe33cHSR8Oi9OarrxzIHWLDNgp8x+5j2l0T3AG\nRPQMXj7jaK6qEdHGD6geg+ButeyjYyxu64l8Ooax11/jfdYHcK+BmmtmP3Bd/6FQ\nDCtrXTBv5Txl/T6D/6OsyabSlwGWFDNEAaS47hKCFiYJBR+Az6r7d1QFBIzuqF0+\nT7EwaKECgYEA/tUMcTXnBRquAtQHW4cSvHH2qoZAhGjXCmj/pk4UCOZDT1DP/K5u\nm7tOZB4RJBmzg0a1QS89aJ6lhY0Oy1Y9MZTM6ofkBKd8+P0Rgo04UDVeQ8sA9c8x\n4bFOrOEqe7NoK7Mwxyn5NOmNi/wxy+tpiMH4Jt3y4TLDEZPg0Tsfu90CgYEAxHnc\nfN3gKeY3SV6nHi+johBirSNazhr4n0fx/TCsy02e5Rrn0rksGVfPij5Y2g8HQLel\nhdbG7tVyA7UtGgVjwiT+4j0VXmWpCIqfPsibRoy29oPqO4p5pULsz+ueb4biRW2Q\ntdLqS4OldM8YUFwkS7k3Pp13SAY+Ir9rHL6wv8sCgYEA/AZaYtCrZMnpFNT7XdLt\nfb+78xQJVKqXGi2TwLbxa4fHQ/cpa75bl9scAToXO7vLZOaWNhxxQDm+e6Fw4zqs\nFJAURVMV+GBo4ZrvKU1fRzwwuR1ZGsHKlGoV5DZgHKznNmjmseJaG7FsEujdms58\ntgsXz+Cr53qbn5O/wU4W6WUCgYBA16sB9sPtcBIc/8UNvFE3wkqes4VbciFNiBQA\nKJlOe26OVCPgMsawEn/nMw5l4QHWxQU2t5xt5Dm9qYSaCt9Sip0oE1rDDbAMpptJ\nwDEmxnf3wa+DOP9OoFjBghSG4DA7E57nsxUqGOd5NoPiuZYs+5KU8qkUNyM4mo4C\nLZjtowKBgCWuDPr8MBL1S3ym55VTNUWcuMKUHZkMg0HjRpi8ABXeARoM5FAWykbk\nhFnI/Waj6EHNGoVVpKttJldP+uRRA+S3wZYKH4gRQcsWI/BVdiBkZgoTG7cwnixs\nCqMQ4p6In1Q/6EafPqkVQOY1abNKQ7ZGbhksB/fVbf37XfOnDj3H\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "internal/testing/tls/other.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEODCCAiCgAwIBAgIRAMcIOg3oRf7n7mR1BUqNnlcwDQYJKoZIhvcNAQELBQAw\nDTELMAkGA1UEAxMCY2EwHhcNMTcwODI1MTU0NTUzWhcNMjcwODI1MTU0NTUyWjAQ\nMQ4wDAYDVQQDEwVvdGhlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAM4lPXHK6YV7GFMeSsHKVq33LDn8Zt0IkY5pGNCuuksPa3vYBFRkwTrrobKQDnmw\njLeaQISyxyelhT4aIU34OQpGjOmCzceYPG0uOcecq7noVvbgw0UeQHkEL0p4NOlb\n9LMNVhKTgQUhZC5mYSIpXNxlE/LlGmqrFX/8peSaJ1oAkkB5FYN0gAbYhd8kpJX3\n9Nr2A+f88oicSX5K0L73LUFUrxoTcrFP1pWnFgg28vLvvzrW/VZtq4qJPl3GTLbM\nxHOu000LaHK8TgIJMCQUalh2q2nz+Htmtv+g/b27YEMGcDBW1qBX9oMKgoM/Z1Hv\nzjVhAm9I649heDUguFXCaoMCAwEAAaOBjzCBjDAOBgNVHQ8BAf8EBAMCA7gwHQYD\nVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBSUR1DE/STLJwf2\nhW4qXPrOSLqlmDAfBgNVHSMEGDAWgBTNBS7rmAcLgCfraHEZbvsVLjsOhTAbBgNV\nHREEFDASggpmb29iYXIuY29thwQBAgMEMA0GCSqGSIb3DQEBCwUAA4ICAQBG87BU\nUuDuUCnvcwUNbga8fhe1PR6z2jueKQiI10SxkYG/g6PMQGGYDNO9DZFKu9l/TMTf\nLnuEozN+Csig+wC+sc32/MdR35XTmkKtNhL0cVgvKP0Q6zNk97/QJErLtDpYb9VR\nGm2Ky6FGDp+/EEUvQUKRpGBmWIqOtjxqQu8lLoJlt/TPhxJ0lGDd3c8WwaVFYTbS\nisBKdHpS2hkn/O1Yd4QtNk06pCpUDQuPumUOBoa+dK3y0jZ+e34h1NoR5EZvfRJy\np3n7CLD2eZSNc4oWKb67X0RDao5LD0b51crjgsFYHhCTS+Mgh0YkgukQZ8uBKpUJ\nIBhz2Nr1QXykrJUhal9MrKukjczEikGxzK1VsDgxYY1kLBURhM9/TfvICmcAaQqv\nMF9B78lnoJiPZZxD+a5N9MawzN6QBqX8GpvhZoAnj6iAuNwKJVyENpZqravxeq2o\nbuNjgQ+SmfqxQDfMD3lu95yidqD7bcDipJsXEPQzdBjZ1JOJCGi2eiAm50e6bq94\nCMKmmRjtIbF1hJnHeEFPvXqdPpqcyEvcaDebph/f+54wubTgwFI3VMnhhlv2EPIe\nrwcbZV3kNpUZWXAZVzYlQcbK+9US8PocOUzmqzA7ZZRO+rCWNxahHjdgrMK3fG6r\nWudSHHXawj3dkPeWrQde6SILSK/myhGLdjg1fg==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "internal/testing/tls/other.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICgzCCAWsCAQAwEDEOMAwGA1UEAxMFb3RoZXIwggEiMA0GCSqGSIb3DQEBAQUA\nA4IBDwAwggEKAoIBAQDOJT1xyumFexhTHkrBylat9yw5/GbdCJGOaRjQrrpLD2t7\n2ARUZME666GykA55sIy3mkCEsscnpYU+GiFN+DkKRozpgs3HmDxtLjnHnKu56Fb2\n4MNFHkB5BC9KeDTpW/SzDVYSk4EFIWQuZmEiKVzcZRPy5RpqqxV//KXkmidaAJJA\neRWDdIAG2IXfJKSV9/Ta9gPn/PKInEl+StC+9y1BVK8aE3KxT9aVpxYINvLy7786\n1v1WbauKiT5dxky2zMRzrtNNC2hyvE4CCTAkFGpYdqtp8/h7Zrb/oP29u2BDBnAw\nVtagV/aDCoKDP2dR7841YQJvSOuPYXg1ILhVwmqDAgMBAAGgLjAsBgkqhkiG9w0B\nCQ4xHzAdMBsGA1UdEQQUMBKCCmZvb2Jhci5jb22HBAECAwQwDQYJKoZIhvcNAQEL\nBQADggEBABuaL2t2Zcv9R72OH93EpIzgExL37odLUiIjTIIykK2TT/gb1LtnE1WK\nTHdqaLpnPot9IqBofppfkXPMrG7vavJoPlAp0lU2FHYIz64PHou8lj9yiXezDKDn\nJia3TOxCu5VTRnYT7Ypt8kSull/jlyBQgTP+P0YXwoYAXJteQr9O6yD75yWOAx3A\nf/oQS77xoe0jdU4RkEMQRQQUIiaNyH8Bx4CeETmPoJDzEiIvnC5xDoySks1VJK7b\nw11IANF7zO5UWtYv/i3+Wh5XLMJ0GIIVTpuGkeVaZCjB8goiBJXoaHOKsO5ygJo5\nN+nxwiDTwUIZM+dU88mtCh0dvJeMMfA=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "internal/testing/tls/other.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAziU9ccrphXsYUx5KwcpWrfcsOfxm3QiRjmkY0K66Sw9re9gE\nVGTBOuuhspAOebCMt5pAhLLHJ6WFPhohTfg5CkaM6YLNx5g8bS45x5yruehW9uDD\nRR5AeQQvSng06Vv0sw1WEpOBBSFkLmZhIilc3GUT8uUaaqsVf/yl5JonWgCSQHkV\ng3SABtiF3ySklff02vYD5/zyiJxJfkrQvvctQVSvGhNysU/WlacWCDby8u+/Otb9\nVm2riok+XcZMtszEc67TTQtocrxOAgkwJBRqWHarafP4e2a2/6D9vbtgQwZwMFbW\noFf2gwqCgz9nUe/ONWECb0jrj2F4NSC4VcJqgwIDAQABAoIBAQCmygix5gwU/KiM\nr6iqrOx+6sq0y9vqIIGsaKo0RfriukIrvHacVbzl0DpPADFGEit4beyfsQpjsI9i\n1L93l0uHXderIzMdt7XEXK9RKxjiXPLn4qj7ZmOhxloA9ctRuB3/NN4cP44XOZIV\n3K3gdvj0NS/zyZwbC/tkR2Vt1a/bJ8DFfaFrSdk/btpVY2BH/uWjMls1BYIs3tEk\nnroJYb+fyliC+n/QXrLQAPTVLfI3jyVjRYpW5b6mZHQk4SGDde1XbtqRCp/f6PLu\nH8FQumd+SfrjgTwefphSWCW2H/aMNsZpL9NK/hmglKg3OcGKhwQswdmL9rDVFQmy\nAqxXwxk5AoGBAPPmijvwg6bKwqeSCdhoYjQVNXdTbdda3arjC/G/vxhsVntXluOX\nwGF2jInAu+sAShBFdhG4JlL+itlpA/aDDIMUxsF+YnhTOX5pofL8Nr4sstY6wjBf\n4jVHQKLnaOm/mVpHqWABVv/M085XK7HHZR9e9y32ry1geRSiPF/E5I03AoGBANhf\nOPL5WWO7TOchWci6qRcv0kZ47iSE1JxSGnXr+KIIBPIAgrwhYkqgrl6noSe3x1qB\ntP7ZvmFWewxmo3mN2OwPTsxAhnjQK4D1PGJcsvf2A3f1uiwG+emqWpcMshTQnjda\nHi2krfMaHwErE+dEbZiilLDRYAMAWlQodXnhllMVAoGAKdDIumYN7DavENO05Glh\nDNTmCcM//cASaQ3sKlJZjPJmEVd/Ax4tWYhdp/BnR28RQ6DlETylNW12mLesekMV\njhOtz9a/QynhnY62uVYMfKZlMt14FZsayU+iAUvzbL/wps3KeC9CnzCaz7GaSCyL\nZcl+T18PwZPcrnDyMOks1hkCgYEArrQUE3tpxbER4v12tTCiHuqp6eTyw+HMmXth\nih1B3/KBq7Tl2mlKJ9+daygGYz9sY5OfRLcjlQxyxgyJqjfyEog5o4nmCd5rgfCB\nFRqsFrI5Er8B11K6rwSxqIzDrTLUzPSisU/qdAN/TT4vD+icZUXAsRQdZc7/IDya\nvhJ7ghECgYEAiZ6v380l2jcEk+tm4UYZ+nXq3c2wq8mZkQcEDVpqvDGL8k/dPEq9\nxOy7PVooQHQJyC2bgTbEKs6JYvzAYQmohiq7L3y9WxDQFJeImtzuNi0s/dwP+mBh\nR6htM5JwAO3JnE9V863M6kvtLEIk7XptI5gC0kN3Thi70yT3lnU+emk=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "internal/testing/tls/server.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIENzCCAh+gAwIBAgIQSCdvhIY3KZ9w9YRcDH1oRzANBgkqhkiG9w0BAQsFADAN\nMQswCQYDVQQDEwJjYTAeFw0xNzA4MjUxNTQ1NTNaFw0yNzA4MjUxNTQ1NTJaMBEx\nDzANBgNVBAMTBnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nANlVMhHZHjRWiGqmpOse1KZTmdUaiSgl3T88+Mie17UbiLmsOfnkd3PuEnKlXRXM\nsqOg15k9xfnV6SQebpBfkcwqJVH/USjWY4C1e2vDrva+j95L+uzDMZDF9nxpnjHE\nuHT9+hnrmB3Xted0tzxRC/77Cht8Kn4gaoljbBoZsRnv0vRRUYKA2OJHJRRCHhzZ\nAN6A+lWbWyvyvd3UeOLl3oFhk4lS5fwYY6RY8W9ZTJVxetVycvro42kK2jtpqUeF\nNercfjOg8VBWYjqB3Ey1wHDpjS407TW7RWEGA+3mP8ZFsuoYr7Rs+z8LprbYEPpF\nojgvss+vMvjGkrcU8v0MR/sCAwEAAaOBjjCBizAOBgNVHQ8BAf8EBAMCA7gwHQYD\nVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRS8x8oOcBJnrrW\njiS0t3qjKee63TAfBgNVHSMEGDAWgBTNBS7rmAcLgCfraHEZbvsVLjsOhTAaBgNV\nHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQELBQADggIBAI9t4Pvn\no0cW0SrC4EFNJqUaffbqUNd3i+dBn5/EQc8EGYVY3k8E8iUMHzRDyH+/VRp3RUOH\noJDDS2uyeJ2InkC193MOpNJDQV5qf3yOmQCeVmmjcXkg1Nc+3oe8ttWiVW0ArFXS\noud6V7/6qzIz3850ypi0Zz4KLVhSGzaI8dnzPopqNEG+ZbgIwvQLqWhv2bLpqycY\n5ANECpjH6QIEp8JNjga9Sp/LspNCqMDmVswBGarySNWZ1+uflg9X30hsdCzVPgX3\nKMy0wVelT/4y893BCTo2KendGh70jaGxm8nBH7OXkeki2TI6boAvsn2Iash0HSZ4\nhPacZ4QWFEYW4jZeu3ZNTJ9tc2u2jgpAGueOcWRY75CraLid1V5t1kpGxAtX4NvX\nX56e/IlmEI6qPsaoPouQ1riAWMdRQUT1FLNPanv4vElDYXBNcFl7knuS14JDCC0M\nK6ttSb2MxJYfC6J+OJpHQd2GWU5aO2uZcgi9jRMslNwR+R94bxI+q/bjrI31JsDz\n1pVRnGRWH7cDejA2f+q7X8/uRuA8bfnqBcu0uI9YR64W0VMMLe41+iR0wt5N3yWr\n/DalWIvmUvE8LoaGDwxV9T3xq1I1dWnASX+Xmb1SQ0CnhEEooZfQYfb3ffciG6mU\nUvVC0YW1cjOgb193W/N+Dgju7/a/e+XgbsgT\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "internal/testing/tls/server.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICgzCCAWsCAQAwETEPMA0GA1UEAxMGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEA2VUyEdkeNFaIaqak6x7UplOZ1RqJKCXdPzz4yJ7XtRuI\nuaw5+eR3c+4ScqVdFcyyo6DXmT3F+dXpJB5ukF+RzColUf9RKNZjgLV7a8Ou9r6P\n3kv67MMxkMX2fGmeMcS4dP36GeuYHde153S3PFEL/vsKG3wqfiBqiWNsGhmxGe/S\n9FFRgoDY4kclFEIeHNkA3oD6VZtbK/K93dR44uXegWGTiVLl/BhjpFjxb1lMlXF6\n1XJy+ujjaQraO2mpR4U16tx+M6DxUFZiOoHcTLXAcOmNLjTtNbtFYQYD7eY/xkWy\n6hivtGz7PwumttgQ+kWiOC+yz68y+MaStxTy/QxH+wIDAQABoC0wKwYJKoZIhvcN\nAQkOMR4wHDAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQEL\nBQADggEBAA76r3SIQUER3XyGp4MOrfKGwBE7RnALcxW1XkrhID2bng2hzovrZZNO\nxutL1zqPFCxClIKUxYAXpMeY8lnS6H8I6FoM6ALCZbK7q9rmMK198LPMo3zC6TsO\nrEP8HOF9wxaYubg8xaq8iDlaL4e418M0UPOlE75PtkDAjhY++7ZTsjPVr/9WsJpZ\nMmEZ5kSS59PZbMbyqXn5MxE0iSD0LfM+lmkIBwSvD8rjq3SQ5NKCg6CJkRRq7BVe\nbujA2pPb6ivS5pujjIxkdUoz6S0G+ewZG16kbBoygWuRVFD8xqR9Pa41KSPhpx85\n1qSrqR4zHvS3r+RS9UVIXnbh9ejW6Vg=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "internal/testing/tls/server.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA2VUyEdkeNFaIaqak6x7UplOZ1RqJKCXdPzz4yJ7XtRuIuaw5\n+eR3c+4ScqVdFcyyo6DXmT3F+dXpJB5ukF+RzColUf9RKNZjgLV7a8Ou9r6P3kv6\n7MMxkMX2fGmeMcS4dP36GeuYHde153S3PFEL/vsKG3wqfiBqiWNsGhmxGe/S9FFR\ngoDY4kclFEIeHNkA3oD6VZtbK/K93dR44uXegWGTiVLl/BhjpFjxb1lMlXF61XJy\n+ujjaQraO2mpR4U16tx+M6DxUFZiOoHcTLXAcOmNLjTtNbtFYQYD7eY/xkWy6hiv\ntGz7PwumttgQ+kWiOC+yz68y+MaStxTy/QxH+wIDAQABAoIBAEzhDVAw/LVI8wK/\nJlGh21lm82Dl/SS9mDE5kUvunKGNNuVvXibewb65tb7mbjI68epeCEZGCtVg7RMA\nzN23YOzW79K8vWnzxMkP6bPqSecw69WYDRBZ0BvFW3cRKYuzagjAmws2Qt4zoz5Y\nFEV66gJtrVqhpqptLyKgj+n/sp1YiHMjkcJF5PGnAPudYxpiDHiPFked7vZtigyq\neE2GCVbaOmRGPMe28JzRmOuFqEN9GccRjlq+AuzYe14lWKa6fZLVke78luByF//M\nRA+gGoKfHq859wACOEGbqShMWxC++y1HJ2Mu4adUlikzVq8rboinaAxzv56kc1n6\nEDc/qPECgYEA2x+qxHKSJnQuItZp0CpsDk51yr1fJcWtdEbcgmomiwMwl/Nk0OAB\nrplrW5gezyVRfMDsoqAnmBS301JEL/7QuEWvedTpptKC503Z+mCINPTZAaJfa7Jb\nKUCSQHO3hThfOXFMkb9mcJEVVNqtobnK61tC+JNOSd00Z9TdLQAbcokCgYEA/ehf\nVq3md8bkQFlnMtFib8SIr8IP5j4JecFSeqa/OnBFfHJr0trUhoMO04uaQOl6lTT7\n9Ca36WfJsv3EGkMHEFeceOsPswf65bI/qgxgUS7Qmu+NZTjvXL0gCpeHLPzEaLTV\nCDXoo+YAJzzv9yWwVEvIIru5SJPVud6Gap5L1WMCgYAjfO91PXD6FVrbfYpJknVJ\no99j5GOihG9hI5DW9kYjwXJ/SYYMZhsfoe1HOk3TEqIt6Djq5bFD6icTbIFqnIRF\nM9QFkTv+Lp3QxEUHTdcBbJ4wq5F0qcAl4DVPhu4z/zs83GKgQDVhCb5AreHtDWAV\n2gPwqjrFr7OrFUh0302SsQKBgQCOHKZn9HNfLOIKJj/9kHYhCoZaoSqW+rgA/rQ0\nU+oKQlaR/dTdsn9rPiVpP+S5WjSzGHHAyH79U4rv9Nryu/tTKUY5447o7JmAQJEj\nk0PBjItTfKrOMdy/MlehtggBpQQlerkVnF62hYAmdhP1Z5HWzIea8SkWNzBTlPn0\n6N6W8wKBgQCDBPXqGii/ur5IBQ+RASIL78RgIGH49XYcq7IrYFOuztGBbqf4qorR\nBTl/mWFFHr+fAcLHlC/qTSBBflGClElqcg+j+92RAI6HbCFemdSbnsv5FCmciL6e\n09x+oprKq3/WAVARUBif3RtDq92SFwSBfrx8JFhGIa9kUcsDFSBaDQ==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "internal/testing/tls/wrong-ca.crl",
    "content": "-----BEGIN X509 CRL-----\nMIICgjBsAgEBMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMTCHdyb25nLWNhFw0x\nNzA4MjUxNTQ1NTdaFw0yNzA4MjUxNTQ1NTdaMACgIzAhMB8GA1UdIwQYMBaAFGM6\n559SRpX6VJlKbIObtrLXvc1rMA0GCSqGSIb3DQEBCwUAA4ICAQA+6zsHCq9YRZ+a\nfwsZmbGQqDUBVp5TWtDsy+qvKf/084CgTn0sR28HKEONQvX+R1CyzAaCGrkm081k\nyDUizdyGrVR8zmCc7O3ztPobfZBmQXbR0pcxwweFiELBO1exEQ5IpM4J0KOPc+CB\nAwVA227Q4oKrKyUtNQ9d3qr0/2E2HE8W0aV7Ax38MvXsUfafWk0SNPDusFiYNsTt\nv50Gmd2yBlaMzT9Dsze9wuoTvT42lpCby/NSSDYynG9Cra2y0VoIpVxvPqVzn77L\n1otkaQRatbfktaa1WVufEK81FXEyeYdM/T4bKCbB5oBKxmwwiS8ukvunc9gdrosx\n/7QIlpr3iBEu+X+GeOdGyPA+a+S566Hil38QCyMUX4fI7xjYt3ek2MI1YeNEv576\nCwEihc7NvPh5MI0OQq0nIjTcIeEGQag0eAaGwmJ+AmrZ+4bop5QavuwHVh/7sNel\nrNhFucmx+gEPeS/Ae0cp2BeXjEXHmdfwKDCT1n1Rdd5wfLeFuKlBG8NdZKwZ7HH7\nvwi+JwIBx/WJ/f1qcPlWAyF4Y/HUJIRRNSXJOUQCWhCvfGtLQ4xs7uo4ViE8CtyO\nRE/3xHrX9UwJUymw50Efj3PpOxWPvJ9B7A+8ED1kJR29HQz5gVZhEcW6nDTntwDQ\n5nrvhzi93xzaTQIhEe53uz+rTGs/fw==\n-----END X509 CRL-----\n"
  },
  {
    "path": "internal/testing/tls/wrong-ca.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIE5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwh3cm9u\nZy1jYTAeFw0xNzA4MjUxNTQ1NTRaFw0yNzA4MjUxNTQ1NTdaMBMxETAPBgNVBAMT\nCHdyb25nLWNhMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzs91DHKf\nenUfIPigN6n4CsTSHGMkRpWQw9T5vIqUd3ZgMJLCoEnj0OpVlFVcb9+P2/f5RYg8\nH0dmM1iHGi5uCoDnJk7K5zaLMgxGtRy8dwZ1nHri3sZwM6Z9AyvktuSVbuElzp08\nutwB+fstR2METN5Dovp738rdx2q3zYYYcfnEXcYvxBdxVeitFgrXauRDxuqMio+5\n5bHVUpIWlpu8Fd3CqnMUS4N6McijIn6T2wiyALJDf9xE7edAYl4ExYVKaOyR3Ff3\n+BhiS+IEPd9AoctU9JYFpDavfaiZz77AukwwfU+W93NTTFQ+rf/ev8XzsgkFyZPw\nCCfKuet+o2/8MIxv4nwKxv6GGMFbQz1gNw3RqG5m19zppqVzp1vgMcNXSeRPFlQI\nfXYFN9BY9bvx2L2ZpTn3gsdgzDGNzYU5fGro3YzmtelNBCY3sAz/riG6+wMDHCId\n5K5NxrJBW3tTvEZQyKVZA1W22/F/Wz2LxA+4ZLhUoUuXTkJxLS75EWLkK2xK+IXv\nh4s8n8CwfhFV/De7u18Pho0XKTm2IPir1nL0WNhjy4jvBYDN/Jy4fE4QALt4oH9t\n+GITkDo0Zd/USZSkAOXgEb+Ks5F/fztI9yVp7/nhhj1KnSIrkObBaltlfEOe4vgz\n3dNFAG5kH5lyG90uffKR7h1Vo7UkYcpJ3IMCAwEAAaNFMEMwDgYDVR0PAQH/BAQD\nAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFGM6559SRpX6VJlKbIOb\ntrLXvc1rMA0GCSqGSIb3DQEBCwUAA4ICAQCIm4pRbel8V/W1OKSzJXg3F76ORIjz\nzN4mAtCX8YtFQwawBlES25Ju2IQ9XfvqM0CPO5LEe1v8ZTXeke9Vjf/XGReBCCqy\n/STzLSBHflQqvybMYH87K5h5e91Ow9T2HjyPtzS3RdyaahU/Y8/EnOTG89uJlpN3\n0k0/KXfwVKAyjrOaoTeGPM9BjDssNq2S07h5C8sCby3MpR26CIMGbFnotwTjmSww\nqkDSVd63/ZIB5/dOcOlBd1+rE3LOzYxDiZtKWu0NM+o7N0m4Y+gD4siyxRWuKslz\ncTwiwnLmzZG5BUvRT2FmzCwejp45+LjrXmUZ8hCznk68hnkilx9XLdkBL/1qyk40\nI1IUFQtkkcyznwUyKpC0z4VJZAVL8xi6KO60TOYtidxFTJxkPrWcAHvgzItao3XZ\nC4hLlNk7RD6BJ8oyMtpXFq7MHAAb8MWSLu/rSAhQHoKqlCEK4Iks9nWLmRP0OdAw\nBcXGMuTIn1jFRM0CQvg68GPFOH3FKv+cyUbjPoXvCBYiXKmxA/WX3rYvDo2paZKU\n/mDMu+EdAR3Zk/wYXl4738ujqzO88Nw2LBHLKhXytHMaSbfmWf085r0L+fqHLuVM\njlpPEi6vQum25j9tvGnp6GyO8lUDAUqk5gtYIp+D67+NG+9eBocA1ADVpeKZHBQV\nxGgCdjnoP+nDVw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "internal/testing/tls/wrong-ca.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEAzs91DHKfenUfIPigN6n4CsTSHGMkRpWQw9T5vIqUd3ZgMJLC\noEnj0OpVlFVcb9+P2/f5RYg8H0dmM1iHGi5uCoDnJk7K5zaLMgxGtRy8dwZ1nHri\n3sZwM6Z9AyvktuSVbuElzp08utwB+fstR2METN5Dovp738rdx2q3zYYYcfnEXcYv\nxBdxVeitFgrXauRDxuqMio+55bHVUpIWlpu8Fd3CqnMUS4N6McijIn6T2wiyALJD\nf9xE7edAYl4ExYVKaOyR3Ff3+BhiS+IEPd9AoctU9JYFpDavfaiZz77AukwwfU+W\n93NTTFQ+rf/ev8XzsgkFyZPwCCfKuet+o2/8MIxv4nwKxv6GGMFbQz1gNw3RqG5m\n19zppqVzp1vgMcNXSeRPFlQIfXYFN9BY9bvx2L2ZpTn3gsdgzDGNzYU5fGro3Yzm\ntelNBCY3sAz/riG6+wMDHCId5K5NxrJBW3tTvEZQyKVZA1W22/F/Wz2LxA+4ZLhU\noUuXTkJxLS75EWLkK2xK+IXvh4s8n8CwfhFV/De7u18Pho0XKTm2IPir1nL0WNhj\ny4jvBYDN/Jy4fE4QALt4oH9t+GITkDo0Zd/USZSkAOXgEb+Ks5F/fztI9yVp7/nh\nhj1KnSIrkObBaltlfEOe4vgz3dNFAG5kH5lyG90uffKR7h1Vo7UkYcpJ3IMCAwEA\nAQKCAgEAhCK25YIi9Sn5/qX8MDSP/8lream6lsKfIRBllBpy67UdlktewO0U+vmO\nPl0f13bewqu4f72gtFd5LBtHDupVcq6Tgb1cFMibvRls3/EBVYcyBA3cAHyHWejo\n/OrBkj2QYKzH7DA4iidht+fNMUxJhheI3YvvM7i5ZN2BnHYuDjyIQ2YKRN65kis8\n09WPd4Nq7qATtcBJBUJPSxd+CTJtxQbQhvlKIUla/I319WcsbwkqOhmr2PjSrbJQ\nR8lMgSs9tLZaJ4+pJsHlpBg/n4ySDg4NNMzZw+cQz1e3Fq4JE770SExe57Guqhk1\nhxTxrFP89WagZP/5oCxUcd/OJPy7At+MzLY+xDySXUqJjXO32FvSY1QCXySKxwxT\njT2uOEEUiQs3Aap94ejm6rPEaifrGLlv+a28R+6gaaJIAQ+b/U8NapkhQI4k7uZT\nIY2FeIJKbbthjYYmvlpTMbIMKMTvRrqlWWOJ7Nd8gJo8vtFT0rRbm3joLzfJy3M+\nITIUjrLPIMHkEJ+A8OaqIEG7Wy97ONevUZDKTj0oElaTgIcIuI0aPdjB5cC7R/iz\n4G0SJ62UheFrq10RX3IG7xRtyyNiF7Qy7CIJAFYYZuXknNPGUve+Dnbn/TInewSV\n96pJf3xZj0PY1sYWLmFIJYoHLGK4VLmd4zm4Tw1ewYz/7Oh/ZYECggEBAPEzseYe\nJkx6+Wz8v6hZjcYRn1+8Awdbb3mv5oW2eNHxtjX+ltYeABjUBYJsvO54ptIV5Bq1\nwojVSCAOy8z750SiRCzmm7yEr9Zc3bRoY03L/fi/pKlTxhS2zwrZIKIn/0Z4hYz+\n7UILIu23Pv0ctCi3zy09vGVvQi9VJ6KTuFWgaIDnGq5heQ0/7Ae+FfTVuqLOMUCb\n4x+9ui2r7Xlu/TPMNaiNXb7OxJ7Yw0xr2w1OiHqBuYRGMXULc24nkTYPmCbZbHcA\nrUPq/JPP2HYvjqK/4iWEkuAYzTPTXWBmD1zGD644dazCBn6QKuwICjo9F+/mI2rP\nSMwD5UmZsIrxQEMCggEBANt/nD+A/66u7zJiStbIjryMXHuVzJsMNBt66sA3zxES\nwnVjpZ0vL+qrwsFa8rHLh90LmcOCJ6u3NYb2GZidK9P/uyAjKQdjVJEvk7T7QGMX\nyQHhQBIxh7TqK0CWYJy69E+Mhmn+HwsMX8GH/tHc+wdr5K6t9RgvWgMeV5sFfGrf\nfJr9VhVRhBqxpoL1fp+A3ya+z5Bn/dcXJuBs7lqHhKCySJKCMmvjijRj4zuVHaHX\nKI47gwX1vsxerqNEKA2kBuQOuKtd+ySQ6EvdhkE4G1lDESr5NGBjTtIsLlB7UcOK\nGIFSbmbYwCgzjFU9/sz5gfrgNRFb0gd/TDR4+CCcTsECggEBAI7m/cNEoZQ2V4iG\nxlZLmH98+VuS3IiDV6xU1tLppPNdrYKX7220IIKVOx5mphjzSoK1jYt1nGfNVQoJ\nOh2cMQysxo+DoUkzo6nxIzk7j3oMHdA+WqQniffDxy66LWdlIwzxYs6CSrcSOgN0\nydDULLjjDc/T/8ZpAGFipjTgKBozCzcztM8T2NBMyt5bdE62QfkrCGsq8IlhsuhU\nMEH9y+3gUvolpyDhCATEkBC65fEgUiOir/L6U1rxCdZ9gr7wxkheELEAqabPlg1M\n2wZKbstlu+pWfV5f01OdKnlufjOM9MVXlgBgg9CAQa3NpaGTiJcNVnZ1kL+uny3X\n7IylGlkCggEATH/wO+3ArugHM78wKCVkIfCldukhk1QwgPdZA78vqtqn7XPaT6sX\nfyl3yh3hgffWlUKqx4oAO4ex3yS8jQUSNmPlmvDGJu4GlkdHqob6zM6IXuBbjTu3\n+WS3yF3gtB8wcN0gJ6bKuPYKFZBJTmk/EDoZTIwSZOhz7axQihXiY/kaG4Z5zxpG\n+Wq7Bt96zyqCG6Xa/5BO1v0ZrpQoimK65ardQjqgShvWmiXKF4UD+9jaKKAzLQuW\nAPJq2Toy33YwdKFw2UD6+6aJX4+IcAiW94g5XonWKFXULcn6JlCkkYr6uW+6TJv0\ndM5qdXcS6+t10rL7q94dmEFUlOEoUW1IwQKCAQA4687RyeOp0wS21ZFpRwAhuWkQ\nZxPNeOG/lxERYD/rj3cE/zSdqun+Z2HwD3tndjbjOZhw5XoIfsRl7PsOGx5ngrmI\nfdYE8n25myO1TQADblD79/kypYauXLbquJwXRNhGqzJPBNlN5rga0OzbK3YH+oG0\nsndBVW3UIr071Zs3dO45+EJKbgKU2sYgi7yhMaSVkZxey3BteRBhqzqWlgUfqMwK\nNbj1vuE/Ghso0dbZiYDX9IxrS7BT4ddZ0Wm24KIj1WlXNKv/zZhqktlh+GM2pRlx\nDQyYdp4njnkFpRuDSMSAW5V3/zyFnsZpH4ToT+MQwKFe0p7T00evdPdojRST\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "internal/testing/tls/wrong-client.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEJTCCAg2gAwIBAgIQINVcdjlrWhsKxwcXL4vbUDANBgkqhkiG9w0BAQsFADAT\nMREwDwYDVQQDEwh3cm9uZy1jYTAeFw0xNzA4MjUxNTQ1NThaFw0yNzA4MjUxNTQ1\nNTZaMBcxFTATBgNVBAMTDHdyb25nLWNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBANHAXfyT8vuNKc3FeedZH3865bF5PLbRs2R8CaUOdQj/HTM5\nxSsb5Tr3x6IkWK+5SwtnGdaLY7GktSktXyUNf2uZflXHCLAqiqcBpLNO9mcFAACz\npRb7C18ZZ6d9b7UtPA5oK1Vt45iUzI+mdCC0BtRTWeyKdtTF4muD2TtF7RMQwnjf\nRGV1EkfQ3sKpX3P7daiA/W116NlESpX3J/VAzoQu+3BrDeXrqgEbNVl+/NN/7uA0\nRY0HxE75RNhL9yuz1VFP4/NaFOdWN3pSMKwcmiIeNC5n0eyW/fodQpOT/dcscfbP\n3a0XeoZkfB8nuVcGkmtUkuw0jPy+R64vnQvlugsCAwEAAaNxMG8wDgYDVR0PAQH/\nBAQDAgO4MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQU\n0pX6qc7YwiehN5AYh+p4JNkFEXwwHwYDVR0jBBgwFoAUYzrnn1JGlfpUmUpsg5u2\nste9zWswDQYJKoZIhvcNAQELBQADggIBALH5pFNhbnro2vFE+8RqbRqOZZNoyKqL\nINY/e0MNPmhp4CE2BQcrxgFcRgJmyh4lOP8gmIHT8q/9kOvYqMmfU1vbVF/XLFsa\nNJxkQSX9uilV1LDykyRbwlI4McjdTW7rLEkW8YrZueMXnDYQHGx9L2qYWgXzA5yA\nMfsgq3pr39sDVDfYg1H+0daA3nIw+OWDsWjORXvzo5TQzjUXLhREp6WuuRKBT1+p\nVHGAnUcwDEb6L1bWEloG9ogXJfsXuCUxF+/II1RYSKiAmjge1nDOM2USfIKfD5nz\ntLJn0pn0B5dyceJTgOK6dwCXwn0Gc99qVzBSSHtPe+abSuY5dNoIwtL4R4rDE4U+\n+y2vQwzum+GhHn/ZEDuYT/0+IDqkVxeWBiZ5IFEkRpBEFpmEJOdKWaSrIQWMpIjf\nFIlxY3VzUD8H5M65kMSRKXbRJ1zSHMcIFKK2R98SPuYnYmgc4kOh49WkEr6dj2B1\n0QNZxPg70HP3qWgCxf8F5Mxg5YOtz7gN6N3AutrlYV0KB/OT0h4lhtvW3inxRgID\niAHw0A4X/1qbFUeSUpINZaVQFtBh6fT/JfYDDTFFBcoqrLOZpFaS7r6FbGP3FDS4\nv9MqsYOSA6LrOHMdRop+eDV718iGDcUVtIItRZZV0s4UTo5q0JpGBtVPo/R8ui19\neaGxvLJT1Gd+\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "internal/testing/tls/wrong-client.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICXDCCAUQCAQAwFzEVMBMGA1UEAxMMd3JvbmctY2xpZW50MIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0cBd/JPy+40pzcV551kffzrlsXk8ttGzZHwJ\npQ51CP8dMznFKxvlOvfHoiRYr7lLC2cZ1otjsaS1KS1fJQ1/a5l+VccIsCqKpwGk\ns072ZwUAALOlFvsLXxlnp31vtS08DmgrVW3jmJTMj6Z0ILQG1FNZ7Ip21MXia4PZ\nO0XtExDCeN9EZXUSR9Dewqlfc/t1qID9bXXo2URKlfcn9UDOhC77cGsN5euqARs1\nWX7803/u4DRFjQfETvlE2Ev3K7PVUU/j81oU51Y3elIwrByaIh40LmfR7Jb9+h1C\nk5P91yxx9s/drRd6hmR8Hye5VwaSa1SS7DSM/L5Hri+dC+W6CwIDAQABoAAwDQYJ\nKoZIhvcNAQELBQADggEBAMxscjfVRQ0/0c6f0MWtJJe+vy5Gj26XHVy5EsbH1ofq\neWF00CFlVw5CdznGV0NL6LOE+sz5sBKsN2sZU7xPeV5XRHVXpAuECcOcgWK6FkqA\nwSmwVWZ93o+kJXrUZTyZBMkvQMUUr30JIpXIXJmLWKPBq5KRBJLirHZYw4FmbARv\niZdNlQ1rLOZKZl7yUVkAfyfw+ueb0OPFp/fzuCerNB0ySSmYdHzqDFsMLm4Bq/2z\nFJOgasAU2RvxFVoRp7P/ZUSvtOKACMUHaYBnKZAvkKv3MIS/qLCSkzxNwsclT8uF\naKRFZnOkZZHvEaXVAwCfJB7tI3TELb+L2KyqU36Q1Oc=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "internal/testing/tls/wrong-client.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA0cBd/JPy+40pzcV551kffzrlsXk8ttGzZHwJpQ51CP8dMznF\nKxvlOvfHoiRYr7lLC2cZ1otjsaS1KS1fJQ1/a5l+VccIsCqKpwGks072ZwUAALOl\nFvsLXxlnp31vtS08DmgrVW3jmJTMj6Z0ILQG1FNZ7Ip21MXia4PZO0XtExDCeN9E\nZXUSR9Dewqlfc/t1qID9bXXo2URKlfcn9UDOhC77cGsN5euqARs1WX7803/u4DRF\njQfETvlE2Ev3K7PVUU/j81oU51Y3elIwrByaIh40LmfR7Jb9+h1Ck5P91yxx9s/d\nrRd6hmR8Hye5VwaSa1SS7DSM/L5Hri+dC+W6CwIDAQABAoIBAHml4JyRTdX4q+sM\ngcPcG3lVtkt0rfK1sh4wFgPlW5kpJE1GTwTOe+b0N5LhE5Jum4h0djbIxrwLc4n7\nJ3g82M6VygCDm5VYRuvO9y+LNzrOWo8NoUyvsouoF0a7aCMipfcRETjNr7cZbX5O\nooEpB+Dyqm+Wao7CaavDXySSTInGHG4AD9HM5nQsVIebS1HOkhI7SmNkZTOd3gzp\nbR/iZgaYI5eC7Zj7hHNr4gWdRBuefU8wLZZGoqByHRTSrKwICRLIkGyoMRAD9p9r\nS48lyUmd3BGHRPLmNl4u0kfsVlcCYBNKVZV6kkSVv4Wht/KVr3tl0+2wrNUe17w7\nvlCsCPECgYEA9AdHq3UjswD6PYITCdrDOGVLMpgL0obA6X2Fi6GsMiRXQgsvxMVY\na4D2vtfZvLa8TSA+b8bK6uSMyD3mOHLxBkUPMQZxiHzq/ldK9vSIvzky0woF6suT\nJ8fa2F0QfjlKWhFMwf6JVGyZl+vmYqqF55lxRJSWGZHSSxaxYPU7dbkCgYEA3Aqb\nYInpCp7zSlWYfXorwnyvsHsTdFbsoGGgMrc5l2B7PcP+Na7lm0dsNCrp7n7TSrE0\n8iIoqhYq5u7RlGf5QXzXcGgZQMHcxLcrqBPviEktuUifXZEwfw9NXrlt4iF/oVTc\n++7jqUZ+iH9NIMoxrQPXVdXJSJN9iwc7/yG6j+MCgYA+ehqsWCpSqx5mXwYW0M6I\ngs6U3n6wYNXFMeDeFf9rOwioHQsW2tu/cl46EDNr8HEXYfj6TzAmoWs13TszGqKA\n02+HQroQksLraVgFEChupOtRQtCvA33ignWSTYlqd6qEksdPJ6brWX6decUbX8M2\nv39TaqNfWok3tlClnUOi6QKBgQCIhfQ9g5OZyWE937nLMH/yHZaMMvCxIDWUlL3m\neZQ7/dq5Sd9xw2AmZbwW6gFWvk2ubCBjkxoT3ckkm0xhfdlC7ohk79GrQh0N2HA3\nypa1wmGiMhLe5PRoAUCJ4xbwVMRxfsvVbDTIlDpxyjo6e/kyVc3HLevDIe+k0QpC\nk9TC7QKBgDyghEPAM5euQHk2o7cMr0YK52vzoM1FGhrRAF5MhTYJEYBNu0Z+0McB\nG8kwy4WH5zMuvKj1zZckAzkbpD/iL3XQuzs9pZdNnXzdf25/us0pZ2a/6v5+fpmF\nJEuwQ1AztPEv4tLd3+xrmE+j+qd3xDqYt8eaWFswcuxchB6nUdqq\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "invoke.go",
    "content": "package grpcurl\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"github.com/golang/protobuf/jsonpb\"     //lint:ignore SA1019 we have to import these because some of their types appear in exported API\n\t\"github.com/golang/protobuf/proto\"      //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/desc\"    //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/dynamic\" //lint:ignore SA1019 same as above\n\t\"github.com/jhump/protoreflect/dynamic/grpcdynamic\"\n\t\"github.com/jhump/protoreflect/grpcreflect\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/status\"\n)\n\n// InvocationEventHandler is a bag of callbacks for handling events that occur in the course\n// of invoking an RPC. The handler also provides request data that is sent. The callbacks are\n// generally called in the order they are listed below.\ntype InvocationEventHandler interface {\n\t// OnResolveMethod is called with a descriptor of the method that is being invoked.\n\tOnResolveMethod(*desc.MethodDescriptor)\n\t// OnSendHeaders is called with the request metadata that is being sent.\n\tOnSendHeaders(metadata.MD)\n\t// OnReceiveHeaders is called when response headers have been received.\n\tOnReceiveHeaders(metadata.MD)\n\t// OnReceiveResponse is called for each response message received.\n\tOnReceiveResponse(proto.Message)\n\t// OnReceiveTrailers is called when response trailers and final RPC status have been received.\n\tOnReceiveTrailers(*status.Status, metadata.MD)\n}\n\n// RequestMessageSupplier is a function that is called to retrieve request\n// messages for a GRPC operation. This type is deprecated and will be removed in\n// a future release.\n//\n// Deprecated: This is only used with the deprecated InvokeRpc. Instead, use\n// RequestSupplier with InvokeRPC.\ntype RequestMessageSupplier func() ([]byte, error)\n\n// InvokeRpc uses the given gRPC connection to invoke the given method. This function is deprecated\n// and will be removed in a future release. It just delegates to the similarly named InvokeRPC\n// method, whose signature is only slightly different.\n//\n// Deprecated: use InvokeRPC instead.\nfunc InvokeRpc(ctx context.Context, source DescriptorSource, cc *grpc.ClientConn, methodName string,\n\theaders []string, handler InvocationEventHandler, requestData RequestMessageSupplier) error {\n\n\treturn InvokeRPC(ctx, source, cc, methodName, headers, handler, func(m proto.Message) error {\n\t\t// New function is almost identical, but the request supplier function works differently.\n\t\t// So we adapt the logic here to maintain compatibility.\n\t\tdata, err := requestData()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn jsonpb.Unmarshal(bytes.NewReader(data), m)\n\t})\n}\n\n// RequestSupplier is a function that is called to populate messages for a gRPC operation. The\n// function should populate the given message or return a non-nil error. If the supplier has no\n// more messages, it should return io.EOF. When it returns io.EOF, it should not in any way\n// modify the given message argument.\ntype RequestSupplier func(proto.Message) error\n\n// InvokeRPC uses the given gRPC channel to invoke the given method. The given descriptor source\n// is used to determine the type of method and the type of request and response message. The given\n// headers are sent as request metadata. Methods on the given event handler are called as the\n// invocation proceeds.\n//\n// The given requestData function supplies the actual data to send. It should return io.EOF when\n// there is no more request data. If the method being invoked is a unary or server-streaming RPC\n// (e.g. exactly one request message) and there is no request data (e.g. the first invocation of\n// the function returns io.EOF), then an empty request message is sent.\n//\n// If the requestData function and the given event handler coordinate or share any state, they should\n// be thread-safe. This is because the requestData function may be called from a different goroutine\n// than the one invoking event callbacks. (This only happens for bi-directional streaming RPCs, where\n// one goroutine sends request messages and another consumes the response messages).\nfunc InvokeRPC(ctx context.Context, source DescriptorSource, ch grpcdynamic.Channel, methodName string,\n\theaders []string, handler InvocationEventHandler, requestData RequestSupplier) error {\n\n\tmd := MetadataFromHeaders(headers)\n\n\tsvc, mth := parseSymbol(methodName)\n\tif svc == \"\" || mth == \"\" {\n\t\treturn fmt.Errorf(\"given method name %q is not in expected format: 'service/method' or 'service.method'\", methodName)\n\t}\n\n\tdsc, err := source.FindSymbol(svc)\n\tif err != nil {\n\t\t// return a gRPC status error if hasStatus is true\n\t\terrStatus, hasStatus := status.FromError(err)\n\t\tswitch {\n\t\tcase hasStatus && isNotFoundError(err):\n\t\t\treturn status.Errorf(errStatus.Code(), \"target server does not expose service %q: %s\", svc, errStatus.Message())\n\t\tcase hasStatus:\n\t\t\treturn status.Errorf(errStatus.Code(), \"failed to query for service descriptor %q: %s\", svc, errStatus.Message())\n\t\tcase isNotFoundError(err):\n\t\t\treturn fmt.Errorf(\"target server does not expose service %q\", svc)\n\t\t}\n\t\treturn fmt.Errorf(\"failed to query for service descriptor %q: %v\", svc, err)\n\t}\n\tsd, ok := dsc.(*desc.ServiceDescriptor)\n\tif !ok {\n\t\treturn fmt.Errorf(\"target server does not expose service %q\", svc)\n\t}\n\tmtd := sd.FindMethodByName(mth)\n\tif mtd == nil {\n\t\treturn fmt.Errorf(\"service %q does not include a method named %q\", svc, mth)\n\t}\n\n\thandler.OnResolveMethod(mtd)\n\n\t// we also download any applicable extensions so we can provide full support for parsing user-provided data\n\tvar ext dynamic.ExtensionRegistry\n\talreadyFetched := map[string]bool{}\n\tif err = fetchAllExtensions(source, &ext, mtd.GetInputType(), alreadyFetched); err != nil {\n\t\treturn fmt.Errorf(\"error resolving server extensions for message %s: %v\", mtd.GetInputType().GetFullyQualifiedName(), err)\n\t}\n\tif err = fetchAllExtensions(source, &ext, mtd.GetOutputType(), alreadyFetched); err != nil {\n\t\treturn fmt.Errorf(\"error resolving server extensions for message %s: %v\", mtd.GetOutputType().GetFullyQualifiedName(), err)\n\t}\n\n\tmsgFactory := dynamic.NewMessageFactoryWithExtensionRegistry(&ext)\n\treq := msgFactory.NewMessage(mtd.GetInputType())\n\n\thandler.OnSendHeaders(md)\n\tctx = metadata.NewOutgoingContext(ctx, md)\n\n\tstub := grpcdynamic.NewStubWithMessageFactory(ch, msgFactory)\n\tctx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\tif mtd.IsClientStreaming() && mtd.IsServerStreaming() {\n\t\treturn invokeBidi(ctx, stub, mtd, handler, requestData, req)\n\t} else if mtd.IsClientStreaming() {\n\t\treturn invokeClientStream(ctx, stub, mtd, handler, requestData, req)\n\t} else if mtd.IsServerStreaming() {\n\t\treturn invokeServerStream(ctx, stub, mtd, handler, requestData, req)\n\t} else {\n\t\treturn invokeUnary(ctx, stub, mtd, handler, requestData, req)\n\t}\n}\n\nfunc invokeUnary(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescriptor, handler InvocationEventHandler,\n\trequestData RequestSupplier, req proto.Message) error {\n\n\terr := requestData(req)\n\tif err != nil && err != io.EOF {\n\t\treturn fmt.Errorf(\"error getting request data: %v\", err)\n\t}\n\tif err != io.EOF {\n\t\t// verify there is no second message, which is a usage error\n\t\terr := requestData(req)\n\t\tif err == nil {\n\t\t\treturn fmt.Errorf(\"method %q is a unary RPC, but request data contained more than 1 message\", md.GetFullyQualifiedName())\n\t\t} else if err != io.EOF {\n\t\t\treturn fmt.Errorf(\"error getting request data: %v\", err)\n\t\t}\n\t}\n\n\t// Now we can actually invoke the RPC!\n\tvar respHeaders metadata.MD\n\tvar respTrailers metadata.MD\n\tresp, err := stub.InvokeRpc(ctx, md, req, grpc.Trailer(&respTrailers), grpc.Header(&respHeaders))\n\n\tstat, ok := status.FromError(err)\n\tif !ok {\n\t\t// Error codes sent from the server will get printed differently below.\n\t\t// So just bail for other kinds of errors here.\n\t\treturn fmt.Errorf(\"grpc call for %q failed: %v\", md.GetFullyQualifiedName(), err)\n\t}\n\n\thandler.OnReceiveHeaders(respHeaders)\n\n\tif stat.Code() == codes.OK {\n\t\thandler.OnReceiveResponse(resp)\n\t}\n\n\thandler.OnReceiveTrailers(stat, respTrailers)\n\n\treturn nil\n}\n\nfunc invokeClientStream(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescriptor, handler InvocationEventHandler,\n\trequestData RequestSupplier, req proto.Message) error {\n\n\t// invoke the RPC!\n\tstr, err := stub.InvokeRpcClientStream(ctx, md)\n\n\t// Upload each request message in the stream\n\tvar resp proto.Message\n\tfor err == nil {\n\t\terr = requestData(req)\n\t\tif err == io.EOF {\n\t\t\tresp, err = str.CloseAndReceive()\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error getting request data: %v\", err)\n\t\t}\n\n\t\terr = str.SendMsg(req)\n\t\tif err == io.EOF {\n\t\t\t// We get EOF on send if the server says \"go away\"\n\t\t\t// We have to use CloseAndReceive to get the actual code\n\t\t\tresp, err = str.CloseAndReceive()\n\t\t\tbreak\n\t\t}\n\n\t\treq.Reset()\n\t}\n\n\t// finally, process response data\n\tstat, ok := status.FromError(err)\n\tif !ok {\n\t\t// Error codes sent from the server will get printed differently below.\n\t\t// So just bail for other kinds of errors here.\n\t\treturn fmt.Errorf(\"grpc call for %q failed: %v\", md.GetFullyQualifiedName(), err)\n\t}\n\n\tif str != nil {\n\t\tif respHeaders, err := str.Header(); err == nil {\n\t\t\thandler.OnReceiveHeaders(respHeaders)\n\t\t}\n\t}\n\n\tif stat.Code() == codes.OK {\n\t\thandler.OnReceiveResponse(resp)\n\t}\n\n\tif str != nil {\n\t\thandler.OnReceiveTrailers(stat, str.Trailer())\n\t}\n\n\treturn nil\n}\n\nfunc invokeServerStream(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescriptor, handler InvocationEventHandler,\n\trequestData RequestSupplier, req proto.Message) error {\n\n\terr := requestData(req)\n\tif err != nil && err != io.EOF {\n\t\treturn fmt.Errorf(\"error getting request data: %v\", err)\n\t}\n\tif err != io.EOF {\n\t\t// verify there is no second message, which is a usage error\n\t\terr := requestData(req)\n\t\tif err == nil {\n\t\t\treturn fmt.Errorf(\"method %q is a server-streaming RPC, but request data contained more than 1 message\", md.GetFullyQualifiedName())\n\t\t} else if err != io.EOF {\n\t\t\treturn fmt.Errorf(\"error getting request data: %v\", err)\n\t\t}\n\t}\n\n\t// Now we can actually invoke the RPC!\n\tstr, err := stub.InvokeRpcServerStream(ctx, md, req)\n\n\tif str != nil {\n\t\tif respHeaders, err := str.Header(); err == nil {\n\t\t\thandler.OnReceiveHeaders(respHeaders)\n\t\t}\n\t}\n\n\t// Download each response message\n\tfor err == nil {\n\t\tvar resp proto.Message\n\t\tresp, err = str.RecvMsg()\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\thandler.OnReceiveResponse(resp)\n\t}\n\n\tstat, ok := status.FromError(err)\n\tif !ok {\n\t\t// Error codes sent from the server will get printed differently below.\n\t\t// So just bail for other kinds of errors here.\n\t\treturn fmt.Errorf(\"grpc call for %q failed: %v\", md.GetFullyQualifiedName(), err)\n\t}\n\n\tif str != nil {\n\t\thandler.OnReceiveTrailers(stat, str.Trailer())\n\t}\n\n\treturn nil\n}\n\nfunc invokeBidi(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescriptor, handler InvocationEventHandler,\n\trequestData RequestSupplier, req proto.Message) error {\n\n\tctx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\t// invoke the RPC!\n\tstr, err := stub.InvokeRpcBidiStream(ctx, md)\n\n\tvar wg sync.WaitGroup\n\tvar sendErr atomic.Value\n\n\tdefer wg.Wait()\n\n\tif err == nil {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\t// Concurrently upload each request message in the stream\n\t\t\tvar err error\n\t\t\tfor err == nil {\n\t\t\t\terr = requestData(req)\n\n\t\t\t\tif err == io.EOF {\n\t\t\t\t\terr = str.CloseSend()\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\terr = fmt.Errorf(\"error getting request data: %v\", err)\n\t\t\t\t\tcancel()\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\terr = str.SendMsg(req)\n\n\t\t\t\treq.Reset()\n\t\t\t}\n\n\t\t\tif err != nil {\n\t\t\t\tsendErr.Store(err)\n\t\t\t}\n\t\t}()\n\t}\n\n\tif str != nil {\n\t\tif respHeaders, err := str.Header(); err == nil {\n\t\t\thandler.OnReceiveHeaders(respHeaders)\n\t\t}\n\t}\n\n\t// Download each response message\n\tfor err == nil {\n\t\tvar resp proto.Message\n\t\tresp, err = str.RecvMsg()\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\thandler.OnReceiveResponse(resp)\n\t}\n\n\tif se, ok := sendErr.Load().(error); ok && se != io.EOF {\n\t\terr = se\n\t}\n\n\tstat, ok := status.FromError(err)\n\tif !ok {\n\t\t// Error codes sent from the server will get printed differently below.\n\t\t// So just bail for other kinds of errors here.\n\t\treturn fmt.Errorf(\"grpc call for %q failed: %v\", md.GetFullyQualifiedName(), err)\n\t}\n\n\tif str != nil {\n\t\thandler.OnReceiveTrailers(stat, str.Trailer())\n\t}\n\n\treturn nil\n}\n\ntype notFoundError string\n\nfunc notFound(kind, name string) error {\n\treturn notFoundError(fmt.Sprintf(\"%s not found: %s\", kind, name))\n}\n\nfunc (e notFoundError) Error() string {\n\treturn string(e)\n}\n\nfunc isNotFoundError(err error) bool {\n\tif grpcreflect.IsElementNotFoundError(err) {\n\t\treturn true\n\t}\n\t_, ok := err.(notFoundError)\n\treturn ok\n}\n\nfunc parseSymbol(svcAndMethod string) (string, string) {\n\tpos := strings.LastIndex(svcAndMethod, \"/\")\n\tif pos < 0 {\n\t\tpos = strings.LastIndex(svcAndMethod, \".\")\n\t\tif pos < 0 {\n\t\t\treturn \"\", \"\"\n\t\t}\n\t}\n\treturn svcAndMethod[:pos], svcAndMethod[pos+1:]\n}\n"
  },
  {
    "path": "mk-test-files.sh",
    "content": "#!/bin/bash\n\nset -e\n\ncd \"$(dirname $0)\"\n\n# Run this script to generate files used by tests.\n\necho \"Creating protosets...\"\nprotoc testing/test.proto \\\n\t--include_imports \\\n\t--descriptor_set_out=testing/test.protoset\n\nprotoc testing/example.proto \\\n\t--include_imports \\\n\t--descriptor_set_out=testing/example.protoset\n\nprotoc testing/jsonpb_test_proto/test_objects.proto \\\n\t--go_out=paths=source_relative:.\n\necho \"Creating certs for TLS testing...\"\nif ! hash certstrap 2>/dev/null; then\n  # certstrap not found: try to install it\n  go get github.com/square/certstrap\n  go install github.com/square/certstrap\nfi\n\nfunction cs() {\n\tcertstrap --depot-path testing/tls \"$@\" --passphrase \"\"\n}\n\nrm -rf testing/tls\n\n# Create CA\ncs init --years 10 --common-name ca\n\n# Create client cert\ncs request-cert --common-name client\ncs sign client --years 10 --CA ca\n\n# Create server cert\ncs request-cert --common-name server --ip 127.0.0.1 --domain localhost\ncs sign server --years 10 --CA ca\n\n# Create another server cert for error testing\ncs request-cert --common-name other --ip 1.2.3.4 --domain foobar.com\ncs sign other --years 10 --CA ca\n\n# Create another CA and client cert for more\n# error testing\ncs init --years 10 --common-name wrong-ca\ncs request-cert --common-name wrong-client\ncs sign wrong-client --years 10 --CA wrong-ca\n\n# Create expired cert\ncs request-cert --common-name expired --ip 127.0.0.1 --domain localhost\ncs sign expired --years 0 --CA ca\n"
  },
  {
    "path": "releasing/README.md",
    "content": "# Releases of gRPCurl\n\nThis document provides instructions for building a release of `grpcurl`.\n\nThe release process consists of a handful of tasks:\n1. Drop a release tag in git.\n2. Build binaries for various platforms. This is done using the local `go` tool and uses `GOOS` and `GOARCH` environment variables to cross-compile for supported platforms.\n3. Creates a release in GitHub, uploads the binaries, and creates provisional release notes (in the form of a change log).\n4. Build a docker image for the new release.\n5. Push the docker image to Docker Hub, with both a version tag and the \"latest\" tag.\n6. Submits a PR to update the [Homebrew](https://brew.sh/) recipe with the latest version.\n\nMost of this is automated via a script in this same directory. The main thing you will need is a GitHub personal access token, which will be used for creating the release in GitHub (so you need write access to the fullstorydev/grpcurl repo) and to open a Homebrew pull request.\n\n## Creating a new release\n\nSo, to actually create a new release, just run the script in this directory.\n\nFirst, you need a version number for the new release, following sem-ver format: `v<Major>.<Minor>.<Patch>`. Second, you need a personal access token for GitHub.\n\nWe'll use `v2.3.4` as an example version and `abcdef0123456789abcdef` as an example GitHub token:\n\n```sh\n# from the root of the repo\nGITHUB_TOKEN=abcdef0123456789abcd \\\n    ./releasing/do-release.sh v2.3.4\n```\n\nWasn't that easy! There is one last step: update the release notes in GitHub. By default, the script just records a change log of commit descriptions. Use that log (and, if necessary, drill into individual PRs included in the release) to flesh out notes in the format of the `RELEASE_NOTES.md` file _in this directory_. Then login to GitHub, go to the new release, edit the notes, and paste in the markdown you just wrote.\n\nThat should be all there is to it! If things go wrong and you have to re-do part of the process, see the sections below.\n\n----\n\n### GitHub Releases\nThe GitHub release is the first step performed by the `do-release.sh` script. So generally, if there is an issue with that step, you can re-try the whole script.\n\nNote, if running the script did something wrong, you may have to first login to GitHub and remove uploaded artifacts for a botched release attempt. In general, this is _very undesirable_. Releases should usually be considered immutable. Instead of removing uploaded assets and providing new ones, it is often better to remove uploaded assets (to make bad binaries no longer available) and then _release a new patch version_. (You can edit the release notes for the botched version explaining why there are no artifacts for it.)\n\nThe steps to do a GitHub-only release (vs. running the entire script) are the following:\n\n```sh\n# from the root of the repo\ngit tag v2.3.4\nGITHUB_TOKEN=abcdef0123456789abcdef \\\n    GO111MODULE=on \\\n    make release\n```\n\nThe `git tag ...` step is necessary because the release target requires that the current SHA have a sem-ver tag. That's the version it will use when creating the release.\n\nThis will create the release in GitHub with provisional release notes that just include a change log of commit messages. You still need to login to GitHub and revise those notes to adhere to the recommended format. (See `RELEASE_NOTES.md` in this directory.)\n\n### Docker Hub Releases\n\nTo re-run only the Docker Hub release steps, you can manually run through each step in the \"Docker\" section of `do_release.sh`.\n\nIf the `docker push ...` steps fail, you may need to run `docker login`, enter your Docker Hub login credentials, and then try to push again.\n\n### Homebrew Releases\n\nThe last step is to update the Homebrew recipe to use the latest version. First, we need to compute the SHA256 checksum for the source archive:\n\n```sh\n# download the source archive from GitHub\nURL=https://github.com/fullstorydev/grpcurl/archive/refs/tags/v2.3.4.tar.gz\ncurl -L -o tmp.tgz $URL\n# and compute the SHA\nSHA=\"$(sha256sum < tmp.tgz | awk '{ print $1 }')\"\n```\n\nTo actually create the brew PR, you need your GitHub personal access token again, as well as the URL and SHA from the previous step:\n\n```sh\nHOMEBREW_GITHUB_API_TOKEN=abcdef0123456789abcdef \\\n    brew bump-formula-pr --url $URL --sha256 $SHA grpcurl\n```\n\nThis creates a PR to bump the formula to the new version. When this PR is merged by brew maintainers, the new version becomes available!\n"
  },
  {
    "path": "releasing/RELEASE_NOTES.md",
    "content": "## Changes\n\n### Command-line tool\n\n* _In this list, describe the changes to the command-line tool._\n* _Use one bullet per change. Include both bug-fixes and improvements. Omit this section if there are no changes that impact the command-line tool._\n\n### Go package \"github.com/fullstorydev/grpcurl\"\n\n* _In this list, describe the changes to exported API in the main package in this repo: \"github.com/fullstorydev/grpcurl\". These will often be closely related to changes to the command-line tool, though not always: changes that only impact the cmd/grpcurl directory of this repo do not impact exported API._\n* _Use one bullet per change. Include both bug-fixes and improvements. Omit this section if there are no changes that impact the exported API._"
  },
  {
    "path": "releasing/do-release.sh",
    "content": "#!/bin/bash\n\n# strict mode\nset -euo pipefail\nIFS=$'\\n\\t'\n\nif [[ -z ${DRY_RUN:-} ]]; then\n    PREFIX=\"\"\nelse\n    PREFIX=\"echo\"\nfi\n\n# input validation\nif [[ -z ${GITHUB_TOKEN:-} ]]; then\n    echo \"GITHUB_TOKEN environment variable must be set before running.\" >&2\n    exit 1\nfi\nif [[ $# -ne 1 || $1 == \"\" ]]; then\n    echo \"This program requires one argument: the version number, in 'vM.N.P' format.\" >&2\n    exit 1\nfi\nVERSION=$1\n\n# Change to root of the repo\ncd \"$(dirname \"$0\")/..\"\n\n# GitHub release\n\n$PREFIX git tag \"$VERSION\"\n# make sure GITHUB_TOKEN is exported, for the benefit of this next command\nexport GITHUB_TOKEN\nGO111MODULE=on $PREFIX make release\n# if that was successful, it could have touched go.mod and go.sum, so revert those\n$PREFIX git checkout go.mod go.sum\n\n# Docker release\n\n# make sure credentials are valid for later push steps; this might\n# be interactive since this will prompt for username and password\n# if there are no valid current credentials.\n$PREFIX docker login\necho \"$VERSION\" > VERSION\n\n# Docker Buildx support is included in Docker 19.03\n# Below step installs emulators for different architectures on the host\n# This enables running and building containers for below architectures mentioned using --platforms\n$PREFIX docker run --privileged --rm tonistiigi/binfmt:qemu-v6.1.0 --install all\n# Create a new builder instance\nexport DOCKER_CLI_EXPERIMENTAL=enabled\n$PREFIX docker buildx create --use --name multiarch-builder --node multiarch-builder0\n# push to docker hub, both the given version as a tag and for \"latest\" tag\n$PREFIX docker buildx build --platform linux/amd64,linux/s390x,linux/arm64,linux/ppc64le --tag fullstorydev/grpcurl:${VERSION} --tag fullstorydev/grpcurl:latest --push --progress plain --no-cache .\n$PREFIX docker buildx build --platform linux/amd64,linux/s390x,linux/arm64,linux/ppc64le --tag fullstorydev/grpcurl:${VERSION}-alpine --tag fullstorydev/grpcurl:latest-alpine --push --progress plain --no-cache --target alpine .\nrm VERSION\n\n# Homebrew release\n\nURL=\"https://github.com/fullstorydev/grpcurl/archive/refs/tags/${VERSION}.tar.gz\"\ncurl -L -o tmp.tgz \"$URL\"\nSHA=\"$(sha256sum < tmp.tgz | awk '{ print $1 }')\"\nrm tmp.tgz\nHOMEBREW_GITHUB_API_TOKEN=\"$GITHUB_TOKEN\" $PREFIX brew bump-formula-pr --url \"$URL\" --sha256 \"$SHA\" grpcurl\n"
  },
  {
    "path": "snap/README.md",
    "content": "# packing and releasing\nTo pack the current branch to a snap package:\n\n`snapcraft pack`\n\nTo install the package locally:\n\n`snap install ./grpcurl_v[version tag]_amd64.snap --devmode`\n\nTo upload the snap to the edge channel:\n\n`snapcraft upload --release edge ./grpcurl_v[version tag]_amd64.snap`\n\n(you need to own the package name registration for this!)\n\n# ownership\nThe snap's current owner is `pietro.pasotti@canonical.com`; who is very happy to support with maintaining the snap distribution and/or transfer its ownership to the developers.\n\nPlease reach out to me for questions regarding the snap; including:\n- adding support for other architectures\n- automating the release\n\nCheers and thanks for the awesome tool!"
  },
  {
    "path": "snap/snapcraft.yaml",
    "content": "name: grpcurl\nbase: core24\n# allow grpcurl part to call craftctl set-version\nadopt-info: grpcurl\nsummary: grpcurl is a command-line tool that lets you interact with gRPC servers.\n\ndescription: |\n  grpcurl is a command-line tool that lets you interact with gRPC servers. \n  It's basically curl for gRPC servers.\n\ngrade: stable\nconfinement: strict\nlicense: MIT\n\nplatforms:\n  amd64:\n    build-on:\n      - amd64\n    build-for:\n      - amd64\n  arm64:\n    build-on:\n      - amd64\n      - arm64\n    build-for:\n      - arm64\n\napps:\n  grpcurl:\n    command: grpcurl\n    plugs:\n      - network\n\nparts:\n  grpcurl:\n    plugin: go\n    build-snaps: [go/latest/stable]\n    source: https://github.com/fullstorydev/grpcurl\n    source-type: git\n    override-build: |\n      tag=\"$(git describe --tags --abbrev=0)\"\n      craftctl set version=\"$tag\"\n\n      go build -o $CRAFT_PART_INSTALL/grpcurl ./cmd/grpcurl/grpcurl.go\n      \n      # adjust the permissions\n      chmod 0755 $CRAFT_PART_INSTALL/grpcurl\n"
  },
  {
    "path": "tls_settings_test.go",
    "content": "package grpcurl_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials\"\n\n\t. \"github.com/fullstorydev/grpcurl\"\n\tgrpcurl_testing \"github.com/fullstorydev/grpcurl/internal/testing\"\n)\n\nfunc TestPlainText(t *testing.T) {\n\te, err := createTestServerAndClient(nil, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to setup server and client: %v\", err)\n\t}\n\tdefer e.Close()\n\n\tsimpleTest(t, e.cc)\n}\n\nfunc TestBasicTLS(t *testing.T) {\n\tserverCreds, err := ServerTransportCredentials(\"\", \"internal/testing/tls/server.crt\", \"internal/testing/tls/server.key\", false)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\tclientCreds, err := ClientTransportCredentials(false, \"internal/testing/tls/ca.crt\", \"\", \"\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\te, err := createTestServerAndClient(serverCreds, clientCreds)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to setup server and client: %v\", err)\n\t}\n\tdefer e.Close()\n\n\tsimpleTest(t, e.cc)\n}\n\nfunc TestInsecureClientTLS(t *testing.T) {\n\tserverCreds, err := ServerTransportCredentials(\"\", \"internal/testing/tls/server.crt\", \"internal/testing/tls/server.key\", false)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\tclientCreds, err := ClientTransportCredentials(true, \"\", \"\", \"\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\te, err := createTestServerAndClient(serverCreds, clientCreds)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to setup server and client: %v\", err)\n\t}\n\tdefer e.Close()\n\n\tsimpleTest(t, e.cc)\n}\n\nfunc TestClientCertTLS(t *testing.T) {\n\tserverCreds, err := ServerTransportCredentials(\"internal/testing/tls/ca.crt\", \"internal/testing/tls/server.crt\", \"internal/testing/tls/server.key\", false)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\tclientCreds, err := ClientTransportCredentials(false, \"internal/testing/tls/ca.crt\", \"internal/testing/tls/client.crt\", \"internal/testing/tls/client.key\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\te, err := createTestServerAndClient(serverCreds, clientCreds)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to setup server and client: %v\", err)\n\t}\n\tdefer e.Close()\n\n\tsimpleTest(t, e.cc)\n}\n\nfunc TestRequireClientCertTLS(t *testing.T) {\n\tserverCreds, err := ServerTransportCredentials(\"internal/testing/tls/ca.crt\", \"internal/testing/tls/server.crt\", \"internal/testing/tls/server.key\", true)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\tclientCreds, err := ClientTransportCredentials(false, \"internal/testing/tls/ca.crt\", \"internal/testing/tls/client.crt\", \"internal/testing/tls/client.key\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\te, err := createTestServerAndClient(serverCreds, clientCreds)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to setup server and client: %v\", err)\n\t}\n\tdefer e.Close()\n\n\tsimpleTest(t, e.cc)\n}\n\nfunc TestBrokenTLS_ClientPlainText(t *testing.T) {\n\tserverCreds, err := ServerTransportCredentials(\"\", \"internal/testing/tls/server.crt\", \"internal/testing/tls/server.key\", false)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\t// client connection (usually) succeeds since client is not waiting for TLS handshake\n\t// (we try several times, but if we never get a connection and the error message is\n\t// a known/expected possibility, we'll just bail)\n\tvar e testEnv\n\tfailCount := 0\n\tfor {\n\t\te, err = createTestServerAndClient(serverCreds, nil)\n\t\tif err == nil {\n\t\t\t// success!\n\t\t\tdefer e.Close()\n\t\t\tbreak\n\t\t}\n\n\t\tif strings.Contains(err.Error(), \"deadline exceeded\") ||\n\t\t\tstrings.Contains(err.Error(), \"use of closed network connection\") {\n\t\t\t// It is possible that the connection never becomes healthy:\n\t\t\t//   1) grpc connects successfully\n\t\t\t//   2) grpc client tries to send HTTP/2 preface and settings frame\n\t\t\t//   3) server, expecting handshake, closes the connection\n\t\t\t//   4) in the client, the write fails, so the connection never\n\t\t\t//      becomes ready\n\t\t\t// The client will attempt to reconnect on transient errors, so\n\t\t\t// may eventually bump into the connect time limit. This used to\n\t\t\t// result in a \"deadline exceeded\" error, but more recent versions\n\t\t\t// of the grpc library report any underlying I/O error instead, so\n\t\t\t// we also check for \"use of closed network connection\".\n\t\t\tfailCount++\n\t\t\tif failCount > 5 {\n\t\t\t\treturn // bail...\n\t\t\t}\n\t\t\t// we'll try again\n\n\t\t} else {\n\t\t\t// some other error occurred, so we'll consider that a test failure\n\t\t\tt.Fatalf(\"failed to setup server and client: %v\", err)\n\t\t}\n\t}\n\n\t// but request fails because server closes connection upon seeing request\n\t// bytes that are not a TLS handshake\n\tcl := grpcurl_testing.NewTestServiceClient(e.cc)\n\t_, err = cl.UnaryCall(context.Background(), &grpcurl_testing.SimpleRequest{})\n\tif err == nil {\n\t\tt.Fatal(\"expecting failure\")\n\t}\n\t// various errors possible when server closes connection\n\tif !strings.Contains(err.Error(), \"transport is closing\") &&\n\t\t!strings.Contains(err.Error(), \"connection is unavailable\") &&\n\t\t!strings.Contains(err.Error(), \"use of closed network connection\") &&\n\t\t!strings.Contains(err.Error(), \"all SubConns are in TransientFailure\") {\n\n\t\tt.Fatalf(\"expecting transport failure, got: %v\", err)\n\t}\n}\n\nfunc TestBrokenTLS_ServerPlainText(t *testing.T) {\n\tclientCreds, err := ClientTransportCredentials(false, \"internal/testing/tls/ca.crt\", \"\", \"\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\te, err := createTestServerAndClient(nil, clientCreds)\n\tif err == nil {\n\t\te.Close()\n\t\tt.Fatal(\"expecting TLS failure setting up server and client\")\n\t}\n\tif !strings.Contains(err.Error(), \"first record does not look like a TLS handshake\") {\n\t\tt.Fatalf(\"expecting TLS handshake failure, got: %v\", err)\n\t}\n}\n\nfunc TestBrokenTLS_ServerUsesWrongCert(t *testing.T) {\n\tserverCreds, err := ServerTransportCredentials(\"\", \"internal/testing/tls/other.crt\", \"internal/testing/tls/other.key\", false)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\tclientCreds, err := ClientTransportCredentials(false, \"internal/testing/tls/ca.crt\", \"\", \"\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\te, err := createTestServerAndClient(serverCreds, clientCreds)\n\tif err == nil {\n\t\te.Close()\n\t\tt.Fatal(\"expecting TLS failure setting up server and client\")\n\t}\n\tif !strings.Contains(err.Error(), \"certificate is valid for\") {\n\t\tt.Fatalf(\"expecting TLS certificate error, got: %v\", err)\n\t}\n}\n\nfunc TestBrokenTLS_ClientHasExpiredCert(t *testing.T) {\n\tserverCreds, err := ServerTransportCredentials(\"internal/testing/tls/ca.crt\", \"internal/testing/tls/server.crt\", \"internal/testing/tls/server.key\", false)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\tclientCreds, err := ClientTransportCredentials(false, \"internal/testing/tls/ca.crt\", \"internal/testing/tls/expired.crt\", \"internal/testing/tls/expired.key\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\te, err := createTestServerAndClient(serverCreds, clientCreds)\n\tif err == nil {\n\t\te.Close()\n\t\tt.Fatal(\"expecting TLS failure setting up server and client\")\n\t}\n\tif !strings.Contains(err.Error(), \"certificate\") {\n\t\tt.Fatalf(\"expecting TLS certificate error, got: %v\", err)\n\t}\n}\n\nfunc TestBrokenTLS_ServerHasExpiredCert(t *testing.T) {\n\tserverCreds, err := ServerTransportCredentials(\"\", \"internal/testing/tls/expired.crt\", \"internal/testing/tls/expired.key\", false)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\tclientCreds, err := ClientTransportCredentials(false, \"internal/testing/tls/ca.crt\", \"\", \"\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\te, err := createTestServerAndClient(serverCreds, clientCreds)\n\tif err == nil {\n\t\te.Close()\n\t\tt.Fatal(\"expecting TLS failure setting up server and client\")\n\t}\n\tif !strings.Contains(err.Error(), \"certificate has expired or is not yet valid\") {\n\t\tt.Fatalf(\"expecting TLS certificate expired, got: %v\", err)\n\t}\n}\n\nfunc TestBrokenTLS_ClientNotTrusted(t *testing.T) {\n\tserverCreds, err := ServerTransportCredentials(\"internal/testing/tls/ca.crt\", \"internal/testing/tls/server.crt\", \"internal/testing/tls/server.key\", true)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\tclientCreds, err := ClientTransportCredentials(false, \"internal/testing/tls/ca.crt\", \"internal/testing/tls/wrong-client.crt\", \"internal/testing/tls/wrong-client.key\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\te, err := createTestServerAndClient(serverCreds, clientCreds)\n\tif err == nil {\n\t\te.Close()\n\t\tt.Fatal(\"expecting TLS failure setting up server and client\")\n\t}\n\t// Check for either the old error (Go <=1.24) or the new one (Go 1.25+)\n\t// Go 1.24: \"bad certificate\"\n\t// Go 1.25: \"handshake failure\"\n\terrMsg := err.Error()\n\tif !strings.Contains(errMsg, \"bad certificate\") && !strings.Contains(errMsg, \"handshake failure\") {\n\t\tt.Fatalf(\"expecting a specific TLS certificate or handshake error, got: %v\", err)\n\t}\n}\n\nfunc TestBrokenTLS_ServerNotTrusted(t *testing.T) {\n\tserverCreds, err := ServerTransportCredentials(\"\", \"internal/testing/tls/server.crt\", \"internal/testing/tls/server.key\", false)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\tclientCreds, err := ClientTransportCredentials(false, \"\", \"internal/testing/tls/client.crt\", \"internal/testing/tls/client.key\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\te, err := createTestServerAndClient(serverCreds, clientCreds)\n\tif err == nil {\n\t\te.Close()\n\t\tt.Fatal(\"expecting TLS failure setting up server and client\")\n\t}\n\tif !strings.Contains(err.Error(), \"certificate\") {\n\t\tt.Fatalf(\"expecting TLS certificate error, got: %v\", err)\n\t}\n}\n\nfunc TestBrokenTLS_RequireClientCertButNonePresented(t *testing.T) {\n\tserverCreds, err := ServerTransportCredentials(\"internal/testing/tls/ca.crt\", \"internal/testing/tls/server.crt\", \"internal/testing/tls/server.key\", true)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\tclientCreds, err := ClientTransportCredentials(false, \"internal/testing/tls/ca.crt\", \"\", \"\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create server creds: %v\", err)\n\t}\n\n\te, err := createTestServerAndClient(serverCreds, clientCreds)\n\tif err == nil {\n\t\te.Close()\n\t\tt.Fatal(\"expecting TLS failure setting up server and client\")\n\t}\n\t// Check for either the old error (Go <=1.24) or the new one (Go 1.25+)\n\t// Go 1.24: \"bad certificate\"\n\t// Go 1.25: \"handshake failure\"\n\terrMsg := err.Error()\n\tif !strings.Contains(errMsg, \"bad certificate\") && !strings.Contains(errMsg, \"handshake failure\") {\n\t\tt.Fatalf(\"expecting a specific TLS certificate or handshake error, got: %v\", err)\n\t}\n}\n\nfunc simpleTest(t *testing.T, cc *grpc.ClientConn) {\n\tcl := grpcurl_testing.NewTestServiceClient(cc)\n\tctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)\n\tdefer cancel()\n\t_, err := cl.UnaryCall(ctx, &grpcurl_testing.SimpleRequest{}, grpc.WaitForReady(true))\n\tif err != nil {\n\t\tt.Errorf(\"simple RPC failed: %v\", err)\n\t}\n}\n\nfunc createTestServerAndClient(serverCreds, clientCreds credentials.TransportCredentials) (testEnv, error) {\n\tvar e testEnv\n\tcompleted := false\n\tdefer func() {\n\t\tif !completed {\n\t\t\te.Close()\n\t\t}\n\t}()\n\n\tvar svrOpts []grpc.ServerOption\n\tif serverCreds != nil {\n\t\tsvrOpts = []grpc.ServerOption{grpc.Creds(serverCreds)}\n\t}\n\tsvr := grpc.NewServer(svrOpts...)\n\tgrpcurl_testing.RegisterTestServiceServer(svr, grpcurl_testing.TestServer{})\n\tl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\treturn e, err\n\t}\n\tport := l.Addr().(*net.TCPAddr).Port\n\tgo svr.Serve(l)\n\n\tctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)\n\tdefer cancel()\n\n\tcc, err := BlockingDial(ctx, \"tcp\", fmt.Sprintf(\"127.0.0.1:%d\", port), clientCreds)\n\tif err != nil {\n\t\treturn e, err\n\t}\n\n\te.svr = svr\n\te.cc = cc\n\tcompleted = true\n\treturn e, nil\n}\n\ntype testEnv struct {\n\tsvr *grpc.Server\n\tcc  *grpc.ClientConn\n}\n\nfunc (e *testEnv) Close() {\n\tif e.cc != nil {\n\t\te.cc.Close()\n\t\te.cc = nil\n\t}\n\tif e.svr != nil {\n\t\te.svr.GracefulStop()\n\t\te.svr = nil\n\t}\n}\n"
  }
]