Showing preview only (467K chars total). Download the full file or copy to clipboard to get everything.
Repository: fullstorydev/grpcurl
Branch: master
Commit: c54eac28fd8d
Files: 75
Total size: 444.5 KB
Directory structure:
gitextract_zt9nhjki/
├── .circleci/
│ └── config.yml
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ └── codesee-arch-diagram.yml
├── .gitignore
├── .goreleaser.yml
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── cmd/
│ └── grpcurl/
│ ├── grpcurl.go
│ ├── indent_test.go
│ └── unix.go
├── desc_source.go
├── desc_source_test.go
├── download_protoc.sh
├── format.go
├── format_test.go
├── go.mod
├── go.sum
├── grpcurl.go
├── grpcurl_test.go
├── internal/
│ └── testing/
│ ├── cmd/
│ │ ├── bankdemo/
│ │ │ ├── README.md
│ │ │ ├── auth.go
│ │ │ ├── bank.go
│ │ │ ├── bank.pb.go
│ │ │ ├── bank.proto
│ │ │ ├── bank_grpc.pb.go
│ │ │ ├── chat.go
│ │ │ ├── db.go
│ │ │ ├── main.go
│ │ │ ├── support.pb.go
│ │ │ ├── support.proto
│ │ │ └── support_grpc.pb.go
│ │ └── testserver/
│ │ ├── README.md
│ │ ├── testserver.go
│ │ └── unix.go
│ ├── example.proto
│ ├── example.protoset
│ ├── example2.proto
│ ├── jsonpb_test_proto/
│ │ ├── test_objects.pb.go
│ │ └── test_objects.proto
│ ├── test.pb.go
│ ├── test.proto
│ ├── test.protoset
│ ├── test_grpc.pb.go
│ ├── test_server.go
│ └── tls/
│ ├── ca.crl
│ ├── ca.crt
│ ├── ca.key
│ ├── client.crt
│ ├── client.csr
│ ├── client.key
│ ├── expired.crt
│ ├── expired.csr
│ ├── expired.key
│ ├── other.crt
│ ├── other.csr
│ ├── other.key
│ ├── server.crt
│ ├── server.csr
│ ├── server.key
│ ├── wrong-ca.crl
│ ├── wrong-ca.crt
│ ├── wrong-ca.key
│ ├── wrong-client.crt
│ ├── wrong-client.csr
│ └── wrong-client.key
├── invoke.go
├── mk-test-files.sh
├── releasing/
│ ├── README.md
│ ├── RELEASE_NOTES.md
│ └── do-release.sh
├── snap/
│ ├── README.md
│ └── snapcraft.yaml
└── tls_settings_test.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .circleci/config.yml
================================================
shared_configs:
simple_job_steps: &simple_job_steps
- checkout
- run:
name: Run tests
command: |
make test
# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference
version: 2.1
jobs:
build-1-23:
working_directory: ~/repo
docker:
- image: cimg/go:1.23
steps: *simple_job_steps
build-1-24:
working_directory: ~/repo
docker:
- image: cimg/go:1.24
steps: *simple_job_steps
build-1-25:
working_directory: ~/repo
docker:
- image: cimg/go:1.25
steps:
- checkout
- run:
name: Run tests and linters
command: |
make ci
workflows:
pr-build-test:
jobs:
- build-1-23
- build-1-24
- build-1-25
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
# Check for updates once a week
schedule:
interval: "weekly"
================================================
FILE: .github/workflows/codesee-arch-diagram.yml
================================================
# This workflow was added by CodeSee. Learn more at https://codesee.io/
# This is v2.0 of this workflow file
on:
push:
branches:
- master
pull_request_target:
types: [opened, synchronize, reopened]
name: CodeSee
permissions: read-all
jobs:
codesee:
runs-on: ubuntu-latest
continue-on-error: true
name: Analyze the repo with CodeSee
steps:
- uses: Codesee-io/codesee-action@v2
with:
codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }}
================================================
FILE: .gitignore
================================================
dist/
.idea/
VERSION
.tmp/
*.snap
================================================
FILE: .goreleaser.yml
================================================
builds:
- binary: grpcurl
main: ./cmd/grpcurl
goos:
- linux
- darwin
- windows
goarch:
- amd64
- 386
- arm
- arm64
- s390x
- ppc64le
goarm:
- 5
- 6
- 7
ignore:
- goos: darwin
goarch: 386
- goos: windows
goarch: arm64
- goos: darwin
goarch: arm
- goos: windows
goarch: arm
- goos: darwin
goarch: s390x
- goos: windows
goarch: s390x
- goos: darwin
goarch: ppc64le
- goos: windows
goarch: ppc64le
ldflags:
- -s -w -X main.version=v{{.Version}}
archives:
- format: tar.gz
name_template: >-
{{ .Binary }}_{{ .Version }}_
{{- if eq .Os "darwin" }}osx{{ else }}{{ .Os }}{{ end }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}x86_32
{{- else }}{{ .Arch }}{{ end }}
{{- with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}
format_overrides:
- goos: windows
format: zip
files:
- LICENSE
nfpms:
- vendor: Fullstory
homepage: https://github.com/fullstorydev/grpcurl/
maintainer: Engineering at Fullstory <fixme@fixme>
description: 'Like cURL, but for gRPC: Command-line tool for interacting with gRPC servers'
license: MIT
id: nfpms
formats:
- deb
- rpm
================================================
FILE: Dockerfile
================================================
FROM golang:1.25-alpine AS builder
LABEL maintainer="Fullstory Engineering"
# create non-privileged group and user
RUN addgroup -S grpcurl && adduser -S grpcurl -G grpcurl
WORKDIR /tmp/fullstorydev/grpcurl
# copy just the files/sources we need to build grpcurl
COPY VERSION *.go go.* /tmp/fullstorydev/grpcurl/
COPY cmd /tmp/fullstorydev/grpcurl/cmd
# and build a completely static binary (so we can use
# scratch as basis for the final image)
ENV CGO_ENABLED=0
ENV GO111MODULE=on
RUN go build -o /grpcurl \
-ldflags "-w -extldflags \"-static\" -X \"main.version=$(cat VERSION)\"" \
./cmd/grpcurl
FROM alpine:3 AS alpine
WORKDIR /
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /grpcurl /bin/grpcurl
USER grpcurl
ENTRYPOINT ["/bin/grpcurl"]
# New FROM so we have a nice'n'tiny image
FROM scratch
WORKDIR /
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /grpcurl /bin/grpcurl
USER grpcurl
ENTRYPOINT ["/bin/grpcurl"]
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2017 Fullstory, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
dev_build_version=$(shell git describe --tags --always --dirty)
export PATH := $(shell pwd)/.tmp/protoc/bin:$(PATH)
export PROTOC_VERSION := 22.0
# Disable CGO for improved compatibility across distros
export CGO_ENABLED=0
export GOFLAGS=-trimpath
export GOWORK=off
# TODO: run golint and errcheck, but only to catch *new* violations and
# decide whether to change code or not (e.g. we need to be able to whitelist
# violations already in the code). They can be useful to catch errors, but
# they are just too noisy to be a requirement for a CI -- we don't even *want*
# to fix some of the things they consider to be violations.
.PHONY: ci
ci: deps checkgofmt checkgenerate vet staticcheck ineffassign predeclared test
.PHONY: deps
deps:
go get -d -v -t ./...
go mod tidy
.PHONY: updatedeps
updatedeps:
go get -d -v -t -u -f ./...
go mod tidy
.PHONY: install
install:
go install -ldflags '-X "main.version=dev build $(dev_build_version)"' ./...
.PHONY: release
release:
@go install github.com/goreleaser/goreleaser@v1.21.0
goreleaser release --clean
.PHONY: docker
docker:
@echo $(dev_build_version) > VERSION
docker build -t fullstorydev/grpcurl:$(dev_build_version) .
@rm VERSION
.PHONY: generate
generate: .tmp/protoc/bin/protoc
@go install google.golang.org/protobuf/cmd/protoc-gen-go@a709e31e5d12
@go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1.0
@go install github.com/jhump/protoreflect/desc/sourceinfo/cmd/protoc-gen-gosrcinfo@v1.14.1
go generate ./...
go mod tidy
.PHONY: checkgenerate
checkgenerate: generate
git status --porcelain -- '**/*.go'
@if [ -n "$$(git status --porcelain -- '**/*.go')" ]; then \
git diff -- '**/*.go'; \
exit 1; \
fi
.PHONY: checkgofmt
checkgofmt:
gofmt -s -l .
@if [ -n "$$(gofmt -s -l .)" ]; then \
exit 1; \
fi
.PHONY: vet
vet:
go vet ./...
.PHONY: staticcheck
staticcheck:
@go install honnef.co/go/tools/cmd/staticcheck@2025.1.1
staticcheck -checks "inherit,-SA1019" ./...
.PHONY: ineffassign
ineffassign:
@go install github.com/gordonklaus/ineffassign@7953dde2c7bf
ineffassign .
.PHONY: predeclared
predeclared:
@go install github.com/nishanths/predeclared@51e8c974458a0f93dc03fe356f91ae1a6d791e6f
predeclared ./...
# Intentionally omitted from CI, but target here for ad-hoc reports.
.PHONY: golint
golint:
@go install golang.org/x/lint/golint@v0.0.0-20210508222113-6edffad5e616
golint -min_confidence 0.9 -set_exit_status ./...
# Intentionally omitted from CI, but target here for ad-hoc reports.
.PHONY: errcheck
errcheck:
@go install github.com/kisielk/errcheck@v1.2.0
errcheck ./...
.PHONY: test
test: deps
CGO_ENABLED=1 go test -race ./...
.tmp/protoc/bin/protoc: ./Makefile ./download_protoc.sh
./download_protoc.sh
================================================
FILE: README.md
================================================
# gRPCurl
[](https://circleci.com/gh/fullstorydev/grpcurl/tree/master)
[](https://goreportcard.com/report/github.com/fullstorydev/grpcurl)
[](https://snapcraft.io/grpcurl)
`grpcurl` is a command-line tool that lets you interact with gRPC servers. It's
basically `curl` for gRPC servers.
The main purpose for this tool is to invoke RPC methods on a gRPC server from the
command-line. gRPC servers use a binary encoding on the wire
([protocol buffers](https://developers.google.com/protocol-buffers/), or "protobufs"
for short). So they are basically impossible to interact with using regular `curl`
(and older versions of `curl` that do not support HTTP/2 are of course non-starters).
This program accepts messages using JSON encoding, which is much more friendly for both
humans and scripts.
With this tool you can also browse the schema for gRPC services, either by querying
a server that supports [server reflection](https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1/reflection.proto),
by reading proto source files, or by loading in compiled "protoset" files (files that contain
encoded file [descriptor protos](https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto)).
In fact, the way the tool transforms JSON request data into a binary encoded protobuf
is using that very same schema. So, if the server you interact with does not support
reflection, you will either need the proto source files that define the service or need
protoset files that `grpcurl` can use.
This repo also provides a library package, `github.com/fullstorydev/grpcurl`, that has
functions for simplifying the construction of other command-line tools that dynamically
invoke gRPC endpoints. This code is a great example of how to use the various packages of
the [protoreflect](https://godoc.org/github.com/jhump/protoreflect) library, and shows
off what they can do.
See also the [`grpcurl` talk at GopherCon 2018](https://www.youtube.com/watch?v=dDr-8kbMnaw).
## Features
`grpcurl` supports all kinds of RPC methods, including streaming methods. You can even
operate bi-directional streaming methods interactively by running `grpcurl` from an
interactive terminal and using stdin as the request body!
`grpcurl` supports both secure/TLS servers _and_ plain-text servers (i.e. no TLS) and has
numerous options for TLS configuration. It also supports mutual TLS, where the client is
required to present a client certificate.
As mentioned above, `grpcurl` works seamlessly if the server supports the reflection
service. If not, you can supply the `.proto` source files or you can supply protoset
files (containing compiled descriptors, produced by `protoc`) to `grpcurl`.
## Installation
### Binaries
Download the binary from the [releases](https://github.com/fullstorydev/grpcurl/releases) page.
### Homebrew (macOS)
On macOS, `grpcurl` is available via Homebrew:
```shell
brew install grpcurl
```
### Docker
For platforms that support Docker, you can download an image that lets you run `grpcurl`:
```shell
# Download image
docker pull fullstorydev/grpcurl:latest
# Run the tool
docker run fullstorydev/grpcurl api.grpc.me:443 list
```
Note that there are some pitfalls when using docker:
- 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).
- 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.
- 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.
### Other Packages
There are numerous other ways to install `grpcurl`, thanks to support from third parties that
have created recipes/packages for it. These include other ways to install `grpcurl` on a variety
of environments, including Windows and myriad Linux distributions.
You can see more details and the full list of other packages for `grpcurl` at _repology.org_:
https://repology.org/project/grpcurl/information
### Snap
You can install `grpcurl` using the snap package:
`snap install grpcurl`
### From Source
If you already have the [Go SDK](https://golang.org/doc/install) installed, you can use the `go`
tool to install `grpcurl`:
```shell
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
```
This installs the command into the `bin` sub-folder of wherever your `$GOPATH`
environment variable points. (If you have no `GOPATH` environment variable set,
the default install location is `$HOME/go/bin`). If this directory is already in
your `$PATH`, then you should be good to go.
If you have already pulled down this repo to a location that is not in your
`$GOPATH` and want to build from the sources, you can `cd` into the repo and then
run `make install`.
If you encounter compile errors and are using a version of the Go SDK older than 1.13,
you could have out-dated versions of `grpcurl`'s dependencies. You can update the
dependencies by running `make updatedeps`. Or, if you are using Go 1.11 or 1.12, you
can add `GO111MODULE=on` as a prefix to the commands above, which will also build using
the right versions of dependencies (vs. whatever you may already have in your `GOPATH`).
## Usage
The usage doc for the tool explains the numerous options:
```shell
grpcurl -help
```
In the sections below, you will find numerous examples demonstrating how to use
`grpcurl`.
### Invoking RPCs
Invoking an RPC on a trusted server (e.g. TLS without self-signed key or custom CA)
that requires no client certs and supports server reflection is the simplest thing to
do with `grpcurl`. This minimal invocation sends an empty request body:
```shell
grpcurl grpc.server.com:443 my.custom.server.Service/Method
# no TLS
grpcurl -plaintext grpc.server.com:80 my.custom.server.Service/Method
```
To send a non-empty request, use the `-d` argument. Note that all arguments must come
*before* the server address and method name:
```shell
grpcurl -d '{"id": 1234, "tags": ["foo","bar"]}' \
grpc.server.com:443 my.custom.server.Service/Method
```
As can be seen in the example, the supplied body must be in JSON format. The body will
be parsed and then transmitted to the server in the protobuf binary format.
If you want to include `grpcurl` in a command pipeline, such as when using `jq` to
create a request body, you can use `-d @`, which tells `grpcurl` to read the actual
request body from stdin:
```shell
grpcurl -d @ grpc.server.com:443 my.custom.server.Service/Method <<EOM
{
"id": 1234,
"tags": [
"foor",
"bar"
]
}
EOM
```
### Adding Headers/Metadata to Request
Adding 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.
Example :
```shell
grpcurl -H header1:value1 -H header2:value2 -d '{"id": 1234, "tags": ["foo","bar"]}' grpc.server.com:443 my.custom.server.Service/Method
```
For more usage guide, check out the help docs via `grpcurl -help`
### Listing Services
To list all services exposed by a server, use the "list" verb. When using `.proto` source
or protoset files instead of server reflection, this lists all services defined in the
source or protoset files.
```shell
# Server supports reflection
grpcurl localhost:8787 list
# Using compiled protoset files
grpcurl -protoset my-protos.bin list
# Using proto sources
grpcurl -import-path ../protos -proto my-stuff.proto list
# Export proto files (use -proto-out-dir to specify the output directory)
grpcurl -plaintext -proto-out-dir "out_protos" "localhost:8787" describe my.custom.server.Service
# Export protoset file (use -protoset-out to specify the output file)
grpcurl -plaintext -protoset-out "out.protoset" "localhost:8787" describe my.custom.server.Service
```
The "list" verb also lets you see all methods in a particular service:
```shell
grpcurl localhost:8787 list my.custom.server.Service
```
### Describing Elements
The "describe" verb will print the type of any symbol that the server knows about
or that is found in a given protoset file. It also prints a description of that
symbol, in the form of snippets of proto source. It won't necessarily be the
original source that defined the element, but it will be equivalent.
```shell
# Server supports reflection
grpcurl localhost:8787 describe my.custom.server.Service.MethodOne
# Using compiled protoset files
grpcurl -protoset my-protos.bin describe my.custom.server.Service.MethodOne
# Using proto sources
grpcurl -import-path ../protos -proto my-stuff.proto describe my.custom.server.Service.MethodOne
```
## Descriptor Sources
The `grpcurl` tool can operate on a variety of sources for descriptors. The descriptors
are required, in order for `grpcurl` to understand the RPC schema, translate inputs
into the protobuf binary format as well as translate responses from the binary format
into text. The sections below document the supported sources and what command-line flags
are needed to use them.
### Server Reflection
Without 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).
Examples for how to set up server reflection can be found [here](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md#known-implementations).
When using reflection, the server address (host:port or path to Unix socket) is required
even for "list" and "describe" operations, so that `grpcurl` can connect to the server
and ask it for its descriptors.
### Proto Source Files
To use `grpcurl` on servers that do not support reflection, you can use `.proto` source
files.
In addition to using `-proto` flags to point `grpcurl` at the relevant proto source file(s),
you may also need to supply `-import-path` flags to tell `grpcurl` the folders from which
dependencies can be imported.
Just like when compiling with `protoc`, you do *not* need to provide an import path for the
location of the standard protos included with `protoc` (which contain various "well-known
types" with a package definition of `google.protobuf`). These files are "known" by `grpcurl`
as a snapshot of their descriptors is built into the `grpcurl` binary.
When using proto sources, you can omit the server address (host:port or path to Unix socket)
when using the "list" and "describe" operations since they only need to consult the proto
source files.
### Protoset Files
You can also use compiled protoset files with `grpcurl`. If you are scripting `grpcurl` and
need to re-use the same proto sources for many invocations, you will see better performance
by using protoset files (since it skips the parsing and compilation steps with each
invocation).
Protoset files contain binary encoded `google.protobuf.FileDescriptorSet` protos. To create
a protoset file, invoke `protoc` with the `*.proto` files that define the service:
```shell
protoc --proto_path=. \
--descriptor_set_out=myservice.protoset \
--include_imports \
my/custom/server/service.proto
```
The `--descriptor_set_out` argument is what tells `protoc` to produce a protoset,
and the `--include_imports` argument is necessary for the protoset to contain
everything that `grpcurl` needs to process and understand the schema.
When using protosets, you can omit the server address (host:port or path to Unix socket)
when using the "list" and "describe" operations since they only need to consult the
protoset files.
================================================
FILE: cmd/grpcurl/grpcurl.go
================================================
// Command grpcurl makes gRPC requests (a la cURL, but HTTP/2). It can use a supplied descriptor
// file, protobuf sources, or service reflection to translate JSON or text request data into the
// appropriate protobuf messages and vice versa for presenting the response contents.
package main
import (
"context"
"flag"
"fmt"
"io"
"math"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 required to use APIs in other grpcurl package
"github.com/jhump/protoreflect/grpcreflect"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/alts"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/descriptorpb"
// Register gzip compressor so compressed responses will work
_ "google.golang.org/grpc/encoding/gzip"
// Register xds so xds and xds-experimental resolver schemes work
_ "google.golang.org/grpc/xds"
"github.com/fullstorydev/grpcurl"
)
// To avoid confusion between program error codes and the gRPC response
// status codes 'Cancelled' and 'Unknown', 1 and 2 respectively,
// the response status codes emitted use an offset of 64
const statusCodeOffset = 64
const noVersion = "dev build <no version set>"
var version = noVersion
var (
exit = os.Exit
isUnixSocket func() bool // nil when run on non-unix platform
flags = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
help = flags.Bool("help", false, prettify(`
Print usage instructions and exit.`))
printVersion = flags.Bool("version", false, prettify(`
Print version.`))
plaintext = flags.Bool("plaintext", false, prettify(`
Use plain-text HTTP/2 when connecting to server (no TLS).`))
insecure = flags.Bool("insecure", false, prettify(`
Skip server certificate and domain verification. (NOT SECURE!) Not
valid with -plaintext option.`))
// TLS Options
cacert = flags.String("cacert", "", prettify(`
File containing trusted root certificates for verifying the server.
Ignored if -insecure is specified.`))
cert = flags.String("cert", "", prettify(`
File containing client certificate (public key), to present to the
server. Not valid with -plaintext option. Must also provide -key option.`))
key = flags.String("key", "", prettify(`
File containing client private key, to present to the server. Not valid
with -plaintext option. Must also provide -cert option.`))
// ALTS Options
usealts = flags.Bool("alts", false, prettify(`
Use Application Layer Transport Security (ALTS) when connecting to server.`))
altsHandshakerServiceAddress = flags.String("alts-handshaker-service", "", prettify(`If set, this server will be used to do the ATLS handshaking.`))
altsTargetServiceAccounts multiString
protoset multiString
protoFiles multiString
importPaths multiString
addlHeaders multiString
rpcHeaders multiString
reflHeaders multiString
expandHeaders = flags.Bool("expand-headers", false, prettify(`
If set, headers may use '${NAME}' syntax to reference environment
variables. These will be expanded to the actual environment variable
value before sending to the server. For example, if there is an
environment variable defined like FOO=bar, then a header of
'key: ${FOO}' would expand to 'key: bar'. This applies to -H,
-rpc-header, and -reflect-header options. No other expansion/escaping is
performed. This can be used to supply credentials/secrets without having
to put them in command-line arguments.`))
authority = flags.String("authority", "", prettify(`
The authoritative name of the remote server. This value is passed as the
value of the ":authority" pseudo-header in the HTTP/2 protocol. When TLS
is used, this will also be used as the server name when verifying the
server's certificate. It defaults to the address that is provided in the
positional arguments, or 'localhost' in the case of a unix domain
socket.`))
userAgent = flags.String("user-agent", "", prettify(`
If set, the specified value will be added to the User-Agent header set
by the grpc-go library.
`))
data = flags.String("d", "", prettify(`
Data for request contents. If the value is '@' then the request contents
are read from stdin. For calls that accept a stream of requests, the
contents should include all such request messages concatenated together
(possibly delimited; see -format).`))
format = flags.String("format", "json", prettify(`
The format of request data. The allowed values are 'json' or 'text'. For
'json', the input data must be in JSON format. Multiple request values
may be concatenated (messages with a JSON representation other than
object must be separated by whitespace, such as a newline). For 'text',
the input data must be in the protobuf text format, in which case
multiple request values must be separated by the "record separator"
ASCII character: 0x1E. The stream should not end in a record separator.
If it does, it will be interpreted as a final, blank message after the
separator.`))
allowUnknownFields = flags.Bool("allow-unknown-fields", false, prettify(`
When true, the request contents, if 'json' format is used, allows
unknown fields to be present. They will be ignored when parsing
the request.`))
connectTimeout = flags.Float64("connect-timeout", 0, prettify(`
The maximum time, in seconds, to wait for connection to be established.
Defaults to 10 seconds.`))
formatError = flags.Bool("format-error", false, prettify(`
When a non-zero status is returned, format the response using the
value set by the -format flag .`))
keepaliveTime = flags.Float64("keepalive-time", 0, prettify(`
If present, the maximum idle time in seconds, after which a keepalive
probe is sent. If the connection remains idle and no keepalive response
is received for this same period then the connection is closed and the
operation fails.`))
maxTime = flags.Float64("max-time", 0, prettify(`
The maximum total time the operation can take, in seconds. This sets a
timeout on the gRPC context, allowing both client and server to give up
after the deadline has past. This is useful for preventing batch jobs
that use grpcurl from hanging due to slow or bad network links or due
to incorrect stream method usage.`))
maxMsgSz = flags.Int("max-msg-sz", 0, prettify(`
The maximum encoded size of a response message, in bytes, that grpcurl
will accept. If not specified, defaults to 4,194,304 (4 megabytes).`))
emitDefaults = flags.Bool("emit-defaults", false, prettify(`
Emit default values for JSON-encoded responses.`))
protosetOut = flags.String("protoset-out", "", prettify(`
The name of a file to be written that will contain a FileDescriptorSet
proto. With the list and describe verbs, the listed or described
elements and their transitive dependencies will be written to the named
file if this option is given. When invoking an RPC and this option is
given, the method being invoked and its transitive dependencies will be
included in the output file.`))
protoOut = flags.String("proto-out-dir", "", prettify(`
The name of a directory where the generated .proto files will be written.
With the list and describe verbs, the listed or described elements and
their transitive dependencies will be written as .proto files in the
specified directory if this option is given. When invoking an RPC and
this option is given, the method being invoked and its transitive
dependencies will be included in the generated .proto files in the
output directory.`))
msgTemplate = flags.Bool("msg-template", false, prettify(`
When describing messages, show a template of input data.`))
verbose = flags.Bool("v", false, prettify(`
Enable verbose output.`))
veryVerbose = flags.Bool("vv", false, prettify(`
Enable very verbose output (includes timing data).`))
serverName = flags.String("servername", "", prettify(`
Override server name when validating TLS certificate. This flag is
ignored if -plaintext or -insecure is used.
NOTE: Prefer -authority. This flag may be removed in the future. It is
an error to use both -authority and -servername (though this will be
permitted if they are both set to the same value, to increase backwards
compatibility with earlier releases that allowed both to be set).`))
reflection = optionalBoolFlag{val: true}
)
func init() {
flags.Var(&addlHeaders, "H", prettify(`
Additional headers in 'name: value' format. May specify more than one
via multiple flags. These headers will also be included in reflection
requests to a server.`))
flags.Var(&rpcHeaders, "rpc-header", prettify(`
Additional RPC headers in 'name: value' format. May specify more than
one via multiple flags. These headers will *only* be used when invoking
the requested RPC method. They are excluded from reflection requests.`))
flags.Var(&reflHeaders, "reflect-header", prettify(`
Additional reflection headers in 'name: value' format. May specify more
than one via multiple flags. These headers will *only* be used during
reflection requests and will be excluded when invoking the requested RPC
method.`))
flags.Var(&protoset, "protoset", prettify(`
The name of a file containing an encoded FileDescriptorSet. This file's
contents will be used to determine the RPC schema instead of querying
for it from the remote server via the gRPC reflection API. When set: the
'list' action lists the services found in the given descriptors (vs.
those exposed by the remote server), and the 'describe' action describes
symbols found in the given descriptors. May specify more than one via
multiple -protoset flags. It is an error to use both -protoset and
-proto flags.`))
flags.Var(&protoFiles, "proto", prettify(`
The name of a proto source file. Source files given will be used to
determine the RPC schema instead of querying for it from the remote
server via the gRPC reflection API. When set: the 'list' action lists
the services found in the given files and their imports (vs. those
exposed by the remote server), and the 'describe' action describes
symbols found in the given files. May specify more than one via multiple
-proto flags. Imports will be resolved using the given -import-path
flags. Multiple proto files can be specified by specifying multiple
-proto flags. It is an error to use both -protoset and -proto flags.`))
flags.Var(&importPaths, "import-path", prettify(`
The path to a directory from which proto sources can be imported, for
use with -proto flags. Multiple import paths can be configured by
specifying multiple -import-path flags. Paths will be searched in the
order given. If no import paths are given, all files (including all
imports) must be provided as -proto flags, and grpcurl will attempt to
resolve all import statements from the set of file names given.`))
flags.Var(&reflection, "use-reflection", prettify(`
When true, server reflection will be used to determine the RPC schema.
Defaults to true unless a -proto or -protoset option is provided. If
-use-reflection is used in combination with a -proto or -protoset flag,
the provided descriptor sources will be used in addition to server
reflection to resolve messages and extensions.`))
flags.Var(&altsTargetServiceAccounts, "alts-target-service-account", prettify(`
The full email address of the service account that the server is
expected to be using when ALTS is used. You can specify this option
multiple times to indicate multiple allowed service accounts. If the
server authenticates with a service account that is not one of the
expected accounts, the RPC will not be issued. If no such arguments are
provided, no check will be performed, and the RPC will be issued
regardless of the server's service account.`))
}
type multiString []string
func (s *multiString) String() string {
return strings.Join(*s, ",")
}
func (s *multiString) Set(value string) error {
*s = append(*s, value)
return nil
}
// Uses a file source as a fallback for resolving symbols and extensions, but
// only uses the reflection source for listing services
type compositeSource struct {
reflection grpcurl.DescriptorSource
file grpcurl.DescriptorSource
}
func (cs compositeSource) ListServices() ([]string, error) {
return cs.reflection.ListServices()
}
func (cs compositeSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) {
d, err := cs.reflection.FindSymbol(fullyQualifiedName)
if err == nil {
return d, nil
}
return cs.file.FindSymbol(fullyQualifiedName)
}
func (cs compositeSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) {
exts, err := cs.reflection.AllExtensionsForType(typeName)
if err != nil {
// On error fall back to file source
return cs.file.AllExtensionsForType(typeName)
}
// Track the tag numbers from the reflection source
tags := make(map[int32]bool)
for _, ext := range exts {
tags[ext.GetNumber()] = true
}
fileExts, err := cs.file.AllExtensionsForType(typeName)
if err != nil {
return exts, nil
}
for _, ext := range fileExts {
// Prioritize extensions found via reflection
if !tags[ext.GetNumber()] {
exts = append(exts, ext)
}
}
return exts, nil
}
type timingData struct {
Title string
Start time.Time
Value time.Duration
Parent *timingData
Sub []*timingData
}
func (d *timingData) Child(title string) *timingData {
if d == nil {
return nil
}
child := &timingData{Title: title, Start: time.Now()}
d.Sub = append(d.Sub, child)
return child
}
func (d *timingData) Done() {
if d == nil {
return
}
if d.Value == 0 {
d.Value = time.Since(d.Start)
}
}
func main() {
flags.Usage = usage
flags.Parse(os.Args[1:])
if *help {
usage()
os.Exit(0)
}
if *printVersion {
fmt.Fprintf(os.Stderr, "%s %s\n", filepath.Base(os.Args[0]), version)
os.Exit(0)
}
// default behavior is to use tls
usetls := !*plaintext && !*usealts
// Do extra validation on arguments and figure out what user asked us to do.
if *connectTimeout < 0 {
fail(nil, "The -connect-timeout argument must not be negative.")
}
if *keepaliveTime < 0 {
fail(nil, "The -keepalive-time argument must not be negative.")
}
if *maxTime < 0 {
fail(nil, "The -max-time argument must not be negative.")
}
if *maxMsgSz < 0 {
fail(nil, "The -max-msg-sz argument must not be negative.")
}
if *plaintext && *usealts {
fail(nil, "The -plaintext and -alts arguments are mutually exclusive.")
}
if *insecure && !usetls {
fail(nil, "The -insecure argument can only be used with TLS.")
}
if *cert != "" && !usetls {
fail(nil, "The -cert argument can only be used with TLS.")
}
if *key != "" && !usetls {
fail(nil, "The -key argument can only be used with TLS.")
}
if (*key == "") != (*cert == "") {
fail(nil, "The -cert and -key arguments must be used together and both be present.")
}
if *altsHandshakerServiceAddress != "" && !*usealts {
fail(nil, "The -alts-handshaker-service argument must be used with the -alts argument.")
}
if len(altsTargetServiceAccounts) > 0 && !*usealts {
fail(nil, "The -alts-target-service-account argument must be used with the -alts argument.")
}
if *format != "json" && *format != "text" {
fail(nil, "The -format option must be 'json' or 'text'.")
}
if *emitDefaults && *format != "json" {
warn("The -emit-defaults is only used when using json format.")
}
args := flags.Args()
if len(args) == 0 {
fail(nil, "Too few arguments.")
}
var target string
if args[0] != "list" && args[0] != "describe" {
target = args[0]
args = args[1:]
}
if len(args) == 0 {
fail(nil, "Too few arguments.")
}
var list, describe, invoke bool
if args[0] == "list" {
list = true
args = args[1:]
} else if args[0] == "describe" {
describe = true
args = args[1:]
} else {
invoke = true
}
verbosityLevel := 0
if *verbose {
verbosityLevel = 1
}
var rootTiming *timingData
if *veryVerbose {
verbosityLevel = 2
rootTiming = &timingData{Title: "Timing Data", Start: time.Now()}
defer func() {
rootTiming.Done()
dumpTiming(rootTiming, 0)
}()
}
var symbol string
if invoke {
if len(args) == 0 {
fail(nil, "Too few arguments.")
}
symbol = args[0]
args = args[1:]
} else {
if *data != "" {
warn("The -d argument is not used with 'list' or 'describe' verb.")
}
if len(rpcHeaders) > 0 {
warn("The -rpc-header argument is not used with 'list' or 'describe' verb.")
}
if len(args) > 0 {
symbol = args[0]
args = args[1:]
}
}
if len(args) > 0 {
fail(nil, "Too many arguments.")
}
if invoke && target == "" {
fail(nil, "No host:port specified.")
}
if len(protoset) == 0 && len(protoFiles) == 0 && target == "" {
fail(nil, "No host:port specified, no protoset specified, and no proto sources specified.")
}
if len(protoset) > 0 && len(reflHeaders) > 0 {
warn("The -reflect-header argument is not used when -protoset files are used.")
}
if len(protoset) > 0 && len(protoFiles) > 0 {
fail(nil, "Use either -protoset files or -proto files, but not both.")
}
if len(importPaths) > 0 && len(protoFiles) == 0 {
warn("The -import-path argument is not used unless -proto files are used.")
}
if !reflection.val && len(protoset) == 0 && len(protoFiles) == 0 {
fail(nil, "No protoset files or proto files specified and -use-reflection set to false.")
}
// Protoset or protofiles provided and -use-reflection unset
if !reflection.set && (len(protoset) > 0 || len(protoFiles) > 0) {
reflection.val = false
}
ctx := context.Background()
if *maxTime > 0 {
timeout := floatSecondsToDuration(*maxTime)
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel()
}
dial := func() *grpc.ClientConn {
dialTiming := rootTiming.Child("Dial")
defer dialTiming.Done()
dialTime := 10 * time.Second
if *connectTimeout > 0 {
dialTime = floatSecondsToDuration(*connectTimeout)
}
ctx, cancel := context.WithTimeout(ctx, dialTime)
defer cancel()
var opts []grpc.DialOption
if *keepaliveTime > 0 {
timeout := floatSecondsToDuration(*keepaliveTime)
opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: timeout,
Timeout: timeout,
}))
}
if *maxMsgSz > 0 {
opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*maxMsgSz)))
}
if isUnixSocket != nil && isUnixSocket() && !strings.HasPrefix(target, "unix://") {
// prepend unix:// to the address if it's not already there
// this is to maintain backwards compatibility because the custom dialer is replaced by
// the default dialer in grpc-go.
// https://github.com/fullstorydev/grpcurl/pull/480
target = "unix://" + target
}
var creds credentials.TransportCredentials
if *plaintext {
if *authority != "" {
opts = append(opts, grpc.WithAuthority(*authority))
}
} else if *usealts {
clientOptions := alts.DefaultClientOptions()
if len(altsTargetServiceAccounts) > 0 {
clientOptions.TargetServiceAccounts = altsTargetServiceAccounts
}
if *altsHandshakerServiceAddress != "" {
clientOptions.HandshakerServiceAddress = *altsHandshakerServiceAddress
}
creds = alts.NewClientCreds(clientOptions)
} else if usetls {
tlsTiming := dialTiming.Child("TLS Setup")
defer tlsTiming.Done()
tlsConf, err := grpcurl.ClientTLSConfig(*insecure, *cacert, *cert, *key)
if err != nil {
fail(err, "Failed to create TLS config")
}
sslKeylogFile := os.Getenv("SSLKEYLOGFILE")
if sslKeylogFile != "" {
w, err := os.OpenFile(sslKeylogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
fail(err, "Could not open SSLKEYLOGFILE %s", sslKeylogFile)
}
tlsConf.KeyLogWriter = w
}
creds = credentials.NewTLS(tlsConf)
// can use either -servername or -authority; but not both
if *serverName != "" && *authority != "" {
if *serverName == *authority {
warn("Both -servername and -authority are present; prefer only -authority.")
} else {
fail(nil, "Cannot specify different values for -servername and -authority.")
}
}
overrideName := *serverName
if overrideName == "" {
overrideName = *authority
}
if overrideName != "" {
opts = append(opts, grpc.WithAuthority(overrideName))
}
tlsTiming.Done()
} else {
panic("Should have defaulted to use TLS.")
}
grpcurlUA := "grpcurl/" + version
if version == noVersion {
grpcurlUA = "grpcurl/dev-build (no version set)"
}
if *userAgent != "" {
grpcurlUA = *userAgent + " " + grpcurlUA
}
opts = append(opts, grpc.WithUserAgent(grpcurlUA))
blockingDialTiming := dialTiming.Child("BlockingDial")
defer blockingDialTiming.Done()
cc, err := grpcurl.BlockingDial(ctx, "", target, creds, opts...)
if err != nil {
fail(err, "Failed to dial target host %q", target)
}
return cc
}
printFormattedStatus := func(w io.Writer, stat *status.Status, formatter grpcurl.Formatter) {
formattedStatus, err := formatter(stat.Proto())
if err != nil {
fmt.Fprintf(w, "ERROR: %v", err.Error())
}
fmt.Fprint(w, formattedStatus)
}
if *expandHeaders {
var err error
addlHeaders, err = grpcurl.ExpandHeaders(addlHeaders)
if err != nil {
fail(err, "Failed to expand additional headers")
}
rpcHeaders, err = grpcurl.ExpandHeaders(rpcHeaders)
if err != nil {
fail(err, "Failed to expand rpc headers")
}
reflHeaders, err = grpcurl.ExpandHeaders(reflHeaders)
if err != nil {
fail(err, "Failed to expand reflection headers")
}
}
var cc *grpc.ClientConn
var descSource grpcurl.DescriptorSource
var refClient *grpcreflect.Client
var fileSource grpcurl.DescriptorSource
if len(protoset) > 0 {
var err error
fileSource, err = grpcurl.DescriptorSourceFromProtoSets(protoset...)
if err != nil {
fail(err, "Failed to process proto descriptor sets.")
}
} else if len(protoFiles) > 0 {
var err error
fileSource, err = grpcurl.DescriptorSourceFromProtoFiles(importPaths, protoFiles...)
if err != nil {
fail(err, "Failed to process proto source files.")
}
}
if reflection.val {
md := grpcurl.MetadataFromHeaders(append(addlHeaders, reflHeaders...))
refCtx := metadata.NewOutgoingContext(ctx, md)
cc = dial()
refClient = grpcreflect.NewClientAuto(refCtx, cc)
refClient.AllowMissingFileDescriptors()
reflSource := grpcurl.DescriptorSourceFromServer(ctx, refClient)
if fileSource != nil {
descSource = compositeSource{reflSource, fileSource}
} else {
descSource = reflSource
}
} else {
descSource = fileSource
}
// arrange for the RPCs to be cleanly shutdown
reset := func() {
if refClient != nil {
refClient.Reset()
refClient = nil
}
if cc != nil {
cc.Close()
cc = nil
}
}
defer reset()
exit = func(code int) {
// since defers aren't run by os.Exit...
reset()
os.Exit(code)
}
if list {
if symbol == "" {
svcs, err := grpcurl.ListServices(descSource)
if err != nil {
fail(err, "Failed to list services")
}
if len(svcs) == 0 {
fmt.Println("(No services)")
} else {
for _, svc := range svcs {
fmt.Printf("%s\n", svc)
}
}
if err := writeProtoset(descSource, svcs...); err != nil {
fail(err, "Failed to write protoset to %s", *protosetOut)
}
if err := writeProtos(descSource, svcs...); err != nil {
fail(err, "Failed to write protos to %s", *protoOut)
}
} else {
methods, err := grpcurl.ListMethods(descSource, symbol)
if err != nil {
fail(err, "Failed to list methods for service %q", symbol)
}
if len(methods) == 0 {
fmt.Println("(No methods)") // probably unlikely
} else {
for _, m := range methods {
fmt.Printf("%s\n", m)
}
}
if err := writeProtoset(descSource, symbol); err != nil {
fail(err, "Failed to write protoset to %s", *protosetOut)
}
if err := writeProtos(descSource, symbol); err != nil {
fail(err, "Failed to write protos to %s", *protoOut)
}
}
} else if describe {
var symbols []string
if symbol != "" {
symbols = []string{symbol}
} else {
// if no symbol given, describe all exposed services
svcs, err := descSource.ListServices()
if err != nil {
fail(err, "Failed to list services")
}
if len(svcs) == 0 {
fmt.Println("Server returned an empty list of exposed services")
}
symbols = svcs
}
for _, s := range symbols {
if s[0] == '.' {
s = s[1:]
}
dsc, err := descSource.FindSymbol(s)
if err != nil {
fail(err, "Failed to resolve symbol %q", s)
}
fqn := dsc.GetFullyQualifiedName()
var elementType string
switch d := dsc.(type) {
case *desc.MessageDescriptor:
elementType = "a message"
parent, ok := d.GetParent().(*desc.MessageDescriptor)
if ok {
if d.IsMapEntry() {
for _, f := range parent.GetFields() {
if f.IsMap() && f.GetMessageType() == d {
// found it: describe the map field instead
elementType = "the entry type for a map field"
dsc = f
break
}
}
} else {
// see if it's a group
for _, f := range parent.GetFields() {
if f.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP && f.GetMessageType() == d {
// found it: describe the map field instead
elementType = "the type of a group field"
dsc = f
break
}
}
}
}
case *desc.FieldDescriptor:
elementType = "a field"
if d.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP {
elementType = "a group field"
} else if d.IsExtension() {
elementType = "an extension"
}
case *desc.OneOfDescriptor:
elementType = "a one-of"
case *desc.EnumDescriptor:
elementType = "an enum"
case *desc.EnumValueDescriptor:
elementType = "an enum value"
case *desc.ServiceDescriptor:
elementType = "a service"
case *desc.MethodDescriptor:
elementType = "a method"
default:
err = fmt.Errorf("descriptor has unrecognized type %T", dsc)
fail(err, "Failed to describe symbol %q", s)
}
txt, err := grpcurl.GetDescriptorText(dsc, descSource)
if err != nil {
fail(err, "Failed to describe symbol %q", s)
}
fmt.Printf("%s is %s:\n", fqn, elementType)
fmt.Println(txt)
if dsc, ok := dsc.(*desc.MessageDescriptor); ok && *msgTemplate {
// for messages, also show a template in JSON, to make it easier to
// create a request to invoke an RPC
tmpl := grpcurl.MakeTemplate(dsc)
options := grpcurl.FormatOptions{EmitJSONDefaultFields: true}
_, formatter, err := grpcurl.RequestParserAndFormatter(grpcurl.Format(*format), descSource, nil, options)
if err != nil {
fail(err, "Failed to construct formatter for %q", *format)
}
str, err := formatter(tmpl)
if err != nil {
fail(err, "Failed to print template for message %s", s)
}
fmt.Println("\nMessage template:")
fmt.Println(str)
}
}
if err := writeProtoset(descSource, symbols...); err != nil {
fail(err, "Failed to write protoset to %s", *protosetOut)
}
if err := writeProtos(descSource, symbol); err != nil {
fail(err, "Failed to write protos to %s", *protoOut)
}
} else {
// Invoke an RPC
if cc == nil {
cc = dial()
}
var in io.Reader
if *data == "@" {
in = os.Stdin
} else {
in = strings.NewReader(*data)
}
// if not verbose output, then also include record delimiters
// between each message, so output could potentially be piped
// to another grpcurl process
includeSeparators := verbosityLevel == 0
options := grpcurl.FormatOptions{
EmitJSONDefaultFields: *emitDefaults,
IncludeTextSeparator: includeSeparators,
AllowUnknownFields: *allowUnknownFields,
}
rf, formatter, err := grpcurl.RequestParserAndFormatter(grpcurl.Format(*format), descSource, in, options)
if err != nil {
fail(err, "Failed to construct request parser and formatter for %q", *format)
}
h := &grpcurl.DefaultEventHandler{
Out: os.Stdout,
Formatter: formatter,
VerbosityLevel: verbosityLevel,
}
invokeTiming := rootTiming.Child("InvokeRPC")
err = grpcurl.InvokeRPC(ctx, descSource, cc, symbol, append(addlHeaders, rpcHeaders...), h, rf.Next)
invokeTiming.Done()
if err != nil {
if errStatus, ok := status.FromError(err); ok && *formatError {
h.Status = errStatus
} else {
fail(err, "Error invoking method %q", symbol)
}
}
reqSuffix := ""
respSuffix := ""
reqCount := rf.NumRequests()
if reqCount != 1 {
reqSuffix = "s"
}
if h.NumResponses != 1 {
respSuffix = "s"
}
if verbosityLevel > 0 {
fmt.Printf("Sent %d request%s and received %d response%s\n", reqCount, reqSuffix, h.NumResponses, respSuffix)
}
if h.Status.Code() != codes.OK {
if *formatError {
printFormattedStatus(os.Stderr, h.Status, formatter)
} else {
grpcurl.PrintStatus(os.Stderr, h.Status, formatter)
}
exit(statusCodeOffset + int(h.Status.Code()))
}
}
}
func dumpTiming(td *timingData, lvl int) {
var ind strings.Builder
for x := 0; x < lvl; x++ {
ind.WriteString(" ")
}
fmt.Printf("%s%s: %s\n", ind.String(), td.Title, td.Value)
for _, sd := range td.Sub {
dumpTiming(sd, lvl+1)
}
}
func usage() {
fmt.Fprintf(os.Stderr, `Usage:
%s [flags] [address] [list|describe] [symbol]
The 'address' is only optional when used with 'list' or 'describe' and a
protoset or proto flag is provided.
If 'list' is indicated, the symbol (if present) should be a fully-qualified
service name. If present, all methods of that service are listed. If not
present, all exposed services are listed, or all services defined in protosets.
If 'describe' is indicated, the descriptor for the given symbol is shown. The
symbol should be a fully-qualified service, enum, or message name. If no symbol
is given then the descriptors for all exposed or known services are shown.
If neither verb is present, the symbol must be a fully-qualified method name in
'service/method' or 'service.method' format. In this case, the request body will
be used to invoke the named method. If no body is given but one is required
(i.e. the method is unary or server-streaming), an empty instance of the
method's request type will be sent.
The address will typically be in the form "host:port" where host can be an IP
address or a hostname and port is a numeric port or service name. If an IPv6
address is given, it must be surrounded by brackets, like "[2001:db8::1]". For
Unix variants, if a -unix=true flag is present, then the address must be the
path to the domain socket.
Available flags:
`, os.Args[0])
flags.PrintDefaults()
}
func prettify(docString string) string {
parts := strings.Split(docString, "\n")
// cull empty lines and also remove trailing and leading spaces
// from each line in the doc string
j := 0
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
parts[j] = part
j++
}
return strings.Join(parts[:j], "\n")
}
func warn(msg string, args ...interface{}) {
msg = fmt.Sprintf("Warning: %s\n", msg)
fmt.Fprintf(os.Stderr, msg, args...)
}
func fail(err error, msg string, args ...interface{}) {
if err != nil {
msg += ": %v"
args = append(args, err)
}
fmt.Fprintf(os.Stderr, msg, args...)
fmt.Fprintln(os.Stderr)
if err != nil {
exit(1)
} else {
// nil error means it was CLI usage issue
fmt.Fprintf(os.Stderr, "Try '%s -help' for more details.\n", os.Args[0])
exit(2)
}
}
func writeProtoset(descSource grpcurl.DescriptorSource, symbols ...string) error {
if *protosetOut == "" {
return nil
}
f, err := os.Create(*protosetOut)
if err != nil {
return err
}
defer f.Close()
return grpcurl.WriteProtoset(f, descSource, symbols...)
}
func writeProtos(descSource grpcurl.DescriptorSource, symbols ...string) error {
if *protoOut == "" {
return nil
}
return grpcurl.WriteProtoFiles(*protoOut, descSource, symbols...)
}
type optionalBoolFlag struct {
set, val bool
}
func (f *optionalBoolFlag) String() string {
if !f.set {
return "unset"
}
return strconv.FormatBool(f.val)
}
func (f *optionalBoolFlag) Set(s string) error {
v, err := strconv.ParseBool(s)
if err != nil {
return err
}
f.set = true
f.val = v
return nil
}
func (f *optionalBoolFlag) IsBoolFlag() bool {
return true
}
func floatSecondsToDuration(seconds float64) time.Duration {
durationFloat := seconds * float64(time.Second)
if durationFloat > math.MaxInt64 {
// Avoid overflow
return math.MaxInt64
}
return time.Duration(durationFloat)
}
================================================
FILE: cmd/grpcurl/indent_test.go
================================================
package main
import (
"bytes"
"flag"
"testing"
)
func TestFlagDocIndent(t *testing.T) {
// Tests the prettify() and indent() function. The indent() function
// differs by Go version, due to differences in "flags" package across
// versions. Run with multiple versions of Go to ensure that doc output
// is properly indented, regardless of Go version.
var fs flag.FlagSet
var buf bytes.Buffer
fs.SetOutput(&buf)
fs.String("foo", "", prettify(`
This is a flag doc string.
It has multiple lines.
More than two, actually.`))
fs.Int("bar", 100, prettify(`This is a simple flag doc string.`))
fs.Bool("baz", false, prettify(`
This is another long doc string.
It also has multiple lines. But not as long as the first one.`))
fs.PrintDefaults()
expected :=
` -bar int
This is a simple flag doc string. (default 100)
-baz
This is another long doc string.
It also has multiple lines. But not as long as the first one.
-foo string
This is a flag doc string.
It has multiple lines.
More than two, actually.
`
actual := buf.String()
if actual != expected {
t.Errorf("Flag output had wrong indentation.\nExpecting:\n%s\nGot:\n%s", expected, actual)
}
}
================================================
FILE: cmd/grpcurl/unix.go
================================================
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
package main
var (
unix = flags.Bool("unix", false, prettify(`
Indicates that the server address is the path to a Unix domain socket.`))
)
func init() {
isUnixSocket = func() bool {
return *unix
}
}
================================================
FILE: desc_source.go
================================================
package grpcurl
import (
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"sync"
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/desc/protoparse" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/desc/protoprint"
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/grpcreflect"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/descriptorpb"
)
// ErrReflectionNotSupported is returned by DescriptorSource operations that
// rely on interacting with the reflection service when the source does not
// actually expose the reflection service. When this occurs, an alternate source
// (like file descriptor sets) must be used.
var ErrReflectionNotSupported = errors.New("server does not support the reflection API")
// DescriptorSource is a source of protobuf descriptor information. It can be backed by a FileDescriptorSet
// proto (like a file generated by protoc) or a remote server that supports the reflection API.
type DescriptorSource interface {
// ListServices returns a list of fully-qualified service names. It will be all services in a set of
// descriptor files or the set of all services exposed by a gRPC server.
ListServices() ([]string, error)
// FindSymbol returns a descriptor for the given fully-qualified symbol name.
FindSymbol(fullyQualifiedName string) (desc.Descriptor, error)
// AllExtensionsForType returns all known extension fields that extend the given message type name.
AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error)
}
// DescriptorSourceFromProtoSets creates a DescriptorSource that is backed by the named files, whose contents
// are encoded FileDescriptorSet protos.
func DescriptorSourceFromProtoSets(fileNames ...string) (DescriptorSource, error) {
files := &descriptorpb.FileDescriptorSet{}
for _, fileName := range fileNames {
b, err := os.ReadFile(fileName)
if err != nil {
return nil, fmt.Errorf("could not load protoset file %q: %v", fileName, err)
}
var fs descriptorpb.FileDescriptorSet
err = proto.Unmarshal(b, &fs)
if err != nil {
return nil, fmt.Errorf("could not parse contents of protoset file %q: %v", fileName, err)
}
files.File = append(files.File, fs.File...)
}
return DescriptorSourceFromFileDescriptorSet(files)
}
// DescriptorSourceFromProtoFiles creates a DescriptorSource that is backed by the named files,
// whose contents are Protocol Buffer source files. The given importPaths are used to locate
// any imported files.
func DescriptorSourceFromProtoFiles(importPaths []string, fileNames ...string) (DescriptorSource, error) {
fileNames, err := protoparse.ResolveFilenames(importPaths, fileNames...)
if err != nil {
return nil, err
}
p := protoparse.Parser{
ImportPaths: importPaths,
InferImportPaths: len(importPaths) == 0,
IncludeSourceCodeInfo: true,
}
fds, err := p.ParseFiles(fileNames...)
if err != nil {
return nil, fmt.Errorf("could not parse given files: %v", err)
}
return DescriptorSourceFromFileDescriptors(fds...)
}
// DescriptorSourceFromFileDescriptorSet creates a DescriptorSource that is backed by the FileDescriptorSet.
func DescriptorSourceFromFileDescriptorSet(files *descriptorpb.FileDescriptorSet) (DescriptorSource, error) {
unresolved := map[string]*descriptorpb.FileDescriptorProto{}
for _, fd := range files.File {
unresolved[fd.GetName()] = fd
}
resolved := map[string]*desc.FileDescriptor{}
for _, fd := range files.File {
_, err := resolveFileDescriptor(unresolved, resolved, fd.GetName())
if err != nil {
return nil, err
}
}
return &fileSource{files: resolved}, nil
}
func resolveFileDescriptor(unresolved map[string]*descriptorpb.FileDescriptorProto, resolved map[string]*desc.FileDescriptor, filename string) (*desc.FileDescriptor, error) {
if r, ok := resolved[filename]; ok {
return r, nil
}
fd, ok := unresolved[filename]
if !ok {
return nil, fmt.Errorf("no descriptor found for %q", filename)
}
deps := make([]*desc.FileDescriptor, 0, len(fd.GetDependency()))
for _, dep := range fd.GetDependency() {
depFd, err := resolveFileDescriptor(unresolved, resolved, dep)
if err != nil {
return nil, err
}
deps = append(deps, depFd)
}
result, err := desc.CreateFileDescriptor(fd, deps...)
if err != nil {
return nil, err
}
resolved[filename] = result
return result, nil
}
// DescriptorSourceFromFileDescriptors creates a DescriptorSource that is backed by the given
// file descriptors
func DescriptorSourceFromFileDescriptors(files ...*desc.FileDescriptor) (DescriptorSource, error) {
fds := map[string]*desc.FileDescriptor{}
for _, fd := range files {
if err := addFile(fd, fds); err != nil {
return nil, err
}
}
return &fileSource{files: fds}, nil
}
func addFile(fd *desc.FileDescriptor, fds map[string]*desc.FileDescriptor) error {
name := fd.GetName()
if existing, ok := fds[name]; ok {
// already added this file
if existing != fd {
// doh! duplicate files provided
return fmt.Errorf("given files include multiple copies of %q", name)
}
return nil
}
fds[name] = fd
for _, dep := range fd.GetDependencies() {
if err := addFile(dep, fds); err != nil {
return err
}
}
return nil
}
type fileSource struct {
files map[string]*desc.FileDescriptor
er *dynamic.ExtensionRegistry
erInit sync.Once
}
func (fs *fileSource) ListServices() ([]string, error) {
set := map[string]bool{}
for _, fd := range fs.files {
for _, svc := range fd.GetServices() {
set[svc.GetFullyQualifiedName()] = true
}
}
sl := make([]string, 0, len(set))
for svc := range set {
sl = append(sl, svc)
}
return sl, nil
}
// GetAllFiles returns all of the underlying file descriptors. This is
// more thorough and more efficient than the fallback strategy used by
// the GetAllFiles package method, for enumerating all files from a
// descriptor source.
func (fs *fileSource) GetAllFiles() ([]*desc.FileDescriptor, error) {
files := make([]*desc.FileDescriptor, len(fs.files))
i := 0
for _, fd := range fs.files {
files[i] = fd
i++
}
return files, nil
}
func (fs *fileSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) {
for _, fd := range fs.files {
if dsc := fd.FindSymbol(fullyQualifiedName); dsc != nil {
return dsc, nil
}
}
return nil, notFound("Symbol", fullyQualifiedName)
}
func (fs *fileSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) {
fs.erInit.Do(func() {
fs.er = &dynamic.ExtensionRegistry{}
for _, fd := range fs.files {
fs.er.AddExtensionsFromFile(fd)
}
})
return fs.er.AllExtensionsForType(typeName), nil
}
// DescriptorSourceFromServer creates a DescriptorSource that uses the given gRPC reflection client
// to interrogate a server for descriptor information. If the server does not support the reflection
// API then the various DescriptorSource methods will return ErrReflectionNotSupported
func DescriptorSourceFromServer(_ context.Context, refClient *grpcreflect.Client) DescriptorSource {
return serverSource{client: refClient}
}
type serverSource struct {
client *grpcreflect.Client
}
func (ss serverSource) ListServices() ([]string, error) {
svcs, err := ss.client.ListServices()
return svcs, reflectionSupport(err)
}
func (ss serverSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) {
file, err := ss.client.FileContainingSymbol(fullyQualifiedName)
if err != nil {
return nil, reflectionSupport(err)
}
d := file.FindSymbol(fullyQualifiedName)
if d == nil {
return nil, notFound("Symbol", fullyQualifiedName)
}
return d, nil
}
func (ss serverSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) {
var exts []*desc.FieldDescriptor
nums, err := ss.client.AllExtensionNumbersForType(typeName)
if err != nil {
return nil, reflectionSupport(err)
}
for _, fieldNum := range nums {
ext, err := ss.client.ResolveExtension(typeName, fieldNum)
if err != nil {
return nil, reflectionSupport(err)
}
exts = append(exts, ext)
}
return exts, nil
}
func reflectionSupport(err error) error {
if err == nil {
return nil
}
if stat, ok := status.FromError(err); ok && stat.Code() == codes.Unimplemented {
return ErrReflectionNotSupported
}
return err
}
// WriteProtoset will use the given descriptor source to resolve all of the given
// symbols and write a proto file descriptor set with their definitions to the
// given output. The output will include descriptors for all files in which the
// symbols are defined as well as their transitive dependencies.
func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string) error {
filenames, fds, err := getFileDescriptors(symbols, descSource)
if err != nil {
return err
}
// now expand that to include transitive dependencies in topologically sorted
// order (such that file always appears after its dependencies)
expandedFiles := make(map[string]struct{}, len(fds))
allFilesSlice := make([]*descriptorpb.FileDescriptorProto, 0, len(fds))
for _, filename := range filenames {
allFilesSlice = addFilesToSet(allFilesSlice, expandedFiles, fds[filename])
}
// now we can serialize to file
b, err := proto.Marshal(&descriptorpb.FileDescriptorSet{File: allFilesSlice})
if err != nil {
return fmt.Errorf("failed to serialize file descriptor set: %v", err)
}
if _, err := out.Write(b); err != nil {
return fmt.Errorf("failed to write file descriptor set: %v", err)
}
return nil
}
func addFilesToSet(allFiles []*descriptorpb.FileDescriptorProto, expanded map[string]struct{}, fd *desc.FileDescriptor) []*descriptorpb.FileDescriptorProto {
if _, ok := expanded[fd.GetName()]; ok {
// already seen this one
return allFiles
}
expanded[fd.GetName()] = struct{}{}
// add all dependencies first
for _, dep := range fd.GetDependencies() {
allFiles = addFilesToSet(allFiles, expanded, dep)
}
return append(allFiles, fd.AsFileDescriptorProto())
}
// WriteProtoFiles will use the given descriptor source to resolve all the given
// symbols and write proto files with their definitions to the given output directory.
func WriteProtoFiles(outProtoDirPath string, descSource DescriptorSource, symbols ...string) error {
filenames, fds, err := getFileDescriptors(symbols, descSource)
if err != nil {
return err
}
// now expand that to include transitive dependencies in topologically sorted
// order (such that file always appears after its dependencies)
expandedFiles := make(map[string]struct{}, len(fds))
allFileDescriptors := make([]*desc.FileDescriptor, 0, len(fds))
for _, filename := range filenames {
allFileDescriptors = addFilesToFileDescriptorList(allFileDescriptors, expandedFiles, fds[filename])
}
pr := protoprint.Printer{}
// now we can serialize to files
for i := range allFileDescriptors {
if err := writeProtoFile(outProtoDirPath, allFileDescriptors[i], &pr); err != nil {
return err
}
}
return nil
}
func writeProtoFile(outProtoDirPath string, fd *desc.FileDescriptor, pr *protoprint.Printer) error {
outFile := filepath.Join(outProtoDirPath, fd.GetFullyQualifiedName())
outDir := filepath.Dir(outFile)
if err := os.MkdirAll(outDir, 0777); err != nil {
return fmt.Errorf("failed to create directory %q: %w", outDir, err)
}
f, err := os.Create(outFile)
if err != nil {
return fmt.Errorf("failed to create proto file %q: %w", outFile, err)
}
defer f.Close()
if err := pr.PrintProtoFile(fd, f); err != nil {
return fmt.Errorf("failed to write proto file %q: %w", outFile, err)
}
return nil
}
func getFileDescriptors(symbols []string, descSource DescriptorSource) ([]string, map[string]*desc.FileDescriptor, error) {
// compute set of file descriptors
filenames := make([]string, 0, len(symbols))
fds := make(map[string]*desc.FileDescriptor, len(symbols))
for _, sym := range symbols {
d, err := descSource.FindSymbol(sym)
if err != nil {
return nil, nil, fmt.Errorf("failed to find descriptor for %q: %v", sym, err)
}
fd := d.GetFile()
if _, ok := fds[fd.GetName()]; !ok {
fds[fd.GetName()] = fd
filenames = append(filenames, fd.GetName())
}
}
return filenames, fds, nil
}
func addFilesToFileDescriptorList(allFiles []*desc.FileDescriptor, expanded map[string]struct{}, fd *desc.FileDescriptor) []*desc.FileDescriptor {
if _, ok := expanded[fd.GetName()]; ok {
// already seen this one
return allFiles
}
expanded[fd.GetName()] = struct{}{}
// add all dependencies first
for _, dep := range fd.GetDependencies() {
allFiles = addFilesToFileDescriptorList(allFiles, expanded, dep)
}
return append(allFiles, fd)
}
================================================
FILE: desc_source_test.go
================================================
package grpcurl
import (
"bytes"
"os"
"testing"
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
"google.golang.org/protobuf/types/descriptorpb"
)
func TestWriteProtoset(t *testing.T) {
exampleProtoset, err := loadProtoset("./internal/testing/example.protoset")
if err != nil {
t.Fatalf("failed to load example.protoset: %v", err)
}
testProtoset, err := loadProtoset("./internal/testing/test.protoset")
if err != nil {
t.Fatalf("failed to load test.protoset: %v", err)
}
mergedProtoset := &descriptorpb.FileDescriptorSet{
File: append(exampleProtoset.File, testProtoset.File...),
}
descSrc, err := DescriptorSourceFromFileDescriptorSet(mergedProtoset)
if err != nil {
t.Fatalf("failed to create descriptor source: %v", err)
}
checkWriteProtoset(t, descSrc, exampleProtoset, "TestService")
checkWriteProtoset(t, descSrc, testProtoset, "testing.TestService")
checkWriteProtoset(t, descSrc, mergedProtoset, "TestService", "testing.TestService")
}
func loadProtoset(path string) (*descriptorpb.FileDescriptorSet, error) {
b, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var protoset descriptorpb.FileDescriptorSet
if err := proto.Unmarshal(b, &protoset); err != nil {
return nil, err
}
return &protoset, nil
}
func checkWriteProtoset(t *testing.T, descSrc DescriptorSource, protoset *descriptorpb.FileDescriptorSet, symbols ...string) {
var buf bytes.Buffer
if err := WriteProtoset(&buf, descSrc, symbols...); err != nil {
t.Fatalf("failed to write protoset: %v", err)
}
var result descriptorpb.FileDescriptorSet
if err := proto.Unmarshal(buf.Bytes(), &result); err != nil {
t.Fatalf("failed to unmarshal written protoset: %v", err)
}
if !proto.Equal(protoset, &result) {
t.Fatalf("written protoset not equal to input:\nExpecting: %s\nActual: %s", protoset, &result)
}
}
================================================
FILE: download_protoc.sh
================================================
#!/usr/bin/env bash
set -e
cd $(dirname $0)
if [[ -z "$PROTOC_VERSION" ]]; then
echo "Set PROTOC_VERSION env var to indicate the version to download" >&2
exit 1
fi
PROTOC_OS="$(uname -s)"
PROTOC_ARCH="$(uname -m)"
case "${PROTOC_OS}" in
Darwin) PROTOC_OS="osx" ;;
Linux) PROTOC_OS="linux" ;;
*)
echo "Invalid value for uname -s: ${PROTOC_OS}" >&2
exit 1
esac
# This is for macs with M1 chips. Precompiled binaries for osx/amd64 are not available for download, so for that case
# we download the x86_64 version instead. This will work as long as rosetta2 is installed.
if [ "$PROTOC_OS" = "osx" ] && [ "$PROTOC_ARCH" = "arm64" ]; then
PROTOC_ARCH="x86_64"
fi
PROTOC="${PWD}/.tmp/protoc/bin/protoc"
if [[ "$(${PROTOC} --version 2>/dev/null)" != "libprotoc 3.${PROTOC_VERSION}" ]]; then
rm -rf ./.tmp/protoc
mkdir -p .tmp/protoc
curl -L "https://github.com/google/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-${PROTOC_OS}-${PROTOC_ARCH}.zip" > .tmp/protoc/protoc.zip
pushd ./.tmp/protoc && unzip protoc.zip && popd
fi
================================================
FILE: format.go
================================================
package grpcurl
import (
"bufio"
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"reflect"
"strings"
"sync"
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// RequestParser processes input into messages.
type RequestParser interface {
// Next parses input data into the given request message. If called after
// input is exhausted, it returns io.EOF. If the caller re-uses the same
// instance in multiple calls to Next, it should call msg.Reset() in between
// each call.
Next(msg proto.Message) error
// NumRequests returns the number of messages that have been parsed and
// returned by a call to Next.
NumRequests() int
}
type jsonRequestParser struct {
dec *json.Decoder
unmarshaler jsonpb.Unmarshaler
requestCount int
}
// NewJSONRequestParser returns a RequestParser that reads data in JSON format
// from the given reader. The given resolver is used to assist with decoding of
// google.protobuf.Any messages.
//
// Input data that contains more than one message should just include all
// messages concatenated (though whitespace is necessary to separate some kinds
// of values in JSON).
//
// If the given reader has no data, the returned parser will return io.EOF on
// the very first call.
func NewJSONRequestParser(in io.Reader, resolver jsonpb.AnyResolver) RequestParser {
return &jsonRequestParser{
dec: json.NewDecoder(in),
unmarshaler: jsonpb.Unmarshaler{AnyResolver: resolver},
}
}
// NewJSONRequestParserWithUnmarshaler is like NewJSONRequestParser but
// accepts a protobuf jsonpb.Unmarshaler instead of jsonpb.AnyResolver.
func NewJSONRequestParserWithUnmarshaler(in io.Reader, unmarshaler jsonpb.Unmarshaler) RequestParser {
return &jsonRequestParser{
dec: json.NewDecoder(in),
unmarshaler: unmarshaler,
}
}
func (f *jsonRequestParser) Next(m proto.Message) error {
var msg json.RawMessage
if err := f.dec.Decode(&msg); err != nil {
return err
}
f.requestCount++
return f.unmarshaler.Unmarshal(bytes.NewReader(msg), m)
}
func (f *jsonRequestParser) NumRequests() int {
return f.requestCount
}
const (
textSeparatorChar = '\x1e'
)
type textRequestParser struct {
r *bufio.Reader
err error
requestCount int
}
// NewTextRequestParser returns a RequestParser that reads data in the protobuf
// text format from the given reader.
//
// Input data that contains more than one message should include an ASCII
// 'Record Separator' character (0x1E) between each message.
//
// Empty text is a valid text format and represents an empty message. So if the
// given reader has no data, the returned parser will yield an empty message
// for the first call to Next and then return io.EOF thereafter. This also means
// that if the input data ends with a record separator, then a final empty
// message will be parsed *after* the separator.
func NewTextRequestParser(in io.Reader) RequestParser {
return &textRequestParser{r: bufio.NewReader(in)}
}
func (f *textRequestParser) Next(m proto.Message) error {
if f.err != nil {
return f.err
}
var b []byte
b, f.err = f.r.ReadBytes(textSeparatorChar)
if f.err != nil && f.err != io.EOF {
return f.err
}
// remove delimiter
if len(b) > 0 && b[len(b)-1] == textSeparatorChar {
b = b[:len(b)-1]
}
f.requestCount++
return proto.UnmarshalText(string(b), m)
}
func (f *textRequestParser) NumRequests() int {
return f.requestCount
}
// Formatter translates messages into string representations.
type Formatter func(proto.Message) (string, error)
// NewJSONFormatter returns a formatter that returns JSON strings. The JSON will
// include empty/default values (instead of just omitted them) if emitDefaults
// is true. The given resolver is used to assist with encoding of
// google.protobuf.Any messages.
func NewJSONFormatter(emitDefaults bool, resolver jsonpb.AnyResolver) Formatter {
marshaler := jsonpb.Marshaler{
EmitDefaults: emitDefaults,
AnyResolver: resolver,
}
// Workaround for indentation issue in jsonpb with Any messages.
// Bug was originally fixed in https://github.com/golang/protobuf/pull/834
// but later re-introduced before the module was deprecated and frozen.
// If jsonpb is ever replaced with google.golang.org/protobuf/encoding/protojson
// this workaround will no longer be needed.
formatter := func(message proto.Message) (string, error) {
output, err := marshaler.MarshalToString(message)
if err != nil {
return "", err
}
var buf bytes.Buffer
if err := json.Indent(&buf, []byte(output), "", " "); err != nil {
return "", err
}
return buf.String(), nil
}
return formatter
}
// NewTextFormatter returns a formatter that returns strings in the protobuf
// text format. If includeSeparator is true then, when invoked to format
// multiple messages, all messages after the first one will be prefixed with the
// ASCII 'Record Separator' character (0x1E).
func NewTextFormatter(includeSeparator bool) Formatter {
tf := textFormatter{useSeparator: includeSeparator}
return tf.format
}
type textFormatter struct {
useSeparator bool
numFormatted int
}
var protoTextMarshaler = proto.TextMarshaler{ExpandAny: true}
func (tf *textFormatter) format(m proto.Message) (string, error) {
var buf bytes.Buffer
if tf.useSeparator && tf.numFormatted > 0 {
if err := buf.WriteByte(textSeparatorChar); err != nil {
return "", err
}
}
// If message implements MarshalText method (such as a *dynamic.Message),
// it won't get details about whether or not to format to text compactly
// or with indentation. So first see if the message also implements a
// MarshalTextIndent method and use that instead if available.
type indentMarshaler interface {
MarshalTextIndent() ([]byte, error)
}
if indenter, ok := m.(indentMarshaler); ok {
b, err := indenter.MarshalTextIndent()
if err != nil {
return "", err
}
if _, err := buf.Write(b); err != nil {
return "", err
}
} else if err := protoTextMarshaler.Marshal(&buf, m); err != nil {
return "", err
}
// no trailing newline needed
str := buf.String()
if len(str) > 0 && str[len(str)-1] == '\n' {
str = str[:len(str)-1]
}
tf.numFormatted++
return str, nil
}
// Format of request data. The allowed values are 'json' or 'text'.
type Format string
const (
// FormatJSON specifies input data in JSON format. Multiple request values
// may be concatenated (messages with a JSON representation other than
// object must be separated by whitespace, such as a newline)
FormatJSON = Format("json")
// FormatText specifies input data must be in the protobuf text format.
// Multiple request values must be separated by the "record separator"
// ASCII character: 0x1E. The stream should not end in a record separator.
// If it does, it will be interpreted as a final, blank message after the
// separator.
FormatText = Format("text")
)
// AnyResolverFromDescriptorSource returns an AnyResolver that will search for
// types using the given descriptor source.
func AnyResolverFromDescriptorSource(source DescriptorSource) jsonpb.AnyResolver {
return &anyResolver{source: source}
}
// AnyResolverFromDescriptorSourceWithFallback returns an AnyResolver that will
// search for types using the given descriptor source and then fallback to a
// special message if the type is not found. The fallback type will render to
// JSON with a "@type" property, just like an Any message, but also with a
// custom "@value" property that includes the binary encoded payload.
func AnyResolverFromDescriptorSourceWithFallback(source DescriptorSource) jsonpb.AnyResolver {
res := anyResolver{source: source}
return &anyResolverWithFallback{AnyResolver: &res}
}
type anyResolver struct {
source DescriptorSource
er dynamic.ExtensionRegistry
mu sync.RWMutex
mf *dynamic.MessageFactory
resolved map[string]func() proto.Message
}
func (r *anyResolver) Resolve(typeUrl string) (proto.Message, error) {
mname := typeUrl
if slash := strings.LastIndex(mname, "/"); slash >= 0 {
mname = mname[slash+1:]
}
r.mu.RLock()
factory := r.resolved[mname]
r.mu.RUnlock()
// already resolved?
if factory != nil {
return factory(), nil
}
r.mu.Lock()
defer r.mu.Unlock()
// double-check, in case we were racing with another goroutine
// that resolved this one
factory = r.resolved[mname]
if factory != nil {
return factory(), nil
}
// use descriptor source to resolve message type
d, err := r.source.FindSymbol(mname)
if err != nil {
return nil, err
}
md, ok := d.(*desc.MessageDescriptor)
if !ok {
return nil, fmt.Errorf("unknown message: %s", typeUrl)
}
// populate any extensions for this message, too (if there are any)
if exts, err := r.source.AllExtensionsForType(mname); err == nil {
if err := r.er.AddExtension(exts...); err != nil {
return nil, err
}
}
if r.mf == nil {
r.mf = dynamic.NewMessageFactoryWithExtensionRegistry(&r.er)
}
factory = func() proto.Message {
return r.mf.NewMessage(md)
}
if r.resolved == nil {
r.resolved = map[string]func() proto.Message{}
}
r.resolved[mname] = factory
return factory(), nil
}
// anyResolverWithFallback can provide a fallback value for unknown
// messages that will format itself to JSON using an "@value" field
// that has the base64-encoded data for the unknown message value.
type anyResolverWithFallback struct {
jsonpb.AnyResolver
}
func (r anyResolverWithFallback) Resolve(typeUrl string) (proto.Message, error) {
msg, err := r.AnyResolver.Resolve(typeUrl)
if err == nil {
return msg, err
}
// Try "default" resolution logic. This mirrors the default behavior
// of jsonpb, which checks to see if the given message name is registered
// in the proto package.
mname := typeUrl
if slash := strings.LastIndex(mname, "/"); slash >= 0 {
mname = mname[slash+1:]
}
//lint:ignore SA1019 new non-deprecated API requires other code changes; deferring...
mt := proto.MessageType(mname)
if mt != nil {
return reflect.New(mt.Elem()).Interface().(proto.Message), nil
}
// finally, fallback to a special placeholder that can marshal itself
// to JSON using a special "@value" property to show base64-encoded
// data for the embedded message
return &unknownAny{TypeUrl: typeUrl, Error: fmt.Sprintf("%s is not recognized; see @value for raw binary message data", mname)}, nil
}
type unknownAny struct {
TypeUrl string `json:"@type"`
Error string `json:"@error"`
Value string `json:"@value"`
}
func (a *unknownAny) MarshalJSONPB(jsm *jsonpb.Marshaler) ([]byte, error) {
if jsm.Indent != "" {
return json.MarshalIndent(a, "", jsm.Indent)
}
return json.Marshal(a)
}
func (a *unknownAny) Unmarshal(b []byte) error {
a.Value = base64.StdEncoding.EncodeToString(b)
return nil
}
func (a *unknownAny) Reset() {
a.Value = ""
}
func (a *unknownAny) String() string {
b, err := a.MarshalJSONPB(&jsonpb.Marshaler{})
if err != nil {
return fmt.Sprintf("ERROR: %v", err.Error())
}
return string(b)
}
func (a *unknownAny) ProtoMessage() {
}
var _ proto.Message = (*unknownAny)(nil)
// FormatOptions is a set of flags that are passed to a JSON or text formatter.
type FormatOptions struct {
// EmitJSONDefaultFields flag, when true, includes empty/default values in the output.
// FormatJSON only flag.
EmitJSONDefaultFields bool
// AllowUnknownFields is an option for the parser. When true,
// it accepts input which includes unknown fields. These unknown fields
// are skipped instead of returning an error.
// FormatJSON only flag.
AllowUnknownFields bool
// IncludeTextSeparator is true then, when invoked to format multiple messages,
// all messages after the first one will be prefixed with the
// ASCII 'Record Separator' character (0x1E).
// It might be useful when the output is piped to another grpcurl process.
// FormatText only flag.
IncludeTextSeparator bool
}
// RequestParserAndFormatter returns a request parser and formatter for the
// given format. The given descriptor source may be used for parsing message
// data (if needed by the format).
// It accepts a set of options. The field EmitJSONDefaultFields and IncludeTextSeparator
// are options for JSON and protobuf text formats, respectively. The AllowUnknownFields field
// is a JSON-only format flag.
// Requests will be parsed from the given in.
func RequestParserAndFormatter(format Format, descSource DescriptorSource, in io.Reader, opts FormatOptions) (RequestParser, Formatter, error) {
switch format {
case FormatJSON:
resolver := AnyResolverFromDescriptorSource(descSource)
unmarshaler := jsonpb.Unmarshaler{AnyResolver: resolver, AllowUnknownFields: opts.AllowUnknownFields}
return NewJSONRequestParserWithUnmarshaler(in, unmarshaler), NewJSONFormatter(opts.EmitJSONDefaultFields, anyResolverWithFallback{AnyResolver: resolver}), nil
case FormatText:
return NewTextRequestParser(in), NewTextFormatter(opts.IncludeTextSeparator), nil
default:
return nil, nil, fmt.Errorf("unknown format: %s", format)
}
}
// RequestParserAndFormatterFor returns a request parser and formatter for the
// given format. The given descriptor source may be used for parsing message
// data (if needed by the format). The flags emitJSONDefaultFields and
// includeTextSeparator are options for JSON and protobuf text formats,
// respectively. Requests will be parsed from the given in.
// This function is deprecated. Please use RequestParserAndFormatter instead.
// DEPRECATED
func RequestParserAndFormatterFor(format Format, descSource DescriptorSource, emitJSONDefaultFields, includeTextSeparator bool, in io.Reader) (RequestParser, Formatter, error) {
return RequestParserAndFormatter(format, descSource, in, FormatOptions{
EmitJSONDefaultFields: emitJSONDefaultFields,
IncludeTextSeparator: includeTextSeparator,
})
}
// DefaultEventHandler logs events to a writer. This is not thread-safe, but is
// safe for use with InvokeRPC as long as NumResponses and Status are not read
// until the call to InvokeRPC completes.
type DefaultEventHandler struct {
Out io.Writer
Formatter Formatter
// 0 = default
// 1 = verbose
// 2 = very verbose
VerbosityLevel int
// NumResponses is the number of responses that have been received.
NumResponses int
// Status is the status that was received at the end of an RPC. It is
// nil if the RPC is still in progress.
Status *status.Status
}
// NewDefaultEventHandler returns an InvocationEventHandler that logs events to
// the given output. If verbose is true, all events are logged. Otherwise, only
// response messages are logged.
//
// Deprecated: NewDefaultEventHandler exists for compatibility.
// It doesn't allow fine control over the `VerbosityLevel`
// and provides only 0 and 1 options (which corresponds to the `verbose` argument).
// Use DefaultEventHandler{} initializer directly.
func NewDefaultEventHandler(out io.Writer, descSource DescriptorSource, formatter Formatter, verbose bool) *DefaultEventHandler {
verbosityLevel := 0
if verbose {
verbosityLevel = 1
}
return &DefaultEventHandler{
Out: out,
Formatter: formatter,
VerbosityLevel: verbosityLevel,
}
}
var _ InvocationEventHandler = (*DefaultEventHandler)(nil)
func (h *DefaultEventHandler) OnResolveMethod(md *desc.MethodDescriptor) {
if h.VerbosityLevel > 0 {
txt, err := GetDescriptorText(md, nil)
if err == nil {
fmt.Fprintf(h.Out, "\nResolved method descriptor:\n%s\n", txt)
}
}
}
func (h *DefaultEventHandler) OnSendHeaders(md metadata.MD) {
if h.VerbosityLevel > 0 {
fmt.Fprintf(h.Out, "\nRequest metadata to send:\n%s\n", MetadataToString(md))
}
}
func (h *DefaultEventHandler) OnReceiveHeaders(md metadata.MD) {
if h.VerbosityLevel > 0 {
fmt.Fprintf(h.Out, "\nResponse headers received:\n%s\n", MetadataToString(md))
}
}
func (h *DefaultEventHandler) OnReceiveResponse(resp proto.Message) {
h.NumResponses++
if h.VerbosityLevel > 1 {
fmt.Fprintf(h.Out, "\nEstimated response size: %d bytes\n", proto.Size(resp))
}
if h.VerbosityLevel > 0 {
fmt.Fprint(h.Out, "\nResponse contents:\n")
}
if respStr, err := h.Formatter(resp); err != nil {
fmt.Fprintf(h.Out, "Failed to format response message %d: %v\n", h.NumResponses, err)
} else {
fmt.Fprintln(h.Out, respStr)
}
}
func (h *DefaultEventHandler) OnReceiveTrailers(stat *status.Status, md metadata.MD) {
h.Status = stat
if h.VerbosityLevel > 0 {
fmt.Fprintf(h.Out, "\nResponse trailers received:\n%s\n", MetadataToString(md))
}
}
// PrintStatus prints details about the given status to the given writer. The given
// formatter is used to print any detail messages that may be included in the status.
// If the given status has a code of OK, "OK" is printed and that is all. Otherwise,
// "ERROR:" is printed along with a line showing the code, one showing the message
// string, and each detail message if any are present. The detail messages will be
// printed as proto text format or JSON, depending on the given formatter.
func PrintStatus(w io.Writer, stat *status.Status, formatter Formatter) {
if stat.Code() == codes.OK {
fmt.Fprintln(w, "OK")
return
}
fmt.Fprintf(w, "ERROR:\n Code: %s\n Message: %s\n", stat.Code().String(), stat.Message())
statpb := stat.Proto()
if len(statpb.Details) > 0 {
fmt.Fprintf(w, " Details:\n")
for i, det := range statpb.Details {
prefix := fmt.Sprintf(" %d)", i+1)
fmt.Fprintf(w, "%s\t", prefix)
prefix = strings.Repeat(" ", len(prefix)) + "\t"
output, err := formatter(det)
if err != nil {
fmt.Fprintf(w, "Error parsing detail message: %v\n", err)
} else {
lines := strings.Split(output, "\n")
for i, line := range lines {
if i == 0 {
// first line is already indented
fmt.Fprintf(w, "%s\n", line)
} else {
fmt.Fprintf(w, "%s%s\n", prefix, line)
}
}
}
}
}
}
================================================
FILE: format_test.go
================================================
package grpcurl
import (
"bytes"
"fmt"
"io"
"strings"
"testing"
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/types/known/structpb"
)
func TestRequestParser(t *testing.T) {
source, err := DescriptorSourceFromProtoSets("internal/testing/example.protoset")
if err != nil {
t.Fatalf("failed to create descriptor source: %v", err)
}
msg, err := makeProto()
if err != nil {
t.Fatalf("failed to create message: %v", err)
}
testCases := []struct {
format Format
input string
expectedOutput []proto.Message
}{
{
format: FormatJSON,
input: "",
},
{
format: FormatJSON,
input: messageAsJSON,
expectedOutput: []proto.Message{msg},
},
{
format: FormatJSON,
input: messageAsJSON + messageAsJSON + messageAsJSON,
expectedOutput: []proto.Message{msg, msg, msg},
},
{
// unlike JSON, empty input yields one empty message (vs. zero messages)
format: FormatText,
input: "",
expectedOutput: []proto.Message{&structpb.Value{}},
},
{
format: FormatText,
input: messageAsText,
expectedOutput: []proto.Message{msg},
},
{
format: FormatText,
input: messageAsText + string(textSeparatorChar),
expectedOutput: []proto.Message{msg, &structpb.Value{}},
},
{
format: FormatText,
input: messageAsText + string(textSeparatorChar) + messageAsText + string(textSeparatorChar) + messageAsText,
expectedOutput: []proto.Message{msg, msg, msg},
},
}
for i, tc := range testCases {
name := fmt.Sprintf("#%d, %s, %d message(s)", i+1, tc.format, len(tc.expectedOutput))
rf, _, err := RequestParserAndFormatter(tc.format, source, strings.NewReader(tc.input), FormatOptions{})
if err != nil {
t.Errorf("Failed to create parser and formatter: %v", err)
continue
}
numReqs := 0
for {
var req structpb.Value
err := rf.Next(&req)
if err == io.EOF {
break
} else if err != nil {
t.Errorf("%s, msg %d: unexpected error: %v", name, numReqs, err)
}
if !proto.Equal(&req, tc.expectedOutput[numReqs]) {
t.Errorf("%s, msg %d: incorrect message;\nexpecting:\n%v\ngot:\n%v", name, numReqs, tc.expectedOutput[numReqs], &req)
}
numReqs++
}
if rf.NumRequests() != numReqs {
t.Errorf("%s: factory reported wrong number of requests: expecting %d, got %d", name, numReqs, rf.NumRequests())
}
}
}
// Handler prints response data (and headers/trailers in verbose mode).
// This verifies that we get the right output in both JSON and proto text modes.
func TestHandler(t *testing.T) {
source, err := DescriptorSourceFromProtoSets("internal/testing/example.protoset")
if err != nil {
t.Fatalf("failed to create descriptor source: %v", err)
}
d, err := source.FindSymbol("TestService.GetFiles")
if err != nil {
t.Fatalf("failed to find method 'TestService.GetFiles': %v", err)
}
md, ok := d.(*desc.MethodDescriptor)
if !ok {
t.Fatalf("wrong kind of descriptor found: %T", d)
}
reqHeaders := metadata.Pairs("foo", "123", "bar", "456")
respHeaders := metadata.Pairs("foo", "abc", "bar", "def", "baz", "xyz")
respTrailers := metadata.Pairs("a", "1", "b", "2", "c", "3")
rsp, err := makeProto()
if err != nil {
t.Fatalf("failed to create response message: %v", err)
}
for _, format := range []Format{FormatJSON, FormatText} {
for _, numMessages := range []int{1, 3} {
for verbosityLevel := 0; verbosityLevel <= 2; verbosityLevel++ {
name := fmt.Sprintf("%s, %d message(s)", format, numMessages)
if verbosityLevel > 0 {
name += fmt.Sprintf(", verbosityLevel=%d", verbosityLevel)
}
verbose := verbosityLevel > 0
_, formatter, err := RequestParserAndFormatter(format, source, nil, FormatOptions{IncludeTextSeparator: !verbose})
if err != nil {
t.Errorf("Failed to create parser and formatter: %v", err)
continue
}
var buf bytes.Buffer
h := &DefaultEventHandler{
Out: &buf,
Formatter: formatter,
VerbosityLevel: verbosityLevel,
}
h.OnResolveMethod(md)
h.OnSendHeaders(reqHeaders)
h.OnReceiveHeaders(respHeaders)
for i := 0; i < numMessages; i++ {
h.OnReceiveResponse(rsp)
}
h.OnReceiveTrailers(nil, respTrailers)
expectedOutput := ""
if verbose {
expectedOutput += verbosePrefix
}
for i := 0; i < numMessages; i++ {
if verbosityLevel > 1 {
expectedOutput += verboseResponseSize
}
if verbose {
expectedOutput += verboseResponseHeader
}
if format == "json" {
expectedOutput += messageAsJSON
} else {
if i > 0 && !verbose {
expectedOutput += string(textSeparatorChar)
}
expectedOutput += messageAsText
}
}
if verbose {
expectedOutput += verboseSuffix
}
out := buf.String()
if !compare(out, expectedOutput) {
t.Errorf("%s: Incorrect output. Expected:\n%s\nGot:\n%s", name, expectedOutput, out)
}
}
}
}
}
// compare checks that actual and expected are equal, returning true if so.
// A simple equality check (==) does not suffice because jsonpb formats
// structpb.Value strangely. So if that formatting gets fixed, we don't
// want this test in grpcurl to suddenly start failing. So we check each
// line and compare the lines after stripping whitespace (which removes
// the jsonpb format anomalies).
func compare(actual, expected string) bool {
actualLines := strings.Split(actual, "\n")
expectedLines := strings.Split(expected, "\n")
if len(actualLines) != len(expectedLines) {
return false
}
for i := 0; i < len(actualLines); i++ {
if strings.TrimSpace(actualLines[i]) != strings.TrimSpace(expectedLines[i]) {
return false
}
}
return true
}
func makeProto() (proto.Message, error) {
var rsp structpb.Value
err := jsonpb.UnmarshalString(`{
"foo": ["abc", "def", "ghi"],
"bar": { "a": 1, "b": 2 },
"baz": true,
"null": null
}`, &rsp)
if err != nil {
return nil, err
}
return &rsp, nil
}
var (
verbosePrefix = `
Resolved method descriptor:
rpc GetFiles ( .TestRequest ) returns ( .TestResponse );
Request metadata to send:
bar: 456
foo: 123
Response headers received:
bar: def
baz: xyz
foo: abc
`
verboseSuffix = `
Response trailers received:
a: 1
b: 2
c: 3
`
verboseResponseSize = `
Estimated response size: 100 bytes
`
verboseResponseHeader = `
Response contents:
`
messageAsJSON = `{
"bar": {
"a": 1,
"b": 2
},
"baz": true,
"foo": [
"abc",
"def",
"ghi"
],
"null": null
}
`
messageAsText = `struct_value: <
fields: <
key: "bar"
value: <
struct_value: <
fields: <
key: "a"
value: <
number_value: 1
>
>
fields: <
key: "b"
value: <
number_value: 2
>
>
>
>
>
fields: <
key: "baz"
value: <
bool_value: true
>
>
fields: <
key: "foo"
value: <
list_value: <
values: <
string_value: "abc"
>
values: <
string_value: "def"
>
values: <
string_value: "ghi"
>
>
>
>
fields: <
key: "null"
value: <
null_value: NULL_VALUE
>
>
>
`
)
================================================
FILE: go.mod
================================================
module github.com/fullstorydev/grpcurl
go 1.24.0
toolchain go1.24.1
require (
github.com/golang/protobuf v1.5.4
github.com/jhump/protoreflect v1.18.0
google.golang.org/grpc v1.66.2
google.golang.org/protobuf v1.36.11
)
require (
cel.dev/expr v0.15.0 // indirect
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 // indirect
github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect
github.com/jhump/protoreflect/v2 v2.0.0-beta.1 // indirect
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
)
================================================
FILE: go.sum
================================================
cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w=
cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg=
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw=
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc=
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA=
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jhump/protoreflect v1.18.0 h1:TOz0MSR/0JOZ5kECB/0ufGnC2jdsgZ123Rd/k4Z5/2w=
github.com/jhump/protoreflect v1.18.0/go.mod h1:ezWcltJIVF4zYdIFM+D/sHV4Oh5LNU08ORzCGfwvTz8=
github.com/jhump/protoreflect/v2 v2.0.0-beta.1 h1:Dw1rslK/VotaUGYsv53XVWITr+5RCPXfvvlGrM/+B6w=
github.com/jhump/protoreflect/v2 v2.0.0-beta.1/go.mod h1:D9LBEowZyv8/iSu97FU2zmXG3JxVTmNw21mu63niFzU=
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14=
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU=
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
================================================
FILE: grpcurl.go
================================================
// Package grpcurl provides the core functionality exposed by the grpcurl command, for
// dynamically connecting to a server, using the reflection service to inspect the server,
// and invoking RPCs. The grpcurl command-line tool constructs a DescriptorSource, based
// on the command-line parameters, and supplies an InvocationEventHandler to supply request
// data (which can come from command-line args or the process's stdin) and to log the
// events (to the process's stdout).
package grpcurl
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"errors"
"fmt"
"net"
"os"
"regexp"
"slices"
"sort"
"strings"
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/desc/protoprint"
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
xdsCredentials "google.golang.org/grpc/credentials/xds"
_ "google.golang.org/grpc/health" // import grpc/health to enable transparent client side checking
"google.golang.org/grpc/metadata"
protov2 "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/structpb"
)
// ListServices uses the given descriptor source to return a sorted list of fully-qualified
// service names.
func ListServices(source DescriptorSource) ([]string, error) {
svcs, err := source.ListServices()
if err != nil {
return nil, err
}
sort.Strings(svcs)
return svcs, nil
}
type sourceWithFiles interface {
GetAllFiles() ([]*desc.FileDescriptor, error)
}
var _ sourceWithFiles = (*fileSource)(nil)
// GetAllFiles uses the given descriptor source to return a list of file descriptors.
func GetAllFiles(source DescriptorSource) ([]*desc.FileDescriptor, error) {
var files []*desc.FileDescriptor
srcFiles, ok := source.(sourceWithFiles)
// If an error occurs, we still try to load as many files as we can, so that
// caller can decide whether to ignore error or not.
var firstError error
if ok {
files, firstError = srcFiles.GetAllFiles()
} else {
// Source does not implement GetAllFiles method, so use ListServices
// and grab files from there.
svcNames, err := source.ListServices()
if err != nil {
firstError = err
} else {
allFiles := map[string]*desc.FileDescriptor{}
for _, name := range svcNames {
d, err := source.FindSymbol(name)
if err != nil {
if firstError == nil {
firstError = err
}
} else {
addAllFilesToSet(d.GetFile(), allFiles)
}
}
files = make([]*desc.FileDescriptor, len(allFiles))
i := 0
for _, fd := range allFiles {
files[i] = fd
i++
}
}
}
sort.Sort(filesByName(files))
return files, firstError
}
type filesByName []*desc.FileDescriptor
func (f filesByName) Len() int {
return len(f)
}
func (f filesByName) Less(i, j int) bool {
return f[i].GetName() < f[j].GetName()
}
func (f filesByName) Swap(i, j int) {
f[i], f[j] = f[j], f[i]
}
func addAllFilesToSet(fd *desc.FileDescriptor, all map[string]*desc.FileDescriptor) {
if _, ok := all[fd.GetName()]; ok {
// already added
return
}
all[fd.GetName()] = fd
for _, dep := range fd.GetDependencies() {
addAllFilesToSet(dep, all)
}
}
// ListMethods uses the given descriptor source to return a sorted list of method names
// for the specified fully-qualified service name.
func ListMethods(source DescriptorSource, serviceName string) ([]string, error) {
dsc, err := source.FindSymbol(serviceName)
if err != nil {
return nil, err
}
if sd, ok := dsc.(*desc.ServiceDescriptor); !ok {
return nil, notFound("Service", serviceName)
} else {
methods := make([]string, 0, len(sd.GetMethods()))
for _, method := range sd.GetMethods() {
methods = append(methods, method.GetFullyQualifiedName())
}
sort.Strings(methods)
return methods, nil
}
}
// MetadataFromHeaders converts a list of header strings (each string in
// "Header-Name: Header-Value" form) into metadata. If a string has a header
// name without a value (e.g. does not contain a colon), the value is assumed
// to be blank. Binary headers (those whose names end in "-bin") should be
// base64-encoded. But if they cannot be base64-decoded, they will be assumed to
// be in raw form and used as is.
func MetadataFromHeaders(headers []string) metadata.MD {
md := make(metadata.MD)
for _, part := range headers {
if part != "" {
pieces := strings.SplitN(part, ":", 2)
if len(pieces) == 1 {
pieces = append(pieces, "") // if no value was specified, just make it "" (maybe the header value doesn't matter)
}
headerName := strings.ToLower(strings.TrimSpace(pieces[0]))
val := strings.TrimSpace(pieces[1])
if strings.HasSuffix(headerName, "-bin") {
if v, err := decode(val); err == nil {
val = v
}
}
md[headerName] = append(md[headerName], val)
}
}
return md
}
var envVarRegex = regexp.MustCompile(`\${\w+}`)
// ExpandHeaders expands environment variables contained in the header string.
// If no corresponding environment variable is found an error is returned.
// TODO: Add escaping for `${`
func ExpandHeaders(headers []string) ([]string, error) {
expandedHeaders := make([]string, len(headers))
for idx, header := range headers {
if header == "" {
continue
}
results := envVarRegex.FindAllString(header, -1)
if len(results) == 0 {
expandedHeaders[idx] = headers[idx]
continue
}
expandedHeader := header
for _, result := range results {
envVarName := result[2 : len(result)-1] // strip leading `${` and trailing `}`
envVarValue, ok := os.LookupEnv(envVarName)
if !ok {
return nil, fmt.Errorf("header %q refers to missing environment variable %q", header, envVarName)
}
expandedHeader = strings.Replace(expandedHeader, result, envVarValue, -1)
}
expandedHeaders[idx] = expandedHeader
}
return expandedHeaders, nil
}
var base64Codecs = []*base64.Encoding{base64.StdEncoding, base64.URLEncoding, base64.RawStdEncoding, base64.RawURLEncoding}
func decode(val string) (string, error) {
var firstErr error
var b []byte
// we are lenient and can accept any of the flavors of base64 encoding
for _, d := range base64Codecs {
var err error
b, err = d.DecodeString(val)
if err != nil {
if firstErr == nil {
firstErr = err
}
continue
}
return string(b), nil
}
return "", firstErr
}
// MetadataToString returns a string representation of the given metadata, for
// displaying to users.
func MetadataToString(md metadata.MD) string {
if len(md) == 0 {
return "(empty)"
}
keys := make([]string, 0, len(md))
for k := range md {
keys = append(keys, k)
}
sort.Strings(keys)
var b bytes.Buffer
first := true
for _, k := range keys {
vs := md[k]
for _, v := range vs {
if first {
first = false
} else {
b.WriteString("\n")
}
b.WriteString(k)
b.WriteString(": ")
if strings.HasSuffix(k, "-bin") {
v = base64.StdEncoding.EncodeToString([]byte(v))
}
b.WriteString(v)
}
}
return b.String()
}
var printer = &protoprint.Printer{
Compact: true,
OmitComments: protoprint.CommentsNonDoc,
SortElements: true,
ForceFullyQualifiedNames: true,
}
// GetDescriptorText returns a string representation of the given descriptor.
// This returns a snippet of proto source that describes the given element.
func GetDescriptorText(dsc desc.Descriptor, _ DescriptorSource) (string, error) {
// Note: DescriptorSource is not used, but remains an argument for backwards
// compatibility with previous implementation.
txt, err := printer.PrintProtoToString(dsc)
if err != nil {
return "", err
}
// callers don't expect trailing newlines
if txt[len(txt)-1] == '\n' {
txt = txt[:len(txt)-1]
}
return txt, nil
}
// EnsureExtensions uses the given descriptor source to download extensions for
// the given message. It returns a copy of the given message, but as a dynamic
// message that knows about all extensions known to the given descriptor source.
func EnsureExtensions(source DescriptorSource, msg proto.Message) proto.Message {
// load any server extensions so we can properly describe custom options
dsc, err := desc.LoadMessageDescriptorForMessage(msg)
if err != nil {
return msg
}
var ext dynamic.ExtensionRegistry
if err = fetchAllExtensions(source, &ext, dsc, map[string]bool{}); err != nil {
return msg
}
// convert message into dynamic message that knows about applicable extensions
// (that way we can show meaningful info for custom options instead of printing as unknown)
msgFactory := dynamic.NewMessageFactoryWithExtensionRegistry(&ext)
dm, err := fullyConvertToDynamic(msgFactory, msg)
if err != nil {
return msg
}
return dm
}
// fetchAllExtensions recursively fetches from the server extensions for the given message type as well as
// for all message types of nested fields. The extensions are added to the given dynamic registry of extensions
// so that all server-known extensions can be correctly parsed by grpcurl.
func fetchAllExtensions(source DescriptorSource, ext *dynamic.ExtensionRegistry, md *desc.MessageDescriptor, alreadyFetched map[string]bool) error {
msgTypeName := md.GetFullyQualifiedName()
if alreadyFetched[msgTypeName] {
return nil
}
alreadyFetched[msgTypeName] = true
if len(md.GetExtensionRanges()) > 0 {
fds, err := source.AllExtensionsForType(msgTypeName)
if err != nil {
return fmt.Errorf("failed to query for extensions of type %s: %v", msgTypeName, err)
}
for _, fd := range fds {
if err := ext.AddExtension(fd); err != nil {
return fmt.Errorf("could not register extension %s of type %s: %v", fd.GetFullyQualifiedName(), msgTypeName, err)
}
}
}
// recursively fetch extensions for the types of any message fields
for _, fd := range md.GetFields() {
if fd.GetMessageType() != nil {
err := fetchAllExtensions(source, ext, fd.GetMessageType(), alreadyFetched)
if err != nil {
return err
}
}
}
return nil
}
// fullyConvertToDynamic attempts to convert the given message to a dynamic message as well
// as any nested messages it may contain as field values. If the given message factory has
// extensions registered that were not known when the given message was parsed, this effectively
// allows re-parsing to identify those extensions.
func fullyConvertToDynamic(msgFact *dynamic.MessageFactory, msg proto.Message) (proto.Message, error) {
if _, ok := msg.(*dynamic.Message); ok {
return msg, nil // already a dynamic message
}
md, err := desc.LoadMessageDescriptorForMessage(msg)
if err != nil {
return nil, err
}
newMsg := msgFact.NewMessage(md)
dm, ok := newMsg.(*dynamic.Message)
if !ok {
// if message factory didn't produce a dynamic message, then we should leave msg as is
return msg, nil
}
if err := dm.ConvertFrom(msg); err != nil {
return nil, err
}
// recursively convert all field values, too
for _, fd := range md.GetFields() {
if fd.IsMap() {
if fd.GetMapValueType().GetMessageType() != nil {
m := dm.GetField(fd).(map[interface{}]interface{})
for k, v := range m {
// keys can't be nested messages; so we only need to recurse through map values, not keys
newVal, err := fullyConvertToDynamic(msgFact, v.(proto.Message))
if err != nil {
return nil, err
}
dm.PutMapField(fd, k, newVal)
}
}
} else if fd.IsRepeated() {
if fd.GetMessageType() != nil {
s := dm.GetField(fd).([]interface{})
for i, e := range s {
newVal, err := fullyConvertToDynamic(msgFact, e.(proto.Message))
if err != nil {
return nil, err
}
dm.SetRepeatedField(fd, i, newVal)
}
}
} else {
if fd.GetMessageType() != nil {
v := dm.GetField(fd)
newVal, err := fullyConvertToDynamic(msgFact, v.(proto.Message))
if err != nil {
return nil, err
}
dm.SetField(fd, newVal)
}
}
}
return dm, nil
}
// MakeTemplate returns a message instance for the given descriptor that is a
// suitable template for creating an instance of that message in JSON. In
// particular, it ensures that any repeated fields (which include map fields)
// are not empty, so they will render with a single element (to show the types
// and optionally nested fields). It also ensures that nested messages are not
// nil by setting them to a message that is also fleshed out as a template
// message.
func MakeTemplate(md *desc.MessageDescriptor) proto.Message {
return makeTemplate(md, nil)
}
func makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescriptor) proto.Message {
switch md.GetFullyQualifiedName() {
case "google.protobuf.Any":
// empty type URL is not allowed by JSON representation
// so we must give it a dummy type
var anyVal anypb.Any
_ = anypb.MarshalFrom(&anyVal, &emptypb.Empty{}, protov2.MarshalOptions{})
return &anyVal
case "google.protobuf.Value":
// unset kind is not allowed by JSON representation
// so we must give it something
return &structpb.Value{
Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{
Fields: map[string]*structpb.Value{
"google.protobuf.Value": {Kind: &structpb.Value_StringValue{
StringValue: "supports arbitrary JSON",
}},
},
}},
}
case "google.protobuf.ListValue":
return &structpb.ListValue{
Values: []*structpb.Value{
{
Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{
Fields: map[string]*structpb.Value{
"google.protobuf.ListValue": {Kind: &structpb.Value_StringValue{
StringValue: "is an array of arbitrary JSON values",
}},
},
}},
},
},
}
case "google.protobuf.Struct":
return &structpb.Struct{
Fields: map[string]*structpb.Value{
"google.protobuf.Struct": {Kind: &structpb.Value_StringValue{
StringValue: "supports arbitrary JSON objects",
}},
},
}
}
dm := dynamic.NewMessage(md)
// if the message is a recursive structure, we don't want to blow the stack
if slices.Contains(path, md) {
// already visited this type; avoid infinite recursion
return dm
}
path = append(path, dm.GetMessageDescriptor())
// for repeated fields, add a single element with default value
// and for message fields, add a message with all default fields
// that also has non-nil message and non-empty repeated fields
for _, fd := range dm.GetMessageDescriptor().GetFields() {
if fd.IsRepeated() {
switch fd.GetType() {
case descriptorpb.FieldDescriptorProto_TYPE_FIXED32,
descriptorpb.FieldDescriptorProto_TYPE_UINT32:
dm.AddRepeatedField(fd, uint32(0))
case descriptorpb.FieldDescriptorProto_TYPE_SFIXED32,
descriptorpb.FieldDescriptorProto_TYPE_SINT32,
descriptorpb.FieldDescriptorProto_TYPE_INT32,
descriptorpb.FieldDescriptorProto_TYPE_ENUM:
dm.AddRepeatedField(fd, int32(0))
case descriptorpb.FieldDescriptorProto_TYPE_FIXED64,
descriptorpb.FieldDescriptorProto_TYPE_UINT64:
dm.AddRepeatedField(fd, uint64(0))
case descriptorpb.FieldDescriptorProto_TYPE_SFIXED64,
descriptorpb.FieldDescriptorProto_TYPE_SINT64,
descriptorpb.FieldDescriptorProto_TYPE_INT64:
dm.AddRepeatedField(fd, int64(0))
case descriptorpb.FieldDescriptorProto_TYPE_STRING:
dm.AddRepeatedField(fd, "")
case descriptorpb.FieldDescriptorProto_TYPE_BYTES:
dm.AddRepeatedField(fd, []byte{})
case descriptorpb.FieldDescriptorProto_TYPE_BOOL:
dm.AddRepeatedField(fd, false)
case descriptorpb.FieldDescriptorProto_TYPE_FLOAT:
dm.AddRepeatedField(fd, float32(0))
case descriptorpb.FieldDescriptorProto_TYPE_DOUBLE:
dm.AddRepeatedField(fd, float64(0))
case descriptorpb.FieldDescriptorProto_TYPE_MESSAGE,
descriptorpb.FieldDescriptorProto_TYPE_GROUP:
dm.AddRepeatedField(fd, makeTemplate(fd.GetMessageType(), path))
}
} else if fd.GetMessageType() != nil {
dm.SetField(fd, makeTemplate(fd.GetMessageType(), path))
}
}
return dm
}
// ClientTransportCredentials is a helper function that constructs a TLS config with
// the given properties (see ClientTLSConfig) and then constructs and returns gRPC
// transport credentials using that config.
//
// Deprecated: Use grpcurl.ClientTLSConfig and credentials.NewTLS instead.
func ClientTransportCredentials(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string) (credentials.TransportCredentials, error) {
tlsConf, err := ClientTLSConfig(insecureSkipVerify, cacertFile, clientCertFile, clientKeyFile)
if err != nil {
return nil, err
}
return credentials.NewTLS(tlsConf), nil
}
// ClientTLSConfig builds transport-layer config for a gRPC client using the
// given properties. If cacertFile is blank, only standard trusted certs are used to
// verify the server certs. If clientCertFile is blank, the client will not use a client
// certificate. If clientCertFile is not blank then clientKeyFile must not be blank.
func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string) (*tls.Config, error) {
var tlsConf tls.Config
if clientCertFile != "" {
// Load the client certificates from disk
certificate, err := tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
if err != nil {
return nil, fmt.Errorf("could not load client key pair: %v", err)
}
tlsConf.Certificates = []tls.Certificate{certificate}
}
if insecureSkipVerify {
tlsConf.InsecureSkipVerify = true
} else if cacertFile != "" {
// Create a certificate pool from the certificate authority
certPool := x509.NewCertPool()
ca, err := os.ReadFile(cacertFile)
if err != nil {
return nil, fmt.Errorf("could not read ca certificate: %v", err)
}
// Append the certificates from the CA
if ok := certPool.AppendCertsFromPEM(ca); !ok {
return nil, errors.New("failed to append ca certs")
}
tlsConf.RootCAs = certPool
}
return &tlsConf, nil
}
// ServerTransportCredentials builds transport credentials for a gRPC server using the
// given properties. If cacertFile is blank, the server will not request client certs
// unless requireClientCerts is true. When requireClientCerts is false and cacertFile is
// not blank, the server will verify client certs when presented, but will not require
// client certs. The serverCertFile and serverKeyFile must both not be blank.
func ServerTransportCredentials(cacertFile, serverCertFile, serverKeyFile string, requireClientCerts bool) (credentials.TransportCredentials, error) {
var tlsConf tls.Config
// TODO(jh): Remove this line once https://github.com/golang/go/issues/28779 is fixed
// in Go tip. Until then, the recently merged TLS 1.3 support breaks the TLS tests.
tlsConf.MaxVersion = tls.VersionTLS12
// Load the server certificates from disk
certificate, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)
if err != nil {
return nil, fmt.Errorf("could not load key pair: %v", err)
}
tlsConf.Certificates = []tls.Certificate{certificate}
if cacertFile != "" {
// Create a certificate pool from the certificate authority
certPool := x509.NewCertPool()
ca, err := os.ReadFile(cacertFile)
if err != nil {
return nil, fmt.Errorf("could not read ca certificate: %v", err)
}
// Append the certificates from the CA
if ok := certPool.AppendCertsFromPEM(ca); !ok {
return nil, errors.New("failed to append ca certs")
}
tlsConf.ClientCAs = certPool
}
if requireClientCerts {
tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
} else if cacertFile != "" {
tlsConf.ClientAuth = tls.VerifyClientCertIfGiven
} else {
tlsConf.ClientAuth = tls.NoClientCert
}
return credentials.NewTLS(&tlsConf), nil
}
// BlockingDial is a helper method to dial the given address, using optional TLS credentials,
// and blocking until the returned connection is ready. If the given credentials are nil, the
// connection will be insecure (plain-text).
// The network parameter should be left empty in most cases when your address is a RFC 3986
// compliant URI. The resolver from grpc-go will resolve the correct network type.
func BlockingDial(ctx context.Context, network, address string, creds credentials.TransportCredentials, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
if creds == nil {
creds = insecure.NewCredentials()
}
var err error
if strings.HasPrefix(address, "xds:///") {
// The xds:/// prefix is used to signal to the gRPC client to use an xDS server to resolve the
// target. The relevant credentials will be automatically pulled from the GRPC_XDS_BOOTSTRAP or
// GRPC_XDS_BOOTSTRAP_CONFIG env vars.
creds, err = xdsCredentials.NewClientCredentials(xdsCredentials.ClientOptions{FallbackCreds: creds})
if err != nil {
return nil, err
}
}
// grpc.Dial doesn't provide any information on permanent connection errors (like
// TLS handshake failures). So in order to provide good error messages, we need a
// custom dialer that can provide that info. That means we manage the TLS handshake.
result := make(chan interface{}, 1)
writeResult := func(res interface{}) {
// non-blocking write: we only need the first result
select {
case result <- res:
default:
}
}
// custom credentials and dialer will notify on error via the
// writeResult function
creds = &errSignalingCreds{
TransportCredentials: creds,
writeResult: writeResult,
}
switch network {
case "":
// no-op, use address as-is
case "tcp":
if strings.HasPrefix(address, "unix://") {
return nil, fmt.Errorf("tcp network type cannot use unix address %s", address)
}
case "unix":
if !strings.HasPrefix(address, "unix://") {
// prepend unix:// to the address if it's not already there
// this is to maintain backwards compatibility because the custom dialer is replaced by
// the default dialer in grpc-go.
// https://github.com/fullstorydev/grpcurl/pull/480
address = "unix://" + address
}
default:
// custom dialer for other networks
dialer := func(ctx context.Context, address string) (net.Conn, error) {
conn, err := (&net.Dialer{}).DialContext(ctx, network, address)
if err != nil {
// capture the error so we can provide a better message
writeResult(err)
}
return conn, err
}
opts = append([]grpc.DialOption{grpc.WithContextDialer(dialer)}, opts...)
}
// Even with grpc.FailOnNonTempDialError, this call will usually timeout in
// the face of TLS handshake errors. So we can't rely on grpc.WithBlock() to
// know when we're done. So we run it in a goroutine and then use result
// channel to either get the connection or fail-fast.
go func() {
// We put grpc.FailOnNonTempDialError *before* the explicitly provided
// options so that it could be overridden.
opts = append([]grpc.DialOption{grpc.FailOnNonTempDialError(true)}, opts...)
// But we don't want caller to be able to override these two, so we put
// them *after* the explicitly provided options.
opts = append(opts, grpc.WithBlock(), grpc.WithTransportCredentials(creds))
conn, err := grpc.DialContext(ctx, address, opts...)
var res interface{}
if err != nil {
res = err
} else {
res = conn
}
writeResult(res)
}()
select {
case res := <-result:
if conn, ok := res.(*grpc.ClientConn); ok {
return conn, nil
}
return nil, res.(error)
case <-ctx.Done():
return nil, ctx.Err()
}
}
// errSignalingCreds is a wrapper around a TransportCredentials value, but
// it will use the writeResult function to notify on error.
type errSignalingCreds struct {
credentials.TransportCredentials
writeResult func(res interface{})
}
func (c *errSignalingCreds) ClientHandshake(ctx context.Context, addr string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
conn, auth, err := c.TransportCredentials.ClientHandshake(ctx, addr, rawConn)
if err != nil {
c.writeResult(err)
}
return conn, auth, err
}
================================================
FILE: grpcurl_test.go
================================================
package grpcurl_test
import (
"context"
"encoding/json"
"fmt"
"io"
"net"
"os"
"reflect"
"strings"
"testing"
"time"
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/grpcreflect"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/reflection"
"google.golang.org/grpc/status"
. "github.com/fullstorydev/grpcurl"
grpcurl_testing "github.com/fullstorydev/grpcurl/internal/testing"
jsonpbtest "github.com/fullstorydev/grpcurl/internal/testing/jsonpb_test_proto"
)
var (
sourceProtoset DescriptorSource
sourceProtoFiles DescriptorSource
ccNoReflect *grpc.ClientConn
sourceReflect DescriptorSource
ccReflect *grpc.ClientConn
descSources []descSourceCase
)
type descSourceCase struct {
name string
source DescriptorSource
includeRefl bool
}
// NB: These tests intentionally use the deprecated InvokeRpc since that
// calls the other (non-deprecated InvokeRPC). That allows the tests to
// easily exercise both functions.
func TestMain(m *testing.M) {
var err error
sourceProtoset, err = DescriptorSourceFromProtoSets("internal/testing/test.protoset")
if err != nil {
panic(err)
}
sourceProtoFiles, err = DescriptorSourceFromProtoFiles([]string{"internal/testing"}, "test.proto")
if err != nil {
panic(err)
}
// Create a server that includes the reflection service
svrReflect := grpc.NewServer()
grpcurl_testing.RegisterTestServiceServer(svrReflect, grpcurl_testing.TestServer{})
reflection.Register(svrReflect)
var portReflect int
if l, err := net.Listen("tcp", "127.0.0.1:0"); err != nil {
panic(err)
} else {
portReflect = l.Addr().(*net.TCPAddr).Port
go svrReflect.Serve(l)
}
defer svrReflect.Stop()
// And a corresponding client
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if ccReflect, err = grpc.DialContext(ctx, fmt.Sprintf("127.0.0.1:%d", portReflect),
grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()); err != nil {
panic(err)
}
defer ccReflect.Close()
refClient := grpcreflect.NewClientAuto(context.Background(), ccReflect)
defer refClient.Reset()
sourceReflect = DescriptorSourceFromServer(context.Background(), refClient)
// Also create a server that does *not* include the reflection service
svrProtoset := grpc.NewServer()
grpcurl_testing.RegisterTestServiceServer(svrProtoset, grpcurl_testing.TestServer{})
var portProtoset int
if l, err := net.Listen("tcp", "127.0.0.1:0"); err != nil {
panic(err)
} else {
portProtoset = l.Addr().(*net.TCPAddr).Port
go svrProtoset.Serve(l)
}
defer svrProtoset.Stop()
// And a corresponding client
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if ccNoReflect, err = grpc.DialContext(ctx, fmt.Sprintf("127.0.0.1:%d", portProtoset),
grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()); err != nil {
panic(err)
}
defer ccNoReflect.Close()
descSources = []descSourceCase{
{"protoset", sourceProtoset, false},
{"proto", sourceProtoFiles, false},
{"reflect", sourceReflect, true},
}
os.Exit(m.Run())
}
func TestServerDoesNotSupportReflection(t *testing.T) {
refClient := grpcreflect.NewClientAuto(context.Background(), ccNoReflect)
defer refClient.Reset()
refSource := DescriptorSourceFromServer(context.Background(), refClient)
_, err := ListServices(refSource)
if err != ErrReflectionNotSupported {
t.Errorf("ListServices should have returned ErrReflectionNotSupported; instead got %v", err)
}
_, err = ListMethods(refSource, "SomeService")
if err != ErrReflectionNotSupported {
t.Errorf("ListMethods should have returned ErrReflectionNotSupported; instead got %v", err)
}
err = InvokeRpc(context.Background(), refSource, ccNoReflect, "FooService/Method", nil, nil, nil)
// InvokeRpc wraps the error, so we just verify the returned error includes the right message
if err == nil || !strings.Contains(err.Error(), ErrReflectionNotSupported.Error()) {
t.Errorf("InvokeRpc should have returned ErrReflectionNotSupported; instead got %v", err)
}
}
func TestProtosetWithImports(t *testing.T) {
sourceProtoset, err := DescriptorSourceFromProtoSets("internal/testing/example.protoset")
if err != nil {
t.Fatalf("failed to load protoset: %v", err)
}
// really shallow check of the loaded descriptors
if sd, err := sourceProtoset.FindSymbol("TestService"); err != nil {
t.Errorf("failed to find TestService in protoset: %v", err)
} else if sd == nil {
t.Errorf("FindSymbol returned nil for TestService")
} else if _, ok := sd.(*desc.ServiceDescriptor); !ok {
t.Errorf("FindSymbol returned wrong kind of descriptor for TestService: %T", sd)
}
if md, err := sourceProtoset.FindSymbol("TestRequest"); err != nil {
t.Errorf("failed to find TestRequest in protoset: %v", err)
} else if md == nil {
t.Errorf("FindSymbol returned nil for TestRequest")
} else if _, ok := md.(*desc.MessageDescriptor); !ok {
t.Errorf("FindSymbol returned wrong kind of descriptor for TestRequest: %T", md)
}
}
func TestListServices(t *testing.T) {
for _, ds := range descSources {
t.Run(ds.name, func(t *testing.T) {
doTestListServices(t, ds.source, ds.includeRefl)
})
}
}
func doTestListServices(t *testing.T, source DescriptorSource, includeReflection bool) {
names, err := ListServices(source)
if err != nil {
t.Fatalf("failed to list services: %v", err)
}
var expected []string
if includeReflection {
// when using server reflection, we see the TestService as well as the ServerReflection service
expected = []string{"grpc.reflection.v1.ServerReflection", "grpc.reflection.v1alpha.ServerReflection", "testing.TestService"}
} else {
// without reflection, we see all services defined in the same test.proto file, which is the
// TestService as well as UnimplementedService
expected = []string{"testing.TestService", "testing.UnimplementedService"}
}
if !reflect.DeepEqual(expected, names) {
t.Errorf("ListServices returned wrong results: wanted %v, got %v", expected, names)
}
}
func TestListMethods(t *testing.T) {
for _, ds := range descSources {
t.Run(ds.name, func(t *testing.T) {
doTestListMethods(t, ds.source, ds.includeRefl)
})
}
}
func doTestListMethods(t *testing.T, source DescriptorSource, includeReflection bool) {
names, err := ListMethods(source, "testing.TestService")
if err != nil {
t.Fatalf("failed to list methods for TestService: %v", err)
}
expected := []string{
"testing.TestService.EmptyCall",
"testing.TestService.FullDuplexCall",
"testing.TestService.HalfDuplexCall",
"testing.TestService.StreamingInputCall",
"testing.TestService.StreamingOutputCall",
"testing.TestService.UnaryCall",
}
if !reflect.DeepEqual(expected, names) {
t.Errorf("ListMethods returned wrong results: wanted %v, got %v", expected, names)
}
if includeReflection {
// when using server reflection, we see the TestService as well as the ServerReflection service
names, err = ListMethods(source, "grpc.reflection.v1.ServerReflection")
if err != nil {
t.Fatalf("failed to list methods for ServerReflection: %v", err)
}
expected = []string{"grpc.reflection.v1.ServerReflection.ServerReflectionInfo"}
} else {
// without reflection, we see all services defined in the same test.proto file, which is the
// TestService as well as UnimplementedService
names, err = ListMethods(source, "testing.UnimplementedService")
if err != nil {
t.Fatalf("failed to list methods for ServerReflection: %v", err)
}
expected = []string{"testing.UnimplementedService.UnimplementedCall"}
}
if !reflect.DeepEqual(expected, names) {
t.Errorf("ListMethods returned wrong results: wanted %v, got %v", expected, names)
}
// force an error
_, err = ListMethods(source, "FooService")
if err != nil && !strings.Contains(err.Error(), "Symbol not found: FooService") {
t.Errorf("ListMethods should have returned 'not found' error but instead returned %v", err)
}
}
func TestGetAllFiles(t *testing.T) {
expectedFiles := []string{"test.proto"}
expectedFilesWithReflection := []string{
"grpc/reflection/v1/reflection.proto", "grpc/reflection/v1alpha/reflection.proto", "test.proto",
}
for _, ds := range descSources {
t.Run(ds.name, func(t *testing.T) {
files, err := GetAllFiles(ds.source)
if err != nil {
t.Fatalf("failed to get all files: %v", err)
}
names := fileNames(files)
match := false
var expected []string
if ds.includeRefl {
expected = expectedFilesWithReflection
} else {
expected = expectedFiles
}
match = reflect.DeepEqual(expected, names)
if !match {
t.Errorf("GetAllFiles returned wrong results: wanted %v, got %v", expected, names)
}
})
}
// try cases with more complicated set of files
otherSourceProtoset, err := DescriptorSourceFromProtoSets("internal/testing/test.protoset", "internal/testing/example.protoset")
if err != nil {
t.Fatal(err.Error())
}
otherSourceProtoFiles, err := DescriptorSourceFromProtoFiles([]string{"internal/testing"}, "test.proto", "example.proto")
if err != nil {
t.Fatal(err.Error())
}
otherDescSources := []descSourceCase{
{"protoset[b]", otherSourceProtoset, false},
{"proto[b]", otherSourceProtoFiles, false},
}
expectedFiles = []string{
"example.proto",
"example2.proto",
"google/protobuf/any.proto",
"google/protobuf/descriptor.proto",
"google/protobuf/empty.proto",
"google/protobuf/timestamp.proto",
"test.proto",
}
for _, ds := range otherDescSources {
t.Run(ds.name, func(t *testing.T) {
files, err := GetAllFiles(ds.source)
if err != nil {
t.Fatalf("failed to get all files: %v", err)
}
names := fileNames(files)
if !reflect.DeepEqual(expectedFiles, names) {
t.Errorf("GetAllFiles returned wrong results: wanted %v, got %v", expectedFiles, names)
}
})
}
}
func TestExpandHeaders(t *testing.T) {
inHeaders := []string{"key1: ${value}", "key2: bar", "key3: ${woo", "key4: woo}", "key5: ${TEST}",
"key6: ${TEST_VAR}", "${TEST}: ${TEST_VAR}", "key8: ${EMPTY}"}
os.Setenv("value", "value")
os.Setenv("TEST", "value5")
os.Setenv("TEST_VAR", "value6")
os.Setenv("EMPTY", "")
expectedHeaders := map[string]bool{"key1: value": true, "key2: bar": true, "key3: ${woo": true, "key4: woo}": true,
"key5: value5": true, "key6: value6": true, "value5: value6": true, "key8: ": true}
outHeaders, err := ExpandHeaders(inHeaders)
if err != nil {
t.Errorf("The ExpandHeaders function generated an unexpected error %s", err)
}
for _, expandedHeader := range outHeaders {
if _, ok := expectedHeaders[expandedHeader]; !ok {
t.Errorf("The ExpandHeaders function has returned an unexpected header. Received unexpected header %s", expandedHeader)
}
}
badHeaders := []string{"key: ${DNE}"}
_, err = ExpandHeaders(badHeaders)
if err == nil {
t.Errorf("The ExpandHeaders function should return an error for missing environment variables %q", badHeaders)
}
}
func fileNames(files []*desc.FileDescriptor) []string {
names := make([]string, len(files))
for i, f := range files {
names[i] = f.GetName()
}
return names
}
const expectKnownType = `{
"dur": "0s",
"ts": "1970-01-01T00:00:00Z",
"dbl": 0,
"flt": 0,
"i64": "0",
"u64": "0",
"i32": 0,
"u32": 0,
"bool": false,
"str": "",
"bytes": null,
"st": {"google.protobuf.Struct": "supports arbitrary JSON objects"},
"an": {"@type": "type.googleapis.com/google.protobuf.Empty", "value": {}},
"lv": [{"google.protobuf.ListValue": "is an array of arbitrary JSON values"}],
"val": {"google.protobuf.Value": "supports arbitrary JSON"}
}`
func TestMakeTemplateKnownTypes(t *testing.T) {
descriptor, err := desc.LoadMessageDescriptorForMessage((*jsonpbtest.KnownTypes)(nil))
if err != nil {
t.Fatalf("failed to load descriptor: %v", err)
}
message := MakeTemplate(descriptor)
jsm := jsonpb.Marshaler{EmitDefaults: true}
out, err := jsm.MarshalToString(message)
if err != nil {
t.Fatalf("failed to marshal to JSON: %v", err)
}
// make sure template JSON matches expected
var actual, expected interface{}
if err := json.Unmarshal([]byte(out), &actual); err != nil {
t.Fatalf("failed to parse actual JSON: %v", err)
}
if err := json.Unmarshal([]byte(expectKnownType), &expected); err != nil {
t.Fatalf("failed to parse expected JSON: %v", err)
}
if !reflect.DeepEqual(actual, expected) {
t.Errorf("template message is not as expected; want:\n%s\ngot:\n%s", expectKnownType, out)
}
}
func TestDescribe(t *testing.T) {
for _, ds := range descSources {
t.Run(ds.name, func(t *testing.T) {
doTestDescribe(t, ds.source)
})
}
}
func doTestDescribe(t *testing.T, source DescriptorSource) {
sym := "testing.TestService.EmptyCall"
dsc, err := source.FindSymbol(sym)
if err != nil {
t.Fatalf("failed to get descriptor for %q: %v", sym, err)
}
if _, ok := dsc.(*desc.MethodDescriptor); !ok {
t.Fatalf("descriptor for %q was a %T (expecting a MethodDescriptor)", sym, dsc)
}
txt := proto.MarshalTextString(dsc.AsProto())
expected :=
`name: "EmptyCall"
input_type: ".testing.Empty"
output_type: ".testing.Empty"
`
if expected != txt {
t.Errorf("descriptor mismatch: expected %s, got %s", expected, txt)
}
sym = "testing.StreamingOutputCallResponse"
dsc, err = source.FindSymbol(sym)
if err != nil {
t.Fatalf("failed to get descriptor for %q: %v", sym, err)
}
if _, ok := dsc.(*desc.MessageDescriptor); !ok {
t.Fatalf("descriptor for %q was a %T (expecting a MessageDescriptor)", sym, dsc)
}
txt = proto.MarshalTextString(dsc.AsProto())
expected =
`name: "StreamingOutputCallResponse"
field: <
name: "payload"
number: 1
label: LABEL_OPTIONAL
type: TYPE_MESSAGE
type_name: ".testing.Payload"
json_name: "payload"
>
`
if expected != txt {
t.Errorf("descriptor mismatch: expected %s, got %s", expected, txt)
}
_, err = source.FindSymbol("FooService")
if err != nil && !strings.Contains(err.Error(), "Symbol not found: FooService") {
t.Errorf("FindSymbol should have returned 'not found' error but instead returned %v", err)
}
}
const (
// type == COMPRESSABLE, but that is default (since it has
// numeric value == 0) and thus doesn't actually get included
// on the wire
payload1 = `{
"payload": {
"body": "SXQncyBCdXNpbmVzcyBUaW1l"
}
}`
payload2 = `{
"payload": {
"type": "RANDOM",
"body": "Rm91eCBkdSBGYUZh"
}
}`
payload3 = `{
"payload": {
"type": "UNCOMPRESSABLE",
"body": "SGlwaG9wb3BvdGFtdXMgdnMuIFJoeW1lbm9jZXJvcw=="
}
}`
)
func getCC(includeRefl bool) *grpc.ClientConn {
if includeRefl {
return ccReflect
} else {
return ccNoReflect
}
}
func TestUnary(t *testing.T) {
for _, ds := range descSources {
t.Run(ds.name, func(t *testing.T) {
doTestUnary(t, getCC(ds.includeRefl), ds.source)
})
}
}
func doTestUnary(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
// Success
h := &handler{reqMessages: []string{payload1}}
err := InvokeRpc(context.Background(), source, cc, "testing.TestService/UnaryCall", makeHeaders(codes.OK), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
if h.check(t, "testing.TestService.UnaryCall", codes.OK, 1, 1) {
if h.respMessages[0] != payload1 {
t.Errorf("unexpected response from RPC: expecting %s; got %s", payload1, h.respMessages[0])
}
}
// Failure
h = &handler{reqMessages: []string{payload1}}
err = InvokeRpc(context.Background(), source, cc, "testing.TestService/UnaryCall", makeHeaders(codes.NotFound), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
h.check(t, "testing.TestService.UnaryCall", codes.NotFound, 1, 0)
}
func TestClientStream(t *testing.T) {
for _, ds := range descSources {
t.Run(ds.name, func(t *testing.T) {
doTestClientStream(t, getCC(ds.includeRefl), ds.source)
})
}
}
func doTestClientStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
// Success
h := &handler{reqMessages: []string{payload1, payload2, payload3}}
err := InvokeRpc(context.Background(), source, cc, "testing.TestService/StreamingInputCall", makeHeaders(codes.OK), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
if h.check(t, "testing.TestService.StreamingInputCall", codes.OK, 3, 1) {
expected :=
`{
"aggregatedPayloadSize": 61
}`
if h.respMessages[0] != expected {
t.Errorf("unexpected response from RPC: expecting %s; got %s", expected, h.respMessages[0])
}
}
// Fail fast (server rejects as soon as possible)
h = &handler{reqMessages: []string{payload1, payload2, payload3}}
err = InvokeRpc(context.Background(), source, cc, "testing.TestService/StreamingInputCall", makeHeaders(codes.InvalidArgument), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
h.check(t, "testing.TestService.StreamingInputCall", codes.InvalidArgument, -3, 0)
// Fail late (server waits until stream is complete to reject)
h = &handler{reqMessages: []string{payload1, payload2, payload3}}
err = InvokeRpc(context.Background(), source, cc, "testing.TestService/StreamingInputCall", makeHeaders(codes.Internal, true), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
h.check(t, "testing.TestService.StreamingInputCall", codes.Internal, 3, 0)
}
func TestServerStream(t *testing.T) {
for _, ds := range descSources {
t.Run(ds.name, func(t *testing.T) {
doTestServerStream(t, getCC(ds.includeRefl), ds.source)
})
}
}
func doTestServerStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
req := &grpcurl_testing.StreamingOutputCallRequest{
ResponseType: grpcurl_testing.PayloadType_COMPRESSABLE,
ResponseParameters: []*grpcurl_testing.ResponseParameters{
{Size: 10}, {Size: 20}, {Size: 30}, {Size: 40}, {Size: 50},
},
}
payload, err := (&jsonpb.Marshaler{}).MarshalToString(req)
if err != nil {
t.Fatalf("failed to construct request: %v", err)
}
// Success
h := &handler{reqMessages: []string{payload}}
err = InvokeRpc(context.Background(), source, cc, "testing.TestService/StreamingOutputCall", makeHeaders(codes.OK), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
if h.check(t, "testing.TestService.StreamingOutputCall", codes.OK, 1, 5) {
resp := &grpcurl_testing.StreamingOutputCallResponse{}
for i, msg := range h.respMessages {
if err := jsonpb.UnmarshalString(msg, resp); err != nil {
t.Errorf("failed to parse response %d: %v", i+1, err)
}
if resp.Payload.GetType() != grpcurl_testing.PayloadType_COMPRESSABLE {
t.Errorf("response %d has wrong payload type; expecting %v, got %v", i, grpcurl_testing.PayloadType_COMPRESSABLE, resp.Payload.Type)
}
if len(resp.Payload.Body) != (i+1)*10 {
t.Errorf("response %d has wrong payload size; expecting %d, got %d", i, (i+1)*10, len(resp.Payload.Body))
}
resp.Reset()
}
}
// Fail fast (server rejects as soon as possible)
h = &handler{reqMessages: []string{payload}}
err = InvokeRpc(context.Background(), source, cc, "testing.TestService/StreamingOutputCall", makeHeaders(codes.Aborted), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
h.check(t, "testing.TestService.StreamingOutputCall", codes.Aborted, 1, 0)
// Fail late (server waits until stream is complete to reject)
h = &handler{reqMessages: []string{payload}}
err = InvokeRpc(context.Background(), source, cc, "testing.TestService/StreamingOutputCall", makeHeaders(codes.AlreadyExists, true), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
h.check(t, "testing.TestService.StreamingOutputCall", codes.AlreadyExists, 1, 5)
}
func TestHalfDuplexStream(t *testing.T) {
for _, ds := range descSources {
t.Run(ds.name, func(t *testing.T) {
doTestHalfDuplexStream(t, getCC(ds.includeRefl), ds.source)
})
}
}
func doTestHalfDuplexStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
reqs := []string{payload1, payload2, payload3}
// Success
h := &handler{reqMessages: reqs}
err := InvokeRpc(context.Background(), source, cc, "testing.TestService/HalfDuplexCall", makeHeaders(codes.OK), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
if h.check(t, "testing.TestService.HalfDuplexCall", codes.OK, 3, 3) {
for i, resp := range h.respMessages {
if resp != reqs[i] {
t.Errorf("unexpected response %d from RPC:\nexpecting %q\ngot %q", i, reqs[i], resp)
}
}
}
// Fail fast (server rejects as soon as possible)
h = &handler{reqMessages: reqs}
err = InvokeRpc(context.Background(), source, cc, "testing.TestService/HalfDuplexCall", makeHeaders(codes.Canceled), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
h.check(t, "testing.TestService.HalfDuplexCall", codes.Canceled, -3, 0)
// Fail late (server waits until stream is complete to reject)
h = &handler{reqMessages: reqs}
err = InvokeRpc(context.Background(), source, cc, "testing.TestService/HalfDuplexCall", makeHeaders(codes.DataLoss, true), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
h.check(t, "testing.TestService.HalfDuplexCall", codes.DataLoss, 3, 3)
}
func TestFullDuplexStream(t *testing.T) {
for _, ds := range descSources {
t.Run(ds.name, func(t *testing.T) {
doTestFullDuplexStream(t, getCC(ds.includeRefl), ds.source)
})
}
}
func doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
reqs := make([]string, 3)
req := &grpcurl_testing.StreamingOutputCallRequest{
ResponseType: grpcurl_testing.PayloadType_RANDOM,
}
for i := range reqs {
req.ResponseParameters = append(req.ResponseParameters, &grpcurl_testing.ResponseParameters{Size: int32((i + 1) * 10)})
payload, err := (&jsonpb.Marshaler{}).MarshalToString(req)
if err != nil {
t.Fatalf("failed to construct request %d: %v", i, err)
}
reqs[i] = payload
}
// Success
h := &handler{reqMessages: reqs}
err := InvokeRpc(context.Background(), source, cc, "testing.TestService/FullDuplexCall", makeHeaders(codes.OK), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
if h.check(t, "testing.TestService.FullDuplexCall", codes.OK, 3, 6) {
resp := &grpcurl_testing.StreamingOutputCallResponse{}
i := 0
for j := 1; j < 3; j++ {
// three requests
for k := 0; k < j; k++ {
// 1 response for first request, 2 for second, etc
msg := h.respMessages[i]
if err := jsonpb.UnmarshalString(msg, resp); err != nil {
t.Errorf("failed to parse response %d: %v", i+1, err)
}
if resp.Payload.GetType() != grpcurl_testing.PayloadType_RANDOM {
t.Errorf("response %d has wrong payload type; expecting %v, got %v", i, grpcurl_testing.PayloadType_RANDOM, resp.Payload.Type)
}
if len(resp.Payload.Body) != (k+1)*10 {
t.Errorf("response %d has wrong payload size; expecting %d, got %d", i, (k+1)*10, len(resp.Payload.Body))
}
resp.Reset()
i++
}
}
}
// Fail fast (server rejects as soon as possible)
h = &handler{reqMessages: reqs}
err = InvokeRpc(context.Background(), source, cc, "testing.TestService/FullDuplexCall", makeHeaders(codes.PermissionDenied), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
h.check(t, "testing.TestService.FullDuplexCall", codes.PermissionDenied, -3, 0)
// Fail late (server waits until stream is complete to reject)
h = &handler{reqMessages: reqs}
err = InvokeRpc(context.Background(), source, cc, "testing.TestService/FullDuplexCall", makeHeaders(codes.ResourceExhausted, true), h, h.getRequestData)
if err != nil {
t.Fatalf("unexpected error during RPC: %v", err)
}
h.check(t, "testing.TestService.FullDuplexCall", codes.ResourceExhausted, 3, 6)
}
type handler struct {
method *desc.MethodDescriptor
methodCount int
reqHeaders metadata.MD
reqHeadersCount int
reqMessages []string
reqMessagesCount int
respHeaders metadata.MD
respHeadersCount int
respMessages []string
respTrailers metadata.MD
respStatus *status.Status
respTrailersCount int
}
func (h *handler) getRequestData() ([]byte, error) {
// we don't use a mutex, though this method will be called from different goroutine
// than other methods for bidi calls, because this method does not share any state
// with the other methods.
h.reqMessagesCount++
if h.reqMessagesCount > len(h.reqMessages) {
return nil, io.EOF
}
if h.reqMessagesCount > 1 {
// insert delay between messages in request stream
time.Sleep(time.Millisecond * 50)
}
return []byte(h.reqMessages[h.reqMessagesCount-1]), nil
}
func (h *handler) OnResolveMethod(md *desc.MethodDescriptor) {
h.methodCount++
h.method = md
}
func (h *handler) OnSendHeaders(md metadata.MD) {
h.reqHeadersCount++
h.reqHeaders = md
}
func (h *handler) OnReceiveHeaders(md metadata.MD) {
h.respHeadersCount++
h.respHeaders = md
}
func (h *handler) OnReceiveResponse(msg proto.Message) {
jsm := jsonpb.Marshaler{Indent: " "}
respStr, err := jsm.MarshalToString(msg)
if err != nil {
panic(fmt.Errorf("failed to generate JSON form of response message: %v", err))
}
h.respMessages = append(h.respMessages, respStr)
}
func (h *handler) OnReceiveTrailers(stat *status.Status, md metadata.MD) {
h.respTrailersCount++
h.respTrailers = md
h.respStatus = stat
}
func (h *handler) check(t *testing.T, expectedMethod string, expectedCode codes.Code, expectedRequestQueries, expectedResponses int) bool {
// verify a few things were only ever called once
if h.methodCount != 1 {
t.Errorf("expected grpcurl to invoke OnResolveMethod once; was %d", h.methodCount)
}
if h.reqHeadersCount != 1 {
t.Errorf("expected grpcurl to invoke OnSendHeaders once; was %d", h.reqHeadersCount)
}
if h.reqHeadersCount != 1 {
t.Errorf("expected grpcurl to invoke OnSendHeaders once; was %d", h.reqHeadersCount)
}
if h.respHeadersCount != 1 {
t.Errorf("expected grpcurl to invoke OnReceiveHeaders once; was %d", h.respHeadersCount)
}
if h.respTrailersCount != 1 {
t.Errorf("expected grpcurl to invoke OnReceiveTrailers once; was %d", h.respTrailersCount)
}
// check other stuff against given expectations
if h.method.GetFullyQualifiedName() != expectedMethod {
t.Errorf("wrong method: expecting %v, got %v", expectedMethod, h.method.GetFullyQualifiedName())
}
if h.respStatus.Code() != expectedCode {
t.Errorf("wrong code: expecting %v, got %v", expectedCode, h.respStatus.Code())
}
if expectedRequestQueries < 0 {
// negative expectation means "negate and expect up to that number; could be fewer"
if h.reqMessagesCount > -expectedRequestQueries+1 {
// the + 1 is because there will be an extra query that returns EOF
t.Errorf("wrong number of messages queried: expecting no more than %v, got %v", -expectedRequestQueries, h.reqMessagesCount-1)
}
} else {
if h.reqMessagesCount != expectedRequestQueries+1 {
// the + 1 is because there will be an extra query that returns EOF
t.Errorf("wrong number of messages queried: expecting %v, got %v", expectedRequestQueries, h.reqMessagesCount-1)
}
}
if len(h.respMessages) != expectedResponses {
t.Errorf("wrong number of messages received: expecting %v, got %v", expectedResponses, len(h.respMessages))
}
// also check headers and trailers came through as expected
v := h.respHeaders["some-fake-header-1"]
if len(v) != 1 || v[0] != "val1" {
t.Errorf("wrong request header for %q: %v", "some-fake-header-1", v)
}
v = h.respHeaders["some-fake-header-2"]
if len(v) != 1 || v[0] != "val2" {
t.Errorf("wrong request header for %q: %v", "some-fake-header-2", v)
}
v = h.respTrailers["some-fake-trailer-1"]
if len(v) != 1 || v[0] != "valA" {
t.Errorf("wrong request header for %q: %v", "some-fake-trailer-1", v)
}
v = h.respTrailers["some-fake-trailer-2"]
if len(v) != 1 || v[0] != "valB" {
t.Errorf("wrong request header for %q: %v", "some-fake-trailer-2", v)
}
return len(h.respMessages) == expectedResponses
}
func makeHeaders(code codes.Code, failLate ...bool) []string {
if len(failLate) > 1 {
panic("incorrect use of makeContext; should be at most one failLate flag")
}
hdrs := append(make([]string, 0, 5),
fmt.Sprintf("%s: %s", grpcurl_testing.MetadataReplyHeaders, "some-fake-header-1: val1"),
fmt.Sprintf("%s: %s", grpcurl_testing.MetadataReplyHeaders, "some-fake-header-2: val2"),
fmt.Sprintf("%s: %s", grpcurl_testing.MetadataReplyTrailers, "some-fake-trailer-1: valA"),
fmt.Sprintf("%s: %s", grpcurl_testing.MetadataReplyTrailers, "some-fake-trailer-2: valB"))
if code != codes.OK {
if len(failLate) > 0 && failLate[0] {
hdrs = append(hdrs, fmt.Sprintf("%s: %d", grpcurl_testing.MetadataFailLate, code))
} else {
hdrs = append(hdrs, fmt.Sprintf("%s: %d", grpcurl_testing.MetadataFailEarly, code))
}
}
return hdrs
}
================================================
FILE: internal/testing/cmd/bankdemo/README.md
================================================
# bankdemo
The `bankdemo` program is an example gRPC server that was used to demo `grpcurl` at Gophercon 2018.
It 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.
The 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.
In 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.
================================================
FILE: internal/testing/cmd/bankdemo/auth.go
================================================
package main
import (
"context"
"strings"
"google.golang.org/grpc/metadata"
)
func getCustomer(ctx context.Context) string {
// we'll just treat the "auth token" as if it is a
// customer ID, but reject tokens that begin with "agent"
// (those are auth tokens for support agents, not customers)
cust := getAuthCode(ctx)
if strings.HasPrefix(cust, "agent") {
return ""
}
return cust
}
func getAgent(ctx context.Context) string {
// we'll just treat the "auth token" as if it is an agent's
// user ID, but reject tokens that don't begin with "agent"
// (those are auth tokens for customers, not support agents)
agent := getAuthCode(ctx)
if !strings.HasPrefix(agent, "agent") {
return ""
}
return agent
}
func getAuthCode(ctx context.Context) string {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return ""
}
vals := md.Get("authorization")
if len(vals) != 1 {
return ""
}
pieces := strings.SplitN(strings.ToLower(vals[0]), " ", 2)
if len(pieces) != 2 {
return ""
}
if pieces[0] != "token" {
return ""
}
return pieces[1]
}
================================================
FILE: internal/testing/cmd/bankdemo/bank.go
================================================
package main
import (
"context"
"fmt"
"time"
"github.com/golang/protobuf/ptypes/empty"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// bankServer implements the Bank gRPC service.
type bankServer struct {
UnimplementedBankServer
allAccounts *accounts
}
func (s *bankServer) OpenAccount(ctx context.Context, req *OpenAccountRequest) (*Account, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
switch req.Type {
case Account_CHECKING, Account_SAVING, Account_MONEY_MARKET:
if req.InitialDepositCents < 0 {
return nil, status.Errorf(codes.InvalidArgument, "initial deposit amount cannot be negative: %s", dollars(req.InitialDepositCents))
}
case Account_LINE_OF_CREDIT, Account_LOAN, Account_EQUITIES:
if req.InitialDepositCents != 0 {
return nil, status.Errorf(codes.InvalidArgument, "initial deposit amount must be zero for account type %v: %s", req.Type, dollars(req.InitialDepositCents))
}
default:
return nil, status.Errorf(codes.InvalidArgument, "invalid account type: %v", req.Type)
}
return s.allAccounts.openAccount(cust, req.Type, req.InitialDepositCents), nil
}
func (s *bankServer) CloseAccount(ctx context.Context, req *CloseAccountRequest) (*empty.Empty, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
if err := s.allAccounts.closeAccount(cust, req.AccountNumber); err != nil {
return nil, err
}
return &empty.Empty{}, nil
}
func (s *bankServer) GetAccounts(ctx context.Context, _ *empty.Empty) (*GetAccountsResponse, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
accounts := s.allAccounts.getAllAccounts(cust)
return &GetAccountsResponse{Accounts: accounts}, nil
}
func (s *bankServer) GetTransactions(req *GetTransactionsRequest, stream Bank_GetTransactionsServer) error {
cust := getCustomer(stream.Context())
if cust == "" {
return status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
acct, err := s.allAccounts.getAccount(cust, req.AccountNumber)
if err != nil {
return err
}
var start, end time.Time
if req.Start != nil {
err := req.Start.CheckValid()
if err != nil {
return err
}
start = req.Start.AsTime()
}
if req.End != nil {
err := req.End.CheckValid()
if err != nil {
return err
}
end = req.End.AsTime()
} else {
end = time.Date(9999, 12, 31, 23, 59, 59, 999999999, time.Local)
}
txns := acct.getTransactions()
for _, txn := range txns {
err := txn.Date.CheckValid()
if err != nil {
return err
}
t := txn.Date.AsTime()
if (t.After(start) || t.Equal(start)) &&
(t.Before(end) || t.Equal(end)) {
if err := stream.Send(txn); err != nil {
return err
}
}
}
return nil
}
func (s *bankServer) Deposit(ctx context.Context, req *DepositRequest) (*BalanceResponse, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
switch req.Source {
case DepositRequest_ACH, DepositRequest_CASH, DepositRequest_CHECK, DepositRequest_WIRE:
// ok
default:
return nil, status.Errorf(codes.InvalidArgument, "unknown deposit source: %v", req.Source)
}
if req.AmountCents <= 0 {
return nil, status.Errorf(codes.InvalidArgument, "deposit amount cannot be non-positive: %s", dollars(req.AmountCents))
}
desc := fmt.Sprintf("%v deposit", req.Source)
if req.Desc != "" {
desc = fmt.Sprintf("%s: %s", desc, req.Desc)
}
acct, err := s.allAccounts.getAccount(cust, req.AccountNumber)
if err != nil {
return nil, err
}
newBalance, err := acct.newTransaction(req.AmountCents, desc)
if err != nil {
return nil, err
}
return &BalanceResponse{
AccountNumber: req.AccountNumber,
BalanceCents: newBalance,
}, nil
}
func (s *bankServer) Withdraw(ctx context.Context, req *WithdrawRequest) (*BalanceResponse, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
if req.AmountCents >= 0 {
return nil, status.Errorf(codes.InvalidArgument, "withdrawal amount cannot be non-negative: %s", dollars(req.AmountCents))
}
acct, err := s.allAccounts.getAccount(cust, req.AccountNumber)
if err != nil {
return nil, err
}
newBalance, err := acct.newTransaction(req.AmountCents, req.Desc)
if err != nil {
return nil, err
}
return &BalanceResponse{
AccountNumber: req.AccountNumber,
BalanceCents: newBalance,
}, nil
}
func (s *bankServer) Transfer(ctx context.Context, req *TransferRequest) (*TransferResponse, error) {
cust := getCustomer(ctx)
if cust == "" {
return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
}
if req.AmountCents <= 0 {
return nil, status.Errorf(codes.InvalidArgument, "transfer amount cannot be non-positive: %s", dollars(req.AmountCents))
}
var srcAcct *account
var srcDesc string
switch src := req.Source.(type) {
case *TransferRequest_ExternalSource:
srcDesc = fmt.Sprintf("ACH %09d:%06d", src.ExternalSource.AchRoutingNumber, src.ExternalSource.AchAccountNumber)
if src.ExternalSource.AchAccountNumber == 0 || src.ExternalSource.AchRoutingNumber == 0 {
return nil, status.Errorf(codes.InvalidArgument, "external source routing and account numbers cannot be zero: %s", srcDesc)
}
case *TransferRequest_SourceAccountNumber:
srcDesc = fmt.Sprintf("account %06d", src.SourceAccountNumber)
var err error
if srcAcct, err = s.allAccounts.getAccount(cust, src.SourceAccountNumber); err != nil {
return nil, err
}
}
var destAcct *account
var destDesc string
switch dest := req.Dest.(type) {
case *TransferRequest_ExternalDest:
destDesc = fmt.Sprintf("ACH %09d:%06d", dest.ExternalDest.AchRoutingNumber, dest.ExternalDest.AchAccountNumber)
if dest.ExternalDest.AchAccountNumber == 0 || dest.ExternalDest.AchRoutingNumber == 0 {
return nil, status.Errorf(codes.InvalidArgument, "external source routing and account numbers cannot be zero: %s", destDesc)
}
case *TransferRequest_DestAccountNumber:
destDesc = fmt.Sprintf("account %06d", dest.DestAccountNumber)
var err error
if destAcct, err = s.allAccounts.getAccount(cust, dest.DestAccountNumber); err != nil {
return nil, err
}
}
var srcBalance int32
if srcAcct != nil {
desc := fmt.Sprintf("transfer to %s", destDesc)
if req.Desc != "" {
desc = fmt.Sprintf("%s: %s", desc, req.Desc)
}
var err error
if srcBalance, err = srcAcct.newTransaction(-req.AmountCents, desc); err != nil {
return nil, err
}
}
var destBalance int32
if destAcct != nil {
desc := fmt.Sprintf("transfer from %s", srcDesc)
if req.Desc != "" {
desc = fmt.Sprintf("%s: %s", desc, req.Desc)
}
var err error
if destBalance, err = destAcct.newTransaction(req.AmountCents, desc); err != nil {
return nil, err
}
}
return &TransferResponse{
SrcAccountNumber: req.GetSourceAccountNumber(),
SrcBalanceCents: srcBalance,
DestAccountNumber: req.GetDestAccountNumber(),
DestBalanceCents: destBalance,
}, nil
}
================================================
FILE: internal/testing/cmd/bankdemo/bank.pb.go
================================================
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0-devel
// protoc v4.22.0
// source: bank.proto
package main
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
emptypb "google.golang.org/protobuf/types/known/emptypb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Account_Type int32
const (
Account_UNKNOWN Account_Type = 0
Account_CHECKING Account_Type = 1
Account_SAVING Account_Type = 2
Account_MONEY_MARKET Account_Type = 3
Account_LINE_OF_CREDIT Account_Type = 4
Account_LOAN Account_Type = 5
Account_EQUITIES Account_Type = 6
)
// Enum value maps for Account_Type.
var (
Account_Type_name = map[int32]string{
0: "UNKNOWN",
1: "CHECKING",
2: "SAVING",
3: "MONEY_MARKET",
4: "LINE_OF_CREDIT",
5: "LOAN",
6: "EQUITIES",
}
Account_Type_value = map[string]int32{
"UNKNOWN": 0,
"CHECKING": 1,
"SAVING": 2,
"MONEY_MARKET": 3,
"LINE_OF_CREDIT": 4,
"LOAN": 5,
"EQUITIES": 6,
}
)
func (x Account_Type) Enum() *Account_Type {
p := new(Account_Type)
*p = x
return p
}
func (x Account_Type) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Account_Type) Descriptor() protoreflect.EnumDescriptor {
return file_bank_proto_enumTypes[0].Descriptor()
}
func (Account_Type) Type() protoreflect.EnumType {
return &file_bank_proto_enumTypes[0]
}
func (x Account_Type) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Account_Type.Descriptor instead.
func (Account_Type) EnumDescriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{3, 0}
}
type DepositRequest_Source int32
const (
DepositRequest_UNKNOWN DepositRequest_Source = 0
DepositRequest_CASH DepositRequest_Source = 1
DepositRequest_CHECK DepositRequest_Source = 2
DepositRequest_ACH DepositRequest_Source = 3
DepositRequest_WIRE DepositRequest_Source = 4
)
// Enum value maps for DepositRequest_Source.
var (
DepositRequest_Source_name = map[int32]string{
0: "UNKNOWN",
1: "CASH",
2: "CHECK",
3: "ACH",
4: "WIRE",
}
DepositRequest_Source_value = map[string]int32{
"UNKNOWN": 0,
"CASH": 1,
"CHECK": 2,
"ACH": 3,
"WIRE": 4,
}
)
func (x DepositRequest_Source) Enum() *DepositRequest_Source {
p := new(DepositRequest_Source)
*p = x
return p
}
func (x DepositRequest_Source) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (DepositRequest_Source) Descriptor() protoreflect.EnumDescriptor {
return file_bank_proto_enumTypes[1].Descriptor()
}
func (DepositRequest_Source) Type() protoreflect.EnumType {
return &file_bank_proto_enumTypes[1]
}
func (x DepositRequest_Source) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use DepositRequest_Source.Descriptor instead.
func (DepositRequest_Source) EnumDescriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{6, 0}
}
type OpenAccountRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
InitialDepositCents int32 `protobuf:"varint,1,opt,name=initial_deposit_cents,json=initialDepositCents,proto3" json:"initial_deposit_cents,omitempty"`
Type Account_Type `protobuf:"varint,2,opt,name=type,proto3,enum=Account_Type" json:"type,omitempty"`
}
func (x *OpenAccountRequest) Reset() {
*x = OpenAccountRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *OpenAccountRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*OpenAccountRequest) ProtoMessage() {}
func (x *OpenAccountRequest) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use OpenAccountRequest.ProtoReflect.Descriptor instead.
func (*OpenAccountRequest) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{0}
}
func (x *OpenAccountRequest) GetInitialDepositCents() int32 {
if x != nil {
return x.InitialDepositCents
}
return 0
}
func (x *OpenAccountRequest) GetType() Account_Type {
if x != nil {
return x.Type
}
return Account_UNKNOWN
}
type CloseAccountRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"`
}
func (x *CloseAccountRequest) Reset() {
*x = CloseAccountRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CloseAccountRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CloseAccountRequest) ProtoMessage() {}
func (x *CloseAccountRequest) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CloseAccountRequest.ProtoReflect.Descriptor instead.
func (*CloseAccountRequest) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{1}
}
func (x *CloseAccountRequest) GetAccountNumber() uint64 {
if x != nil {
return x.AccountNumber
}
return 0
}
type GetAccountsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Accounts []*Account `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts,omitempty"`
}
func (x *GetAccountsResponse) Reset() {
*x = GetAccountsResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetAccountsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetAccountsResponse) ProtoMessage() {}
func (x *GetAccountsResponse) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetAccountsResponse.ProtoReflect.Descriptor instead.
func (*GetAccountsResponse) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{2}
}
func (x *GetAccountsResponse) GetAccounts() []*Account {
if x != nil {
return x.Accounts
}
return nil
}
type Account struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"`
Type Account_Type `protobuf:"varint,2,opt,name=type,proto3,enum=Account_Type" json:"type,omitempty"`
BalanceCents int32 `protobuf:"varint,3,opt,name=balance_cents,json=balanceCents,proto3" json:"balance_cents,omitempty"`
}
func (x *Account) Reset() {
*x = Account{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Account) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Account) ProtoMessage() {}
func (x *Account) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Account.ProtoReflect.Descriptor instead.
func (*Account) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{3}
}
func (x *Account) GetAccountNumber() uint64 {
if x != nil {
return x.AccountNumber
}
return 0
}
func (x *Account) GetType() Account_Type {
if x != nil {
return x.Type
}
return Account_UNKNOWN
}
func (x *Account) GetBalanceCents() int32 {
if x != nil {
return x.BalanceCents
}
return 0
}
type GetTransactionsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"`
Start *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=start,proto3" json:"start,omitempty"`
End *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=end,proto3" json:"end,omitempty"`
}
func (x *GetTransactionsRequest) Reset() {
*x = GetTransactionsRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetTransactionsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetTransactionsRequest) ProtoMessage() {}
func (x *GetTransactionsRequest) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetTransactionsRequest.ProtoReflect.Descriptor instead.
func (*GetTransactionsRequest) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{4}
}
func (x *GetTransactionsRequest) GetAccountNumber() uint64 {
if x != nil {
return x.AccountNumber
}
return 0
}
func (x *GetTransactionsRequest) GetStart() *timestamppb.Timestamp {
if x != nil {
return x.Start
}
return nil
}
func (x *GetTransactionsRequest) GetEnd() *timestamppb.Timestamp {
if x != nil {
return x.End
}
return nil
}
type Transaction struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"`
SeqNumber uint64 `protobuf:"varint,2,opt,name=seq_number,json=seqNumber,proto3" json:"seq_number,omitempty"`
Date *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=date,proto3" json:"date,omitempty"`
AmountCents int32 `protobuf:"varint,4,opt,name=amount_cents,json=amountCents,proto3" json:"amount_cents,omitempty"`
Desc string `protobuf:"bytes,5,opt,name=desc,proto3" json:"desc,omitempty"`
}
func (x *Transaction) Reset() {
*x = Transaction{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Transaction) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Transaction) ProtoMessage() {}
func (x *Transaction) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Transaction.ProtoReflect.Descriptor instead.
func (*Transaction) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{5}
}
func (x *Transaction) GetAccountNumber() uint64 {
if x != nil {
return x.AccountNumber
}
return 0
}
func (x *Transaction) GetSeqNumber() uint64 {
if x != nil {
return x.SeqNumber
}
return 0
}
func (x *Transaction) GetDate() *timestamppb.Timestamp {
if x != nil {
return x.Date
}
return nil
}
func (x *Transaction) GetAmountCents() int32 {
if x != nil {
return x.AmountCents
}
return 0
}
func (x *Transaction) GetDesc() string {
if x != nil {
return x.Desc
}
return ""
}
type DepositRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"`
AmountCents int32 `protobuf:"varint,2,opt,name=amount_cents,json=amountCents,proto3" json:"amount_cents,omitempty"`
Source DepositRequest_Source `protobuf:"varint,3,opt,name=source,proto3,enum=DepositRequest_Source" json:"source,omitempty"`
Desc string `protobuf:"bytes,4,opt,name=desc,proto3" json:"desc,omitempty"`
}
func (x *DepositRequest) Reset() {
*x = DepositRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DepositRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DepositRequest) ProtoMessage() {}
func (x *DepositRequest) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DepositRequest.ProtoReflect.Descriptor instead.
func (*DepositRequest) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{6}
}
func (x *DepositRequest) GetAccountNumber() uint64 {
if x != nil {
return x.AccountNumber
}
return 0
}
func (x *DepositRequest) GetAmountCents() int32 {
if x != nil {
return x.AmountCents
}
return 0
}
func (x *DepositRequest) GetSource() DepositRequest_Source {
if x != nil {
return x.Source
}
return DepositRequest_UNKNOWN
}
func (x *DepositRequest) GetDesc() string {
if x != nil {
return x.Desc
}
return ""
}
type BalanceResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"`
BalanceCents int32 `protobuf:"varint,2,opt,name=balance_cents,json=balanceCents,proto3" json:"balance_cents,omitempty"`
}
func (x *BalanceResponse) Reset() {
*x = BalanceResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BalanceResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BalanceResponse) ProtoMessage() {}
func (x *BalanceResponse) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BalanceResponse.ProtoReflect.Descriptor instead.
func (*BalanceResponse) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{7}
}
func (x *BalanceResponse) GetAccountNumber() uint64 {
if x != nil {
return x.AccountNumber
}
return 0
}
func (x *BalanceResponse) GetBalanceCents() int32 {
if x != nil {
return x.BalanceCents
}
return 0
}
type WithdrawRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"`
AmountCents int32 `protobuf:"varint,2,opt,name=amount_cents,json=amountCents,proto3" json:"amount_cents,omitempty"`
Desc string `protobuf:"bytes,3,opt,name=desc,proto3" json:"desc,omitempty"`
}
func (x *WithdrawRequest) Reset() {
*x = WithdrawRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *WithdrawRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WithdrawRequest) ProtoMessage() {}
func (x *WithdrawRequest) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WithdrawRequest.ProtoReflect.Descriptor instead.
func (*WithdrawRequest) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{8}
}
func (x *WithdrawRequest) GetAccountNumber() uint64 {
if x != nil {
return x.AccountNumber
}
return 0
}
func (x *WithdrawRequest) GetAmountCents() int32 {
if x != nil {
return x.AmountCents
}
return 0
}
func (x *WithdrawRequest) GetDesc() string {
if x != nil {
return x.Desc
}
return ""
}
type TransferRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Types that are assignable to Source:
//
// *TransferRequest_SourceAccountNumber
// *TransferRequest_ExternalSource
Source isTransferRequest_Source `protobuf_oneof:"source"`
// Types that are assignable to Dest:
//
// *TransferRequest_DestAccountNumber
// *TransferRequest_ExternalDest
Dest isTransferRequest_Dest `protobuf_oneof:"dest"`
AmountCents int32 `protobuf:"varint,5,opt,name=amount_cents,json=amountCents,proto3" json:"amount_cents,omitempty"`
Desc string `protobuf:"bytes,6,opt,name=desc,proto3" json:"desc,omitempty"`
}
func (x *TransferRequest) Reset() {
*x = TransferRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TransferRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TransferRequest) ProtoMessage() {}
func (x *TransferRequest) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TransferRequest.ProtoReflect.Descriptor instead.
func (*TransferRequest) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{9}
}
func (m *TransferRequest) GetSource() isTransferRequest_Source {
if m != nil {
return m.Source
}
return nil
}
func (x *TransferRequest) GetSourceAccountNumber() uint64 {
if x, ok := x.GetSource().(*TransferRequest_SourceAccountNumber); ok {
return x.SourceAccountNumber
}
return 0
}
func (x *TransferRequest) GetExternalSource() *TransferRequest_ExternalAccount {
if x, ok := x.GetSource().(*TransferRequest_ExternalSource); ok {
return x.ExternalSource
}
return nil
}
func (m *TransferRequest) GetDest() isTransferRequest_Dest {
if m != nil {
return m.Dest
}
return nil
}
func (x *TransferRequest) GetDestAccountNumber() uint64 {
if x, ok := x.GetDest().(*TransferRequest_DestAccountNumber); ok {
return x.DestAccountNumber
}
return 0
}
func (x *TransferRequest) GetExternalDest() *TransferRequest_ExternalAccount {
if x, ok := x.GetDest().(*TransferRequest_ExternalDest); ok {
return x.ExternalDest
}
return nil
}
func (x *TransferRequest) GetAmountCents() int32 {
if x != nil {
return x.AmountCents
}
return 0
}
func (x *TransferRequest) GetDesc() string {
if x != nil {
return x.Desc
}
return ""
}
type isTransferRequest_Source interface {
isTransferRequest_Source()
}
type TransferRequest_SourceAccountNumber struct {
SourceAccountNumber uint64 `protobuf:"varint,1,opt,name=source_account_number,json=sourceAccountNumber,proto3,oneof"`
}
type TransferRequest_ExternalSource struct {
ExternalSource *TransferRequest_ExternalAccount `protobuf:"bytes,2,opt,name=external_source,json=externalSource,proto3,oneof"`
}
func (*TransferRequest_SourceAccountNumber) isTransferRequest_Source() {}
func (*TransferRequest_ExternalSource) isTransferRequest_Source() {}
type isTransferRequest_Dest interface {
isTransferRequest_Dest()
}
type TransferRequest_DestAccountNumber struct {
DestAccountNumber uint64 `protobuf:"varint,3,opt,name=dest_account_number,json=destAccountNumber,proto3,oneof"`
}
type TransferRequest_ExternalDest struct {
ExternalDest *TransferRequest_ExternalAccount `protobuf:"bytes,4,opt,name=external_dest,json=externalDest,proto3,oneof"`
}
func (*TransferRequest_DestAccountNumber) isTransferRequest_Dest() {}
func (*TransferRequest_ExternalDest) isTransferRequest_Dest() {}
type TransferResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
SrcAccountNumber uint64 `protobuf:"varint,1,opt,name=src_account_number,json=srcAccountNumber,proto3" json:"src_account_number,omitempty"`
SrcBalanceCents int32 `protobuf:"varint,2,opt,name=src_balance_cents,json=srcBalanceCents,proto3" json:"src_balance_cents,omitempty"`
DestAccountNumber uint64 `protobuf:"varint,3,opt,name=dest_account_number,json=destAccountNumber,proto3" json:"dest_account_number,omitempty"`
DestBalanceCents int32 `protobuf:"varint,4,opt,name=dest_balance_cents,json=destBalanceCents,proto3" json:"dest_balance_cents,omitempty"`
}
func (x *TransferResponse) Reset() {
*x = TransferResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TransferResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TransferResponse) ProtoMessage() {}
func (x *TransferResponse) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[10]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TransferResponse.ProtoReflect.Descriptor instead.
func (*TransferResponse) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{10}
}
func (x *TransferResponse) GetSrcAccountNumber() uint64 {
if x != nil {
return x.SrcAccountNumber
}
return 0
}
func (x *TransferResponse) GetSrcBalanceCents() int32 {
if x != nil {
return x.SrcBalanceCents
}
return 0
}
func (x *TransferResponse) GetDestAccountNumber() uint64 {
if x != nil {
return x.DestAccountNumber
}
return 0
}
func (x *TransferResponse) GetDestBalanceCents() int32 {
if x != nil {
return x.DestBalanceCents
}
return 0
}
type TransferRequest_ExternalAccount struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AchRoutingNumber uint64 `protobuf:"varint,1,opt,name=ach_routing_number,json=achRoutingNumber,proto3" json:"ach_routing_number,omitempty"`
AchAccountNumber uint64 `protobuf:"varint,2,opt,name=ach_account_number,json=achAccountNumber,proto3" json:"ach_account_number,omitempty"`
}
func (x *TransferRequest_ExternalAccount) Reset() {
*x = TransferRequest_ExternalAccount{}
if protoimpl.UnsafeEnabled {
mi := &file_bank_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TransferRequest_ExternalAccount) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TransferRequest_ExternalAccount) ProtoMessage() {}
func (x *TransferRequest_ExternalAccount) ProtoReflect() protoreflect.Message {
mi := &file_bank_proto_msgTypes[11]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TransferRequest_ExternalAccount.ProtoReflect.Descriptor instead.
func (*TransferRequest_ExternalAccount) Descriptor() ([]byte, []int) {
return file_bank_proto_rawDescGZIP(), []int{9, 0}
}
func (x *TransferRequest_ExternalAccount) GetAchRoutingNumber() uint64 {
if x != nil {
return x.AchRoutingNumber
}
return 0
}
func (x *TransferRequest_ExternalAccount) GetAchAccountNumber() uint64 {
if x != nil {
return x.AchAccountNumber
}
return 0
}
var File_bank_proto protoreflect.FileDescriptor
var file_bank_proto_rawDesc = []byte{
0x0a, 0x0a, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d,
0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6b, 0x0a, 0x12, 0x4f, 0x70,
0x65, 0x6e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x70, 0x6f,
0x73, 0x69, 0x74, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
0x13, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x43,
0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x54, 0x79, 0x70,
0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3c, 0x0a, 0x13, 0x43, 0x6c, 0x6f, 0x73, 0x65,
0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25,
0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e,
0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3b, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x08,
0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08,
0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25,
0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e,
0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x54, 0x79,
0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x6c, 0x61,
0x6e, 0x63, 0x65, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
0x0c, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x6b, 0x0a,
0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e,
0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x49, 0x4e, 0x47, 0x10, 0x01,
0x12, 0x0a, 0x0a, 0x06, 0x53, 0x41, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c,
0x4d, 0x4f, 0x4e, 0x45, 0x59, 0x5f, 0x4d, 0x41, 0x52, 0x4b, 0x45, 0x54, 0x10, 0x03, 0x12, 0x12,
0x0a, 0x0e, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x4f, 0x46, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x49, 0x54,
0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x4f, 0x41, 0x4e, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08,
0x45, 0x51, 0x55, 0x49, 0x54, 0x49, 0x45, 0x53, 0x10, 0x06, 0x22, 0x9f, 0x01, 0x0a, 0x16, 0x47,
0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61,
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x05,
0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c,
0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0xba, 0x01, 0x0a,
0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e,
0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01,
0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d,
0x62, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x71, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65,
0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x65, 0x71, 0x4e, 0x75, 0x6d, 0x62,
0x65, 0x72, 0x12, 0x2e, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x64, 0x61,
0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x6e,
0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
0x43, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x22, 0xdd, 0x01, 0x0a, 0x0e, 0x44, 0x65,
0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e,
0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01,
0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d,
0x62, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x65,
0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x61, 0x6d, 0x6f, 0x75, 0x6e,
0x74, 0x43, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e,
gitextract_zt9nhjki/ ├── .circleci/ │ └── config.yml ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ └── codesee-arch-diagram.yml ├── .gitignore ├── .goreleaser.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cmd/ │ └── grpcurl/ │ ├── grpcurl.go │ ├── indent_test.go │ └── unix.go ├── desc_source.go ├── desc_source_test.go ├── download_protoc.sh ├── format.go ├── format_test.go ├── go.mod ├── go.sum ├── grpcurl.go ├── grpcurl_test.go ├── internal/ │ └── testing/ │ ├── cmd/ │ │ ├── bankdemo/ │ │ │ ├── README.md │ │ │ ├── auth.go │ │ │ ├── bank.go │ │ │ ├── bank.pb.go │ │ │ ├── bank.proto │ │ │ ├── bank_grpc.pb.go │ │ │ ├── chat.go │ │ │ ├── db.go │ │ │ ├── main.go │ │ │ ├── support.pb.go │ │ │ ├── support.proto │ │ │ └── support_grpc.pb.go │ │ └── testserver/ │ │ ├── README.md │ │ ├── testserver.go │ │ └── unix.go │ ├── example.proto │ ├── example.protoset │ ├── example2.proto │ ├── jsonpb_test_proto/ │ │ ├── test_objects.pb.go │ │ └── test_objects.proto │ ├── test.pb.go │ ├── test.proto │ ├── test.protoset │ ├── test_grpc.pb.go │ ├── test_server.go │ └── tls/ │ ├── ca.crl │ ├── ca.crt │ ├── ca.key │ ├── client.crt │ ├── client.csr │ ├── client.key │ ├── expired.crt │ ├── expired.csr │ ├── expired.key │ ├── other.crt │ ├── other.csr │ ├── other.key │ ├── server.crt │ ├── server.csr │ ├── server.key │ ├── wrong-ca.crl │ ├── wrong-ca.crt │ ├── wrong-ca.key │ ├── wrong-client.crt │ ├── wrong-client.csr │ └── wrong-client.key ├── invoke.go ├── mk-test-files.sh ├── releasing/ │ ├── README.md │ ├── RELEASE_NOTES.md │ └── do-release.sh ├── snap/ │ ├── README.md │ └── snapcraft.yaml └── tls_settings_test.go
SYMBOL INDEX (796 symbols across 26 files)
FILE: cmd/grpcurl/grpcurl.go
constant statusCodeOffset (line 40) | statusCodeOffset = 64
constant noVersion (line 42) | noVersion = "dev build <no version set>"
function init (line 179) | func init() {
type multiString (line 235) | type multiString
method String (line 237) | func (s *multiString) String() string {
method Set (line 241) | func (s *multiString) Set(value string) error {
type compositeSource (line 248) | type compositeSource struct
method ListServices (line 253) | func (cs compositeSource) ListServices() ([]string, error) {
method FindSymbol (line 257) | func (cs compositeSource) FindSymbol(fullyQualifiedName string) (desc....
method AllExtensionsForType (line 265) | func (cs compositeSource) AllExtensionsForType(typeName string) ([]*de...
type timingData (line 289) | type timingData struct
method Child (line 297) | func (d *timingData) Child(title string) *timingData {
method Done (line 306) | func (d *timingData) Done() {
function main (line 315) | func main() {
function dumpTiming (line 850) | func dumpTiming(td *timingData, lvl int) {
function usage (line 861) | func usage() {
function prettify (line 893) | func prettify(docString string) string {
function warn (line 911) | func warn(msg string, args ...interface{}) {
function fail (line 916) | func fail(err error, msg string, args ...interface{}) {
function writeProtoset (line 932) | func writeProtoset(descSource grpcurl.DescriptorSource, symbols ...strin...
function writeProtos (line 944) | func writeProtos(descSource grpcurl.DescriptorSource, symbols ...string)...
type optionalBoolFlag (line 951) | type optionalBoolFlag struct
method String (line 955) | func (f *optionalBoolFlag) String() string {
method Set (line 962) | func (f *optionalBoolFlag) Set(s string) error {
method IsBoolFlag (line 972) | func (f *optionalBoolFlag) IsBoolFlag() bool {
function floatSecondsToDuration (line 976) | func floatSecondsToDuration(seconds float64) time.Duration {
FILE: cmd/grpcurl/indent_test.go
function TestFlagDocIndent (line 9) | func TestFlagDocIndent(t *testing.T) {
FILE: cmd/grpcurl/unix.go
function init (line 11) | func init() {
FILE: desc_source.go
type DescriptorSource (line 31) | type DescriptorSource interface
function DescriptorSourceFromProtoSets (line 43) | func DescriptorSourceFromProtoSets(fileNames ...string) (DescriptorSourc...
function DescriptorSourceFromProtoFiles (line 63) | func DescriptorSourceFromProtoFiles(importPaths []string, fileNames ...s...
function DescriptorSourceFromFileDescriptorSet (line 81) | func DescriptorSourceFromFileDescriptorSet(files *descriptorpb.FileDescr...
function resolveFileDescriptor (line 96) | func resolveFileDescriptor(unresolved map[string]*descriptorpb.FileDescr...
function DescriptorSourceFromFileDescriptors (line 122) | func DescriptorSourceFromFileDescriptors(files ...*desc.FileDescriptor) ...
function addFile (line 132) | func addFile(fd *desc.FileDescriptor, fds map[string]*desc.FileDescripto...
type fileSource (line 151) | type fileSource struct
method ListServices (line 157) | func (fs *fileSource) ListServices() ([]string, error) {
method GetAllFiles (line 175) | func (fs *fileSource) GetAllFiles() ([]*desc.FileDescriptor, error) {
method FindSymbol (line 185) | func (fs *fileSource) FindSymbol(fullyQualifiedName string) (desc.Desc...
method AllExtensionsForType (line 194) | func (fs *fileSource) AllExtensionsForType(typeName string) ([]*desc.F...
function DescriptorSourceFromServer (line 207) | func DescriptorSourceFromServer(_ context.Context, refClient *grpcreflec...
type serverSource (line 211) | type serverSource struct
method ListServices (line 215) | func (ss serverSource) ListServices() ([]string, error) {
method FindSymbol (line 220) | func (ss serverSource) FindSymbol(fullyQualifiedName string) (desc.Des...
method AllExtensionsForType (line 232) | func (ss serverSource) AllExtensionsForType(typeName string) ([]*desc....
function reflectionSupport (line 248) | func reflectionSupport(err error) error {
function WriteProtoset (line 262) | func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ....
function addFilesToSet (line 285) | func addFilesToSet(allFiles []*descriptorpb.FileDescriptorProto, expande...
function WriteProtoFiles (line 300) | func WriteProtoFiles(outProtoDirPath string, descSource DescriptorSource...
function writeProtoFile (line 322) | func writeProtoFile(outProtoDirPath string, fd *desc.FileDescriptor, pr ...
function getFileDescriptors (line 340) | func getFileDescriptors(symbols []string, descSource DescriptorSource) (...
function addFilesToFileDescriptorList (line 358) | func addFilesToFileDescriptorList(allFiles []*desc.FileDescriptor, expan...
FILE: desc_source_test.go
function TestWriteProtoset (line 12) | func TestWriteProtoset(t *testing.T) {
function loadProtoset (line 36) | func loadProtoset(path string) (*descriptorpb.FileDescriptorSet, error) {
function checkWriteProtoset (line 48) | func checkWriteProtoset(t *testing.T, descSrc DescriptorSource, protoset...
FILE: format.go
type RequestParser (line 24) | type RequestParser interface
type jsonRequestParser (line 35) | type jsonRequestParser struct
method Next (line 67) | func (f *jsonRequestParser) Next(m proto.Message) error {
method NumRequests (line 76) | func (f *jsonRequestParser) NumRequests() int {
function NewJSONRequestParser (line 51) | func NewJSONRequestParser(in io.Reader, resolver jsonpb.AnyResolver) Req...
function NewJSONRequestParserWithUnmarshaler (line 60) | func NewJSONRequestParserWithUnmarshaler(in io.Reader, unmarshaler jsonp...
constant textSeparatorChar (line 81) | textSeparatorChar = '\x1e'
type textRequestParser (line 84) | type textRequestParser struct
method Next (line 105) | func (f *textRequestParser) Next(m proto.Message) error {
method NumRequests (line 125) | func (f *textRequestParser) NumRequests() int {
function NewTextRequestParser (line 101) | func NewTextRequestParser(in io.Reader) RequestParser {
type Formatter (line 130) | type Formatter
function NewJSONFormatter (line 136) | func NewJSONFormatter(emitDefaults bool, resolver jsonpb.AnyResolver) Fo...
function NewTextFormatter (line 164) | func NewTextFormatter(includeSeparator bool) Formatter {
type textFormatter (line 169) | type textFormatter struct
method format (line 176) | func (tf *textFormatter) format(m proto.Message) (string, error) {
type Format (line 216) | type Format
constant FormatJSON (line 222) | FormatJSON = Format("json")
constant FormatText (line 229) | FormatText = Format("text")
function AnyResolverFromDescriptorSource (line 234) | func AnyResolverFromDescriptorSource(source DescriptorSource) jsonpb.Any...
function AnyResolverFromDescriptorSourceWithFallback (line 243) | func AnyResolverFromDescriptorSourceWithFallback(source DescriptorSource...
type anyResolver (line 248) | type anyResolver struct
method Resolve (line 258) | func (r *anyResolver) Resolve(typeUrl string) (proto.Message, error) {
type anyResolverWithFallback (line 316) | type anyResolverWithFallback struct
method Resolve (line 320) | func (r anyResolverWithFallback) Resolve(typeUrl string) (proto.Messag...
type unknownAny (line 345) | type unknownAny struct
method MarshalJSONPB (line 351) | func (a *unknownAny) MarshalJSONPB(jsm *jsonpb.Marshaler) ([]byte, err...
method Unmarshal (line 358) | func (a *unknownAny) Unmarshal(b []byte) error {
method Reset (line 363) | func (a *unknownAny) Reset() {
method String (line 367) | func (a *unknownAny) String() string {
method ProtoMessage (line 375) | func (a *unknownAny) ProtoMessage() {
type FormatOptions (line 381) | type FormatOptions struct
function RequestParserAndFormatter (line 407) | func RequestParserAndFormatter(format Format, descSource DescriptorSourc...
function RequestParserAndFormatterFor (line 427) | func RequestParserAndFormatterFor(format Format, descSource DescriptorSo...
type DefaultEventHandler (line 437) | type DefaultEventHandler struct
method OnResolveMethod (line 474) | func (h *DefaultEventHandler) OnResolveMethod(md *desc.MethodDescripto...
method OnSendHeaders (line 483) | func (h *DefaultEventHandler) OnSendHeaders(md metadata.MD) {
method OnReceiveHeaders (line 489) | func (h *DefaultEventHandler) OnReceiveHeaders(md metadata.MD) {
method OnReceiveResponse (line 495) | func (h *DefaultEventHandler) OnReceiveResponse(resp proto.Message) {
method OnReceiveTrailers (line 510) | func (h *DefaultEventHandler) OnReceiveTrailers(stat *status.Status, m...
function NewDefaultEventHandler (line 460) | func NewDefaultEventHandler(out io.Writer, descSource DescriptorSource, ...
function PrintStatus (line 523) | func PrintStatus(w io.Writer, stat *status.Status, formatter Formatter) {
FILE: format_test.go
function TestRequestParser (line 17) | func TestRequestParser(t *testing.T) {
function TestHandler (line 99) | func TestHandler(t *testing.T) {
function compare (line 191) | func compare(actual, expected string) bool {
function makeProto (line 205) | func makeProto() (proto.Message, error) {
FILE: grpcurl.go
function ListServices (line 43) | func ListServices(source DescriptorSource) ([]string, error) {
type sourceWithFiles (line 52) | type sourceWithFiles interface
function GetAllFiles (line 59) | func GetAllFiles(source DescriptorSource) ([]*desc.FileDescriptor, error) {
type filesByName (line 99) | type filesByName
method Len (line 101) | func (f filesByName) Len() int {
method Less (line 105) | func (f filesByName) Less(i, j int) bool {
method Swap (line 109) | func (f filesByName) Swap(i, j int) {
function addAllFilesToSet (line 113) | func addAllFilesToSet(fd *desc.FileDescriptor, all map[string]*desc.File...
function ListMethods (line 126) | func ListMethods(source DescriptorSource, serviceName string) ([]string,...
function MetadataFromHeaders (line 149) | func MetadataFromHeaders(headers []string) metadata.MD {
function ExpandHeaders (line 175) | func ExpandHeaders(headers []string) ([]string, error) {
function decode (line 202) | func decode(val string) (string, error) {
function MetadataToString (line 222) | func MetadataToString(md metadata.MD) string {
function GetDescriptorText (line 263) | func GetDescriptorText(dsc desc.Descriptor, _ DescriptorSource) (string,...
function EnsureExtensions (line 280) | func EnsureExtensions(source DescriptorSource, msg proto.Message) proto....
function fetchAllExtensions (line 305) | func fetchAllExtensions(source DescriptorSource, ext *dynamic.ExtensionR...
function fullyConvertToDynamic (line 338) | func fullyConvertToDynamic(msgFact *dynamic.MessageFactory, msg proto.Me...
function MakeTemplate (line 403) | func MakeTemplate(md *desc.MessageDescriptor) proto.Message {
function makeTemplate (line 407) | func makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescri...
function ClientTransportCredentials (line 517) | func ClientTransportCredentials(insecureSkipVerify bool, cacertFile, cli...
function ClientTLSConfig (line 530) | func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile...
function ServerTransportCredentials (line 568) | func ServerTransportCredentials(cacertFile, serverCertFile, serverKeyFil...
function BlockingDial (line 613) | func BlockingDial(ctx context.Context, network, address string, creds cr...
type errSignalingCreds (line 712) | type errSignalingCreds struct
method ClientHandshake (line 717) | func (c *errSignalingCreds) ClientHandshake(ctx context.Context, addr ...
FILE: grpcurl_test.go
type descSourceCase (line 42) | type descSourceCase struct
function TestMain (line 52) | func TestMain(m *testing.M) {
function TestServerDoesNotSupportReflection (line 119) | func TestServerDoesNotSupportReflection(t *testing.T) {
function TestProtosetWithImports (line 142) | func TestProtosetWithImports(t *testing.T) {
function TestListServices (line 164) | func TestListServices(t *testing.T) {
function doTestListServices (line 172) | func doTestListServices(t *testing.T, source DescriptorSource, includeRe...
function TestListMethods (line 191) | func TestListMethods(t *testing.T) {
function doTestListMethods (line 199) | func doTestListMethods(t *testing.T, source DescriptorSource, includeRef...
function TestGetAllFiles (line 243) | func TestGetAllFiles(t *testing.T) {
function TestExpandHeaders (line 306) | func TestExpandHeaders(t *testing.T) {
function fileNames (line 333) | func fileNames(files []*desc.FileDescriptor) []string {
constant expectKnownType (line 341) | expectKnownType = `{
function TestMakeTemplateKnownTypes (line 359) | func TestMakeTemplateKnownTypes(t *testing.T) {
function TestDescribe (line 386) | func TestDescribe(t *testing.T) {
function doTestDescribe (line 394) | func doTestDescribe(t *testing.T, source DescriptorSource) {
constant payload1 (line 447) | payload1 = `{
constant payload2 (line 452) | payload2 = `{
constant payload3 (line 458) | payload3 = `{
function getCC (line 466) | func getCC(includeRefl bool) *grpc.ClientConn {
function TestUnary (line 474) | func TestUnary(t *testing.T) {
function doTestUnary (line 482) | func doTestUnary(t *testing.T, cc *grpc.ClientConn, source DescriptorSou...
function TestClientStream (line 506) | func TestClientStream(t *testing.T) {
function doTestClientStream (line 514) | func doTestClientStream(t *testing.T, cc *grpc.ClientConn, source Descri...
function TestServerStream (line 551) | func TestServerStream(t *testing.T) {
function doTestServerStream (line 559) | func doTestServerStream(t *testing.T, cc *grpc.ClientConn, source Descri...
function TestHalfDuplexStream (line 613) | func TestHalfDuplexStream(t *testing.T) {
function doTestHalfDuplexStream (line 621) | func doTestHalfDuplexStream(t *testing.T, cc *grpc.ClientConn, source De...
function TestFullDuplexStream (line 658) | func TestFullDuplexStream(t *testing.T) {
function doTestFullDuplexStream (line 666) | func doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source De...
type handler (line 730) | type handler struct
method getRequestData (line 745) | func (h *handler) getRequestData() ([]byte, error) {
method OnResolveMethod (line 760) | func (h *handler) OnResolveMethod(md *desc.MethodDescriptor) {
method OnSendHeaders (line 765) | func (h *handler) OnSendHeaders(md metadata.MD) {
method OnReceiveHeaders (line 770) | func (h *handler) OnReceiveHeaders(md metadata.MD) {
method OnReceiveResponse (line 775) | func (h *handler) OnReceiveResponse(msg proto.Message) {
method OnReceiveTrailers (line 784) | func (h *handler) OnReceiveTrailers(stat *status.Status, md metadata.M...
method check (line 790) | func (h *handler) check(t *testing.T, expectedMethod string, expectedC...
function makeHeaders (line 852) | func makeHeaders(code codes.Code, failLate ...bool) []string {
FILE: internal/testing/cmd/bankdemo/auth.go
function getCustomer (line 10) | func getCustomer(ctx context.Context) string {
function getAgent (line 21) | func getAgent(ctx context.Context) string {
function getAuthCode (line 32) | func getAuthCode(ctx context.Context) string {
FILE: internal/testing/cmd/bankdemo/bank.go
type bankServer (line 14) | type bankServer struct
method OpenAccount (line 19) | func (s *bankServer) OpenAccount(ctx context.Context, req *OpenAccount...
method CloseAccount (line 40) | func (s *bankServer) CloseAccount(ctx context.Context, req *CloseAccou...
method GetAccounts (line 52) | func (s *bankServer) GetAccounts(ctx context.Context, _ *empty.Empty) ...
method GetTransactions (line 62) | func (s *bankServer) GetTransactions(req *GetTransactionsRequest, stre...
method Deposit (line 109) | func (s *bankServer) Deposit(ctx context.Context, req *DepositRequest)...
method Withdraw (line 144) | func (s *bankServer) Withdraw(ctx context.Context, req *WithdrawReques...
method Transfer (line 168) | func (s *bankServer) Transfer(ctx context.Context, req *TransferReques...
FILE: internal/testing/cmd/bankdemo/bank.pb.go
constant _ (line 21) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 23) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
constant _ (line 28) | _ = proto.ProtoPackageIsVersion4
type Account_Type (line 30) | type Account_Type
method Enum (line 64) | func (x Account_Type) Enum() *Account_Type {
method String (line 70) | func (x Account_Type) String() string {
method Descriptor (line 74) | func (Account_Type) Descriptor() protoreflect.EnumDescriptor {
method Type (line 78) | func (Account_Type) Type() protoreflect.EnumType {
method Number (line 82) | func (x Account_Type) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 87) | func (Account_Type) EnumDescriptor() ([]byte, []int) {
constant Account_UNKNOWN (line 33) | Account_UNKNOWN Account_Type = 0
constant Account_CHECKING (line 34) | Account_CHECKING Account_Type = 1
constant Account_SAVING (line 35) | Account_SAVING Account_Type = 2
constant Account_MONEY_MARKET (line 36) | Account_MONEY_MARKET Account_Type = 3
constant Account_LINE_OF_CREDIT (line 37) | Account_LINE_OF_CREDIT Account_Type = 4
constant Account_LOAN (line 38) | Account_LOAN Account_Type = 5
constant Account_EQUITIES (line 39) | Account_EQUITIES Account_Type = 6
type DepositRequest_Source (line 91) | type DepositRequest_Source
method Enum (line 119) | func (x DepositRequest_Source) Enum() *DepositRequest_Source {
method String (line 125) | func (x DepositRequest_Source) String() string {
method Descriptor (line 129) | func (DepositRequest_Source) Descriptor() protoreflect.EnumDescriptor {
method Type (line 133) | func (DepositRequest_Source) Type() protoreflect.EnumType {
method Number (line 137) | func (x DepositRequest_Source) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 142) | func (DepositRequest_Source) EnumDescriptor() ([]byte, []int) {
constant DepositRequest_UNKNOWN (line 94) | DepositRequest_UNKNOWN DepositRequest_Source = 0
constant DepositRequest_CASH (line 95) | DepositRequest_CASH DepositRequest_Source = 1
constant DepositRequest_CHECK (line 96) | DepositRequest_CHECK DepositRequest_Source = 2
constant DepositRequest_ACH (line 97) | DepositRequest_ACH DepositRequest_Source = 3
constant DepositRequest_WIRE (line 98) | DepositRequest_WIRE DepositRequest_Source = 4
type OpenAccountRequest (line 146) | type OpenAccountRequest struct
method Reset (line 155) | func (x *OpenAccountRequest) Reset() {
method String (line 164) | func (x *OpenAccountRequest) String() string {
method ProtoMessage (line 168) | func (*OpenAccountRequest) ProtoMessage() {}
method ProtoReflect (line 170) | func (x *OpenAccountRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 183) | func (*OpenAccountRequest) Descriptor() ([]byte, []int) {
method GetInitialDepositCents (line 187) | func (x *OpenAccountRequest) GetInitialDepositCents() int32 {
method GetType (line 194) | func (x *OpenAccountRequest) GetType() Account_Type {
type CloseAccountRequest (line 201) | type CloseAccountRequest struct
method Reset (line 209) | func (x *CloseAccountRequest) Reset() {
method String (line 218) | func (x *CloseAccountRequest) String() string {
method ProtoMessage (line 222) | func (*CloseAccountRequest) ProtoMessage() {}
method ProtoReflect (line 224) | func (x *CloseAccountRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 237) | func (*CloseAccountRequest) Descriptor() ([]byte, []int) {
method GetAccountNumber (line 241) | func (x *CloseAccountRequest) GetAccountNumber() uint64 {
type GetAccountsResponse (line 248) | type GetAccountsResponse struct
method Reset (line 256) | func (x *GetAccountsResponse) Reset() {
method String (line 265) | func (x *GetAccountsResponse) String() string {
method ProtoMessage (line 269) | func (*GetAccountsResponse) ProtoMessage() {}
method ProtoReflect (line 271) | func (x *GetAccountsResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 284) | func (*GetAccountsResponse) Descriptor() ([]byte, []int) {
method GetAccounts (line 288) | func (x *GetAccountsResponse) GetAccounts() []*Account {
type Account (line 295) | type Account struct
method Reset (line 305) | func (x *Account) Reset() {
method String (line 314) | func (x *Account) String() string {
method ProtoMessage (line 318) | func (*Account) ProtoMessage() {}
method ProtoReflect (line 320) | func (x *Account) ProtoReflect() protoreflect.Message {
method Descriptor (line 333) | func (*Account) Descriptor() ([]byte, []int) {
method GetAccountNumber (line 337) | func (x *Account) GetAccountNumber() uint64 {
method GetType (line 344) | func (x *Account) GetType() Account_Type {
method GetBalanceCents (line 351) | func (x *Account) GetBalanceCents() int32 {
type GetTransactionsRequest (line 358) | type GetTransactionsRequest struct
method Reset (line 368) | func (x *GetTransactionsRequest) Reset() {
method String (line 377) | func (x *GetTransactionsRequest) String() string {
method ProtoMessage (line 381) | func (*GetTransactionsRequest) ProtoMessage() {}
method ProtoReflect (line 383) | func (x *GetTransactionsRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 396) | func (*GetTransactionsRequest) Descriptor() ([]byte, []int) {
method GetAccountNumber (line 400) | func (x *GetTransactionsRequest) GetAccountNumber() uint64 {
method GetStart (line 407) | func (x *GetTransactionsRequest) GetStart() *timestamppb.Timestamp {
method GetEnd (line 414) | func (x *GetTransactionsRequest) GetEnd() *timestamppb.Timestamp {
type Transaction (line 421) | type Transaction struct
method Reset (line 433) | func (x *Transaction) Reset() {
method String (line 442) | func (x *Transaction) String() string {
method ProtoMessage (line 446) | func (*Transaction) ProtoMessage() {}
method ProtoReflect (line 448) | func (x *Transaction) ProtoReflect() protoreflect.Message {
method Descriptor (line 461) | func (*Transaction) Descriptor() ([]byte, []int) {
method GetAccountNumber (line 465) | func (x *Transaction) GetAccountNumber() uint64 {
method GetSeqNumber (line 472) | func (x *Transaction) GetSeqNumber() uint64 {
method GetDate (line 479) | func (x *Transaction) GetDate() *timestamppb.Timestamp {
method GetAmountCents (line 486) | func (x *Transaction) GetAmountCents() int32 {
method GetDesc (line 493) | func (x *Transaction) GetDesc() string {
type DepositRequest (line 500) | type DepositRequest struct
method Reset (line 511) | func (x *DepositRequest) Reset() {
method String (line 520) | func (x *DepositRequest) String() string {
method ProtoMessage (line 524) | func (*DepositRequest) ProtoMessage() {}
method ProtoReflect (line 526) | func (x *DepositRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 539) | func (*DepositRequest) Descriptor() ([]byte, []int) {
method GetAccountNumber (line 543) | func (x *DepositRequest) GetAccountNumber() uint64 {
method GetAmountCents (line 550) | func (x *DepositRequest) GetAmountCents() int32 {
method GetSource (line 557) | func (x *DepositRequest) GetSource() DepositRequest_Source {
method GetDesc (line 564) | func (x *DepositRequest) GetDesc() string {
type BalanceResponse (line 571) | type BalanceResponse struct
method Reset (line 580) | func (x *BalanceResponse) Reset() {
method String (line 589) | func (x *BalanceResponse) String() string {
method ProtoMessage (line 593) | func (*BalanceResponse) ProtoMessage() {}
method ProtoReflect (line 595) | func (x *BalanceResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 608) | func (*BalanceResponse) Descriptor() ([]byte, []int) {
method GetAccountNumber (line 612) | func (x *BalanceResponse) GetAccountNumber() uint64 {
method GetBalanceCents (line 619) | func (x *BalanceResponse) GetBalanceCents() int32 {
type WithdrawRequest (line 626) | type WithdrawRequest struct
method Reset (line 636) | func (x *WithdrawRequest) Reset() {
method String (line 645) | func (x *WithdrawRequest) String() string {
method ProtoMessage (line 649) | func (*WithdrawRequest) ProtoMessage() {}
method ProtoReflect (line 651) | func (x *WithdrawRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 664) | func (*WithdrawRequest) Descriptor() ([]byte, []int) {
method GetAccountNumber (line 668) | func (x *WithdrawRequest) GetAccountNumber() uint64 {
method GetAmountCents (line 675) | func (x *WithdrawRequest) GetAmountCents() int32 {
method GetDesc (line 682) | func (x *WithdrawRequest) GetDesc() string {
type TransferRequest (line 689) | type TransferRequest struct
method Reset (line 708) | func (x *TransferRequest) Reset() {
method String (line 717) | func (x *TransferRequest) String() string {
method ProtoMessage (line 721) | func (*TransferRequest) ProtoMessage() {}
method ProtoReflect (line 723) | func (x *TransferRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 736) | func (*TransferRequest) Descriptor() ([]byte, []int) {
method GetSource (line 740) | func (m *TransferRequest) GetSource() isTransferRequest_Source {
method GetSourceAccountNumber (line 747) | func (x *TransferRequest) GetSourceAccountNumber() uint64 {
method GetExternalSource (line 754) | func (x *TransferRequest) GetExternalSource() *TransferRequest_Externa...
method GetDest (line 761) | func (m *TransferRequest) GetDest() isTransferRequest_Dest {
method GetDestAccountNumber (line 768) | func (x *TransferRequest) GetDestAccountNumber() uint64 {
method GetExternalDest (line 775) | func (x *TransferRequest) GetExternalDest() *TransferRequest_ExternalA...
method GetAmountCents (line 782) | func (x *TransferRequest) GetAmountCents() int32 {
method GetDesc (line 789) | func (x *TransferRequest) GetDesc() string {
type isTransferRequest_Source (line 796) | type isTransferRequest_Source interface
type TransferRequest_SourceAccountNumber (line 800) | type TransferRequest_SourceAccountNumber struct
method isTransferRequest_Source (line 808) | func (*TransferRequest_SourceAccountNumber) isTransferRequest_Source() {}
type TransferRequest_ExternalSource (line 804) | type TransferRequest_ExternalSource struct
method isTransferRequest_Source (line 810) | func (*TransferRequest_ExternalSource) isTransferRequest_Source() {}
type isTransferRequest_Dest (line 812) | type isTransferRequest_Dest interface
type TransferRequest_DestAccountNumber (line 816) | type TransferRequest_DestAccountNumber struct
method isTransferRequest_Dest (line 824) | func (*TransferRequest_DestAccountNumber) isTransferRequest_Dest() {}
type TransferRequest_ExternalDest (line 820) | type TransferRequest_ExternalDest struct
method isTransferRequest_Dest (line 826) | func (*TransferRequest_ExternalDest) isTransferRequest_Dest() {}
type TransferResponse (line 828) | type TransferResponse struct
method Reset (line 839) | func (x *TransferResponse) Reset() {
method String (line 848) | func (x *TransferResponse) String() string {
method ProtoMessage (line 852) | func (*TransferResponse) ProtoMessage() {}
method ProtoReflect (line 854) | func (x *TransferResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 867) | func (*TransferResponse) Descriptor() ([]byte, []int) {
method GetSrcAccountNumber (line 871) | func (x *TransferResponse) GetSrcAccountNumber() uint64 {
method GetSrcBalanceCents (line 878) | func (x *TransferResponse) GetSrcBalanceCents() int32 {
method GetDestAccountNumber (line 885) | func (x *TransferResponse) GetDestAccountNumber() uint64 {
method GetDestBalanceCents (line 892) | func (x *TransferResponse) GetDestBalanceCents() int32 {
type TransferRequest_ExternalAccount (line 899) | type TransferRequest_ExternalAccount struct
method Reset (line 908) | func (x *TransferRequest_ExternalAccount) Reset() {
method String (line 917) | func (x *TransferRequest_ExternalAccount) String() string {
method ProtoMessage (line 921) | func (*TransferRequest_ExternalAccount) ProtoMessage() {}
method ProtoReflect (line 923) | func (x *TransferRequest_ExternalAccount) ProtoReflect() protoreflect....
method Descriptor (line 936) | func (*TransferRequest_ExternalAccount) Descriptor() ([]byte, []int) {
method GetAchRoutingNumber (line 940) | func (x *TransferRequest_ExternalAccount) GetAchRoutingNumber() uint64 {
method GetAchAccountNumber (line 947) | func (x *TransferRequest_ExternalAccount) GetAchAccountNumber() uint64 {
function file_bank_proto_rawDescGZIP (line 1113) | func file_bank_proto_rawDescGZIP() []byte {
function init (line 1171) | func init() { file_bank_proto_init() }
function file_bank_proto_init (line 1172) | func file_bank_proto_init() {
FILE: internal/testing/cmd/bankdemo/bank_grpc.pb.go
constant _ (line 16) | _ = grpc.SupportPackageIsVersion7
type BankClient (line 21) | type BankClient interface
type bankClient (line 45) | type bankClient struct
method OpenAccount (line 53) | func (c *bankClient) OpenAccount(ctx context.Context, in *OpenAccountR...
method CloseAccount (line 62) | func (c *bankClient) CloseAccount(ctx context.Context, in *CloseAccoun...
method GetAccounts (line 71) | func (c *bankClient) GetAccounts(ctx context.Context, in *emptypb.Empt...
method GetTransactions (line 80) | func (c *bankClient) GetTransactions(ctx context.Context, in *GetTrans...
method Deposit (line 112) | func (c *bankClient) Deposit(ctx context.Context, in *DepositRequest, ...
method Withdraw (line 121) | func (c *bankClient) Withdraw(ctx context.Context, in *WithdrawRequest...
method Transfer (line 130) | func (c *bankClient) Transfer(ctx context.Context, in *TransferRequest...
function NewBankClient (line 49) | func NewBankClient(cc grpc.ClientConnInterface) BankClient {
type Bank_GetTransactionsClient (line 95) | type Bank_GetTransactionsClient interface
type bankGetTransactionsClient (line 100) | type bankGetTransactionsClient struct
method Recv (line 104) | func (x *bankGetTransactionsClient) Recv() (*Transaction, error) {
type BankServer (line 142) | type BankServer interface
type UnimplementedBankServer (line 168) | type UnimplementedBankServer struct
method OpenAccount (line 171) | func (UnimplementedBankServer) OpenAccount(context.Context, *OpenAccou...
method CloseAccount (line 174) | func (UnimplementedBankServer) CloseAccount(context.Context, *CloseAcc...
method GetAccounts (line 177) | func (UnimplementedBankServer) GetAccounts(context.Context, *emptypb.E...
method GetTransactions (line 180) | func (UnimplementedBankServer) GetTransactions(*GetTransactionsRequest...
method Deposit (line 183) | func (UnimplementedBankServer) Deposit(context.Context, *DepositReques...
method Withdraw (line 186) | func (UnimplementedBankServer) Withdraw(context.Context, *WithdrawRequ...
method Transfer (line 189) | func (UnimplementedBankServer) Transfer(context.Context, *TransferRequ...
method mustEmbedUnimplementedBankServer (line 192) | func (UnimplementedBankServer) mustEmbedUnimplementedBankServer() {}
type UnsafeBankServer (line 197) | type UnsafeBankServer interface
function RegisterBankServer (line 201) | func RegisterBankServer(s grpc.ServiceRegistrar, srv BankServer) {
function _Bank_OpenAccount_Handler (line 205) | func _Bank_OpenAccount_Handler(srv interface{}, ctx context.Context, dec...
function _Bank_CloseAccount_Handler (line 223) | func _Bank_CloseAccount_Handler(srv interface{}, ctx context.Context, de...
function _Bank_GetAccounts_Handler (line 241) | func _Bank_GetAccounts_Handler(srv interface{}, ctx context.Context, dec...
function _Bank_GetTransactions_Handler (line 259) | func _Bank_GetTransactions_Handler(srv interface{}, stream grpc.ServerSt...
type Bank_GetTransactionsServer (line 267) | type Bank_GetTransactionsServer interface
type bankGetTransactionsServer (line 272) | type bankGetTransactionsServer struct
method Send (line 276) | func (x *bankGetTransactionsServer) Send(m *Transaction) error {
function _Bank_Deposit_Handler (line 280) | func _Bank_Deposit_Handler(srv interface{}, ctx context.Context, dec fun...
function _Bank_Withdraw_Handler (line 298) | func _Bank_Withdraw_Handler(srv interface{}, ctx context.Context, dec fu...
function _Bank_Transfer_Handler (line 316) | func _Bank_Transfer_Handler(srv interface{}, ctx context.Context, dec fu...
FILE: internal/testing/cmd/bankdemo/chat.go
type chatServer (line 17) | type chatServer struct
method ChatCustomer (line 55) | func (s *chatServer) ChatCustomer(stream Support_ChatCustomerServer) e...
method ChatAgent (line 169) | func (s *chatServer) ChatAgent(stream Support_ChatAgentServer) error {
method newSession (line 334) | func (s *chatServer) newSession(ctx context.Context, cust string) (*se...
method resumeSession (line 360) | func (s *chatServer) resumeSession(ctx context.Context, cust, sessionI...
method closeSession (line 387) | func (s *chatServer) closeSession(sess *session) {
method acceptSession (line 423) | func (s *chatServer) acceptSession(ctx context.Context, agent, session...
type session (line 25) | type session struct
method copySession (line 45) | func (s *session) copySession() *Session {
type listener (line 33) | type listener struct
method send (line 38) | func (l *listener) send(e *ChatEntry) {
FILE: internal/testing/cmd/bankdemo/db.go
type accounts (line 15) | type accounts struct
method openAccount (line 24) | func (a *accounts) openAccount(customer string, accountType Account_Ty...
method closeAccount (line 53) | func (a *accounts) closeAccount(customer string, accountNumber uint64)...
method getAccount (line 86) | func (a *accounts) getAccount(customer string, accountNumber uint64) (...
method getAllAccounts (line 98) | func (a *accounts) getAllAccounts(customer string) []*Account {
method clone (line 148) | func (a *accounts) clone() *accounts {
type account (line 110) | type account struct
method getTransactions (line 116) | func (a *account) getTransactions() []*Transaction {
method newTransaction (line 122) | func (a *account) newTransaction(amountCents int32, desc string) (newB...
method MarshalJSON (line 140) | func (t *Transaction) MarshalJSON() ([]byte, error) {
method UnmarshalJSON (line 144) | func (t *Transaction) UnmarshalJSON(b []byte) error {
function dollars (line 182) | func dollars(amountCents int32) string {
FILE: internal/testing/cmd/bankdemo/main.go
function main (line 24) | func main() {
function gRPCServer (line 86) | func gRPCServer() *grpc.Server {
type svr (line 124) | type svr struct
method load (line 131) | func (s *svr) load() error {
method bgSaver (line 146) | func (s *svr) bgSaver() {
method flush (line 159) | func (s *svr) flush() {
FILE: internal/testing/cmd/bankdemo/support.pb.go
constant _ (line 20) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 22) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
constant _ (line 27) | _ = proto.ProtoPackageIsVersion4
type Void (line 29) | type Void
method Enum (line 45) | func (x Void) Enum() *Void {
method String (line 51) | func (x Void) String() string {
method Descriptor (line 55) | func (Void) Descriptor() protoreflect.EnumDescriptor {
method Type (line 59) | func (Void) Type() protoreflect.EnumType {
method Number (line 63) | func (x Void) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 68) | func (Void) EnumDescriptor() ([]byte, []int) {
constant Void_VOID (line 32) | Void_VOID Void = 0
type ChatCustomerRequest (line 72) | type ChatCustomerRequest struct
method Reset (line 85) | func (x *ChatCustomerRequest) Reset() {
method String (line 94) | func (x *ChatCustomerRequest) String() string {
method ProtoMessage (line 98) | func (*ChatCustomerRequest) ProtoMessage() {}
method ProtoReflect (line 100) | func (x *ChatCustomerRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 113) | func (*ChatCustomerRequest) Descriptor() ([]byte, []int) {
method GetReq (line 117) | func (m *ChatCustomerRequest) GetReq() isChatCustomerRequest_Req {
method GetInit (line 124) | func (x *ChatCustomerRequest) GetInit() *InitiateChat {
method GetMsg (line 131) | func (x *ChatCustomerRequest) GetMsg() string {
method GetHangUp (line 138) | func (x *ChatCustomerRequest) GetHangUp() Void {
type isChatCustomerRequest_Req (line 145) | type isChatCustomerRequest_Req interface
type ChatCustomerRequest_Init (line 149) | type ChatCustomerRequest_Init struct
method isChatCustomerRequest_Req (line 172) | func (*ChatCustomerRequest_Init) isChatCustomerRequest_Req() {}
type ChatCustomerRequest_Msg (line 157) | type ChatCustomerRequest_Msg struct
method isChatCustomerRequest_Req (line 174) | func (*ChatCustomerRequest_Msg) isChatCustomerRequest_Req() {}
type ChatCustomerRequest_HangUp (line 163) | type ChatCustomerRequest_HangUp struct
method isChatCustomerRequest_Req (line 176) | func (*ChatCustomerRequest_HangUp) isChatCustomerRequest_Req() {}
type InitiateChat (line 178) | type InitiateChat struct
method Reset (line 186) | func (x *InitiateChat) Reset() {
method String (line 195) | func (x *InitiateChat) String() string {
method ProtoMessage (line 199) | func (*InitiateChat) ProtoMessage() {}
method ProtoReflect (line 201) | func (x *InitiateChat) ProtoReflect() protoreflect.Message {
method Descriptor (line 214) | func (*InitiateChat) Descriptor() ([]byte, []int) {
method GetResumeSessionId (line 218) | func (x *InitiateChat) GetResumeSessionId() string {
type AgentMessage (line 225) | type AgentMessage struct
method Reset (line 234) | func (x *AgentMessage) Reset() {
method String (line 243) | func (x *AgentMessage) String() string {
method ProtoMessage (line 247) | func (*AgentMessage) ProtoMessage() {}
method ProtoReflect (line 249) | func (x *AgentMessage) ProtoReflect() protoreflect.Message {
method Descriptor (line 262) | func (*AgentMessage) Descriptor() ([]byte, []int) {
method GetAgentName (line 266) | func (x *AgentMessage) GetAgentName() string {
method GetMsg (line 273) | func (x *AgentMessage) GetMsg() string {
type ChatCustomerResponse (line 280) | type ChatCustomerResponse struct
method Reset (line 292) | func (x *ChatCustomerResponse) Reset() {
method String (line 301) | func (x *ChatCustomerResponse) String() string {
method ProtoMessage (line 305) | func (*ChatCustomerResponse) ProtoMessage() {}
method ProtoReflect (line 307) | func (x *ChatCustomerResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 320) | func (*ChatCustomerResponse) Descriptor() ([]byte, []int) {
method GetResp (line 324) | func (m *ChatCustomerResponse) GetResp() isChatCustomerResponse_Resp {
method GetSession (line 331) | func (x *ChatCustomerResponse) GetSession() *Session {
method GetMsg (line 338) | func (x *ChatCustomerResponse) GetMsg() *AgentMessage {
type isChatCustomerResponse_Resp (line 345) | type isChatCustomerResponse_Resp interface
type ChatCustomerResponse_Session (line 349) | type ChatCustomerResponse_Session struct
method isChatCustomerResponse_Resp (line 362) | func (*ChatCustomerResponse_Session) isChatCustomerResponse_Resp() {}
type ChatCustomerResponse_Msg (line 356) | type ChatCustomerResponse_Msg struct
method isChatCustomerResponse_Resp (line 364) | func (*ChatCustomerResponse_Msg) isChatCustomerResponse_Resp() {}
type ChatAgentRequest (line 366) | type ChatAgentRequest struct
method Reset (line 379) | func (x *ChatAgentRequest) Reset() {
method String (line 388) | func (x *ChatAgentRequest) String() string {
method ProtoMessage (line 392) | func (*ChatAgentRequest) ProtoMessage() {}
method ProtoReflect (line 394) | func (x *ChatAgentRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 407) | func (*ChatAgentRequest) Descriptor() ([]byte, []int) {
method GetReq (line 411) | func (m *ChatAgentRequest) GetReq() isChatAgentRequest_Req {
method GetAccept (line 418) | func (x *ChatAgentRequest) GetAccept() *AcceptChat {
method GetMsg (line 425) | func (x *ChatAgentRequest) GetMsg() string {
method GetLeaveSession (line 432) | func (x *ChatAgentRequest) GetLeaveSession() Void {
type isChatAgentRequest_Req (line 439) | type isChatAgentRequest_Req interface
type ChatAgentRequest_Accept (line 443) | type ChatAgentRequest_Accept struct
method isChatAgentRequest_Req (line 466) | func (*ChatAgentRequest_Accept) isChatAgentRequest_Req() {}
type ChatAgentRequest_Msg (line 453) | type ChatAgentRequest_Msg struct
method isChatAgentRequest_Req (line 468) | func (*ChatAgentRequest_Msg) isChatAgentRequest_Req() {}
type ChatAgentRequest_LeaveSession (line 459) | type ChatAgentRequest_LeaveSession struct
method isChatAgentRequest_Req (line 470) | func (*ChatAgentRequest_LeaveSession) isChatAgentRequest_Req() {}
type AcceptChat (line 472) | type AcceptChat struct
method Reset (line 480) | func (x *AcceptChat) Reset() {
method String (line 489) | func (x *AcceptChat) String() string {
method ProtoMessage (line 493) | func (*AcceptChat) ProtoMessage() {}
method ProtoReflect (line 495) | func (x *AcceptChat) ProtoReflect() protoreflect.Message {
method Descriptor (line 508) | func (*AcceptChat) Descriptor() ([]byte, []int) {
method GetSessionId (line 512) | func (x *AcceptChat) GetSessionId() string {
type ChatEntry (line 519) | type ChatEntry struct
method Reset (line 532) | func (x *ChatEntry) Reset() {
method String (line 541) | func (x *ChatEntry) String() string {
method ProtoMessage (line 545) | func (*ChatEntry) ProtoMessage() {}
method ProtoReflect (line 547) | func (x *ChatEntry) ProtoReflect() protoreflect.Message {
method Descriptor (line 560) | func (*ChatEntry) Descriptor() ([]byte, []int) {
method GetDate (line 564) | func (x *ChatEntry) GetDate() *timestamppb.Timestamp {
method GetEntry (line 571) | func (m *ChatEntry) GetEntry() isChatEntry_Entry {
method GetCustomerMsg (line 578) | func (x *ChatEntry) GetCustomerMsg() string {
method GetAgentMsg (line 585) | func (x *ChatEntry) GetAgentMsg() *AgentMessage {
type isChatEntry_Entry (line 592) | type isChatEntry_Entry interface
type ChatEntry_CustomerMsg (line 596) | type ChatEntry_CustomerMsg struct
method isChatEntry_Entry (line 604) | func (*ChatEntry_CustomerMsg) isChatEntry_Entry() {}
type ChatEntry_AgentMsg (line 600) | type ChatEntry_AgentMsg struct
method isChatEntry_Entry (line 606) | func (*ChatEntry_AgentMsg) isChatEntry_Entry() {}
type ChatAgentResponse (line 608) | type ChatAgentResponse struct
method Reset (line 621) | func (x *ChatAgentResponse) Reset() {
method String (line 630) | func (x *ChatAgentResponse) String() string {
method ProtoMessage (line 634) | func (*ChatAgentResponse) ProtoMessage() {}
method ProtoReflect (line 636) | func (x *ChatAgentResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 649) | func (*ChatAgentResponse) Descriptor() ([]byte, []int) {
method GetResp (line 653) | func (m *ChatAgentResponse) GetResp() isChatAgentResponse_Resp {
method GetAcceptedSession (line 660) | func (x *ChatAgentResponse) GetAcceptedSession() *Session {
method GetMsg (line 667) | func (x *ChatAgentResponse) GetMsg() *ChatEntry {
method GetSessionEnded (line 674) | func (x *ChatAgentResponse) GetSessionEnded() Void {
type isChatAgentResponse_Resp (line 681) | type isChatAgentResponse_Resp interface
type ChatAgentResponse_AcceptedSession (line 685) | type ChatAgentResponse_AcceptedSession struct
method isChatAgentResponse_Resp (line 703) | func (*ChatAgentResponse_AcceptedSession) isChatAgentResponse_Resp() {}
type ChatAgentResponse_Msg (line 691) | type ChatAgentResponse_Msg struct
method isChatAgentResponse_Resp (line 705) | func (*ChatAgentResponse_Msg) isChatAgentResponse_Resp() {}
type ChatAgentResponse_SessionEnded (line 697) | type ChatAgentResponse_SessionEnded struct
method isChatAgentResponse_Resp (line 707) | func (*ChatAgentResponse_SessionEnded) isChatAgentResponse_Resp() {}
type Session (line 709) | type Session struct
method Reset (line 719) | func (x *Session) Reset() {
method String (line 728) | func (x *Session) String() string {
method ProtoMessage (line 732) | func (*Session) ProtoMessage() {}
method ProtoReflect (line 734) | func (x *Session) ProtoReflect() protoreflect.Message {
method Descriptor (line 747) | func (*Session) Descriptor() ([]byte, []int) {
method GetSessionId (line 751) | func (x *Session) GetSessionId() string {
method GetCustomerName (line 758) | func (x *Session) GetCustomerName() string {
method GetHistory (line 765) | func (x *Session) GetHistory() []*ChatEntry {
function file_support_proto_rawDescGZIP (line 856) | func file_support_proto_rawDescGZIP() []byte {
function init (line 902) | func init() { file_support_proto_init() }
function file_support_proto_init (line 903) | func file_support_proto_init() {
FILE: internal/testing/cmd/bankdemo/support_grpc.pb.go
constant _ (line 15) | _ = grpc.SupportPackageIsVersion7
type SupportClient (line 20) | type SupportClient interface
type supportClient (line 33) | type supportClient struct
method ChatCustomer (line 41) | func (c *supportClient) ChatCustomer(ctx context.Context, opts ...grpc...
method ChatAgent (line 72) | func (c *supportClient) ChatAgent(ctx context.Context, opts ...grpc.Ca...
function NewSupportClient (line 37) | func NewSupportClient(cc grpc.ClientConnInterface) SupportClient {
type Support_ChatCustomerClient (line 50) | type Support_ChatCustomerClient interface
type supportChatCustomerClient (line 56) | type supportChatCustomerClient struct
method Send (line 60) | func (x *supportChatCustomerClient) Send(m *ChatCustomerRequest) error {
method Recv (line 64) | func (x *supportChatCustomerClient) Recv() (*ChatCustomerResponse, err...
type Support_ChatAgentClient (line 81) | type Support_ChatAgentClient interface
type supportChatAgentClient (line 87) | type supportChatAgentClient struct
method Send (line 91) | func (x *supportChatAgentClient) Send(m *ChatAgentRequest) error {
method Recv (line 95) | func (x *supportChatAgentClient) Recv() (*ChatAgentResponse, error) {
type SupportServer (line 106) | type SupportServer interface
type UnimplementedSupportServer (line 121) | type UnimplementedSupportServer struct
method ChatCustomer (line 124) | func (UnimplementedSupportServer) ChatCustomer(Support_ChatCustomerSer...
method ChatAgent (line 127) | func (UnimplementedSupportServer) ChatAgent(Support_ChatAgentServer) e...
method mustEmbedUnimplementedSupportServer (line 130) | func (UnimplementedSupportServer) mustEmbedUnimplementedSupportServer(...
type UnsafeSupportServer (line 135) | type UnsafeSupportServer interface
function RegisterSupportServer (line 139) | func RegisterSupportServer(s grpc.ServiceRegistrar, srv SupportServer) {
function _Support_ChatCustomer_Handler (line 143) | func _Support_ChatCustomer_Handler(srv interface{}, stream grpc.ServerSt...
type Support_ChatCustomerServer (line 147) | type Support_ChatCustomerServer interface
type supportChatCustomerServer (line 153) | type supportChatCustomerServer struct
method Send (line 157) | func (x *supportChatCustomerServer) Send(m *ChatCustomerResponse) error {
method Recv (line 161) | func (x *supportChatCustomerServer) Recv() (*ChatCustomerRequest, erro...
function _Support_ChatAgent_Handler (line 169) | func _Support_ChatAgent_Handler(srv interface{}, stream grpc.ServerStrea...
type Support_ChatAgentServer (line 173) | type Support_ChatAgentServer interface
type supportChatAgentServer (line 179) | type supportChatAgentServer struct
method Send (line 183) | func (x *supportChatAgentServer) Send(m *ChatAgentResponse) error {
method Recv (line 187) | func (x *supportChatAgentServer) Recv() (*ChatAgentRequest, error) {
FILE: internal/testing/cmd/testserver/testserver.go
function main (line 45) | func main() {
function unaryLogger (line 111) | func unaryLogger(ctx context.Context, req interface{}, info *grpc.UnaryS...
function streamLogger (line 126) | func streamLogger(srv interface{}, ss grpc.ServerStream, info *grpc.Stre...
type loggingStream (line 141) | type loggingStream struct
method SetHeader (line 146) | func (l loggingStream) SetHeader(md metadata.MD) error {
method SendHeader (line 150) | func (l loggingStream) SendHeader(md metadata.MD) error {
method SetTrailer (line 154) | func (l loggingStream) SetTrailer(md metadata.MD) {
method Context (line 158) | func (l loggingStream) Context() context.Context {
method SendMsg (line 162) | func (l loggingStream) SendMsg(m interface{}) error {
method RecvMsg (line 170) | func (l loggingStream) RecvMsg(m interface{}) error {
FILE: internal/testing/cmd/testserver/unix.go
function init (line 14) | func init() {
FILE: internal/testing/jsonpb_test_proto/test_objects.pb.go
constant _ (line 24) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 26) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
constant _ (line 31) | _ = proto.ProtoPackageIsVersion4
type KnownTypes (line 33) | type KnownTypes struct
method Reset (line 55) | func (x *KnownTypes) Reset() {
method String (line 64) | func (x *KnownTypes) String() string {
method ProtoMessage (line 68) | func (*KnownTypes) ProtoMessage() {}
method ProtoReflect (line 70) | func (x *KnownTypes) ProtoReflect() protoreflect.Message {
method Descriptor (line 83) | func (*KnownTypes) Descriptor() ([]byte, []int) {
method GetAn (line 87) | func (x *KnownTypes) GetAn() *anypb.Any {
method GetDur (line 94) | func (x *KnownTypes) GetDur() *durationpb.Duration {
method GetSt (line 101) | func (x *KnownTypes) GetSt() *structpb.Struct {
method GetTs (line 108) | func (x *KnownTypes) GetTs() *timestamppb.Timestamp {
method GetLv (line 115) | func (x *KnownTypes) GetLv() *structpb.ListValue {
method GetVal (line 122) | func (x *KnownTypes) GetVal() *structpb.Value {
method GetDbl (line 129) | func (x *KnownTypes) GetDbl() *wrapperspb.DoubleValue {
method GetFlt (line 136) | func (x *KnownTypes) GetFlt() *wrapperspb.FloatValue {
method GetI64 (line 143) | func (x *KnownTypes) GetI64() *wrapperspb.Int64Value {
method GetU64 (line 150) | func (x *KnownTypes) GetU64() *wrapperspb.UInt64Value {
method GetI32 (line 157) | func (x *KnownTypes) GetI32() *wrapperspb.Int32Value {
method GetU32 (line 164) | func (x *KnownTypes) GetU32() *wrapperspb.UInt32Value {
method GetBool (line 171) | func (x *KnownTypes) GetBool() *wrapperspb.BoolValue {
method GetStr (line 178) | func (x *KnownTypes) GetStr() *wrapperspb.StringValue {
method GetBytes (line 185) | func (x *KnownTypes) GetBytes() *wrapperspb.BytesValue {
function file_test_objects_proto_rawDescGZIP (line 258) | func file_test_objects_proto_rawDescGZIP() []byte {
function init (line 307) | func init() { file_test_objects_proto_init() }
function file_test_objects_proto_init (line 308) | func file_test_objects_proto_init() {
FILE: internal/testing/test.pb.go
constant _ (line 38) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 40) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
constant _ (line 45) | _ = proto.ProtoPackageIsVersion4
type PayloadType (line 48) | type PayloadType
method Enum (line 73) | func (x PayloadType) Enum() *PayloadType {
method String (line 79) | func (x PayloadType) String() string {
method Descriptor (line 83) | func (PayloadType) Descriptor() protoreflect.EnumDescriptor {
method Type (line 87) | func (PayloadType) Type() protoreflect.EnumType {
method Number (line 91) | func (x PayloadType) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 96) | func (PayloadType) EnumDescriptor() ([]byte, []int) {
constant PayloadType_COMPRESSABLE (line 52) | PayloadType_COMPRESSABLE PayloadType = 0
constant PayloadType_UNCOMPRESSABLE (line 54) | PayloadType_UNCOMPRESSABLE PayloadType = 1
constant PayloadType_RANDOM (line 56) | PayloadType_RANDOM PayloadType = 2
type Empty (line 100) | type Empty struct
method Reset (line 106) | func (x *Empty) Reset() {
method String (line 115) | func (x *Empty) String() string {
method ProtoMessage (line 119) | func (*Empty) ProtoMessage() {}
method ProtoReflect (line 121) | func (x *Empty) ProtoReflect() protoreflect.Message {
method Descriptor (line 134) | func (*Empty) Descriptor() ([]byte, []int) {
type Payload (line 139) | type Payload struct
method Reset (line 150) | func (x *Payload) Reset() {
method String (line 159) | func (x *Payload) String() string {
method ProtoMessage (line 163) | func (*Payload) ProtoMessage() {}
method ProtoReflect (line 165) | func (x *Payload) ProtoReflect() protoreflect.Message {
method Descriptor (line 178) | func (*Payload) Descriptor() ([]byte, []int) {
method GetType (line 182) | func (x *Payload) GetType() PayloadType {
method GetBody (line 189) | func (x *Payload) GetBody() []byte {
type EchoStatus (line 198) | type EchoStatus struct
method Reset (line 207) | func (x *EchoStatus) Reset() {
method String (line 216) | func (x *EchoStatus) String() string {
method ProtoMessage (line 220) | func (*EchoStatus) ProtoMessage() {}
method ProtoReflect (line 222) | func (x *EchoStatus) ProtoReflect() protoreflect.Message {
method Descriptor (line 235) | func (*EchoStatus) Descriptor() ([]byte, []int) {
method GetCode (line 239) | func (x *EchoStatus) GetCode() int32 {
method GetMessage (line 246) | func (x *EchoStatus) GetMessage() string {
type SimpleRequest (line 254) | type SimpleRequest struct
method Reset (line 275) | func (x *SimpleRequest) Reset() {
method String (line 284) | func (x *SimpleRequest) String() string {
method ProtoMessage (line 288) | func (*SimpleRequest) ProtoMessage() {}
method ProtoReflect (line 290) | func (x *SimpleRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 303) | func (*SimpleRequest) Descriptor() ([]byte, []int) {
method GetResponseType (line 307) | func (x *SimpleRequest) GetResponseType() PayloadType {
method GetResponseSize (line 314) | func (x *SimpleRequest) GetResponseSize() int32 {
method GetPayload (line 321) | func (x *SimpleRequest) GetPayload() *Payload {
method GetFillUsername (line 328) | func (x *SimpleRequest) GetFillUsername() bool {
method GetFillOauthScope (line 335) | func (x *SimpleRequest) GetFillOauthScope() bool {
method GetResponseStatus (line 342) | func (x *SimpleRequest) GetResponseStatus() *EchoStatus {
type SimpleResponse (line 350) | type SimpleResponse struct
method Reset (line 364) | func (x *SimpleResponse) Reset() {
method String (line 373) | func (x *SimpleResponse) String() string {
method ProtoMessage (line 377) | func (*SimpleResponse) ProtoMessage() {}
method ProtoReflect (line 379) | func (x *SimpleResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 392) | func (*SimpleResponse) Descriptor() ([]byte, []int) {
method GetPayload (line 396) | func (x *SimpleResponse) GetPayload() *Payload {
method GetUsername (line 403) | func (x *SimpleResponse) GetUsername() string {
method GetOauthScope (line 410) | func (x *SimpleResponse) GetOauthScope() string {
type StreamingInputCallRequest (line 418) | type StreamingInputCallRequest struct
method Reset (line 427) | func (x *StreamingInputCallRequest) Reset() {
method String (line 436) | func (x *StreamingInputCallRequest) String() string {
method ProtoMessage (line 440) | func (*StreamingInputCallRequest) ProtoMessage() {}
method ProtoReflect (line 442) | func (x *StreamingInputCallRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 455) | func (*StreamingInputCallRequest) Descriptor() ([]byte, []int) {
method GetPayload (line 459) | func (x *StreamingInputCallRequest) GetPayload() *Payload {
type StreamingInputCallResponse (line 467) | type StreamingInputCallResponse struct
method Reset (line 476) | func (x *StreamingInputCallResponse) Reset() {
method String (line 485) | func (x *StreamingInputCallResponse) String() string {
method ProtoMessage (line 489) | func (*StreamingInputCallResponse) ProtoMessage() {}
method ProtoReflect (line 491) | func (x *StreamingInputCallResponse) ProtoReflect() protoreflect.Messa...
method Descriptor (line 504) | func (*StreamingInputCallResponse) Descriptor() ([]byte, []int) {
method GetAggregatedPayloadSize (line 508) | func (x *StreamingInputCallResponse) GetAggregatedPayloadSize() int32 {
type ResponseParameters (line 516) | type ResponseParameters struct
method Reset (line 529) | func (x *ResponseParameters) Reset() {
method String (line 538) | func (x *ResponseParameters) String() string {
method ProtoMessage (line 542) | func (*ResponseParameters) ProtoMessage() {}
method ProtoReflect (line 544) | func (x *ResponseParameters) ProtoReflect() protoreflect.Message {
method Descriptor (line 557) | func (*ResponseParameters) Descriptor() ([]byte, []int) {
method GetSize (line 561) | func (x *ResponseParameters) GetSize() int32 {
method GetIntervalUs (line 568) | func (x *ResponseParameters) GetIntervalUs() int32 {
type StreamingOutputCallRequest (line 576) | type StreamingOutputCallRequest struct
method Reset (line 594) | func (x *StreamingOutputCallRequest) Reset() {
method String (line 603) | func (x *StreamingOutputCallRequest) String() string {
method ProtoMessage (line 607) | func (*StreamingOutputCallRequest) ProtoMessage() {}
method ProtoReflect (line 609) | func (x *StreamingOutputCallRequest) ProtoReflect() protoreflect.Messa...
method Descriptor (line 622) | func (*StreamingOutputCallRequest) Descriptor() ([]byte, []int) {
method GetResponseType (line 626) | func (x *StreamingOutputCallRequest) GetResponseType() PayloadType {
method GetResponseParameters (line 633) | func (x *StreamingOutputCallRequest) GetResponseParameters() []*Respon...
method GetPayload (line 640) | func (x *StreamingOutputCallRequest) GetPayload() *Payload {
method GetResponseStatus (line 647) | func (x *StreamingOutputCallRequest) GetResponseStatus() *EchoStatus {
type StreamingOutputCallResponse (line 655) | type StreamingOutputCallResponse struct
method Reset (line 664) | func (x *StreamingOutputCallResponse) Reset() {
method String (line 673) | func (x *StreamingOutputCallResponse) String() string {
method ProtoMessage (line 677) | func (*StreamingOutputCallResponse) ProtoMessage() {}
method ProtoReflect (line 679) | func (x *StreamingOutputCallResponse) ProtoReflect() protoreflect.Mess...
method Descriptor (line 692) | func (*StreamingOutputCallResponse) Descriptor() ([]byte, []int) {
method GetPayload (line 696) | func (x *StreamingOutputCallResponse) GetPayload() *Payload {
function file_test_proto_rawDescGZIP (line 829) | func file_test_proto_rawDescGZIP() []byte {
function init (line 884) | func init() { file_test_proto_init() }
function file_test_proto_init (line 885) | func file_test_proto_init() {
FILE: internal/testing/test_grpc.pb.go
constant _ (line 15) | _ = grpc.SupportPackageIsVersion7
type TestServiceClient (line 20) | type TestServiceClient interface
type testServiceClient (line 43) | type testServiceClient struct
method EmptyCall (line 51) | func (c *testServiceClient) EmptyCall(ctx context.Context, in *Empty, ...
method UnaryCall (line 60) | func (c *testServiceClient) UnaryCall(ctx context.Context, in *SimpleR...
method StreamingOutputCall (line 69) | func (c *testServiceClient) StreamingOutputCall(ctx context.Context, i...
method StreamingInputCall (line 101) | func (c *testServiceClient) StreamingInputCall(ctx context.Context, op...
method FullDuplexCall (line 135) | func (c *testServiceClient) FullDuplexCall(ctx context.Context, opts ....
method HalfDuplexCall (line 166) | func (c *testServiceClient) HalfDuplexCall(ctx context.Context, opts ....
function NewTestServiceClient (line 47) | func NewTestServiceClient(cc grpc.ClientConnInterface) TestServiceClient {
type TestService_StreamingOutputCallClient (line 84) | type TestService_StreamingOutputCallClient interface
type testServiceStreamingOutputCallClient (line 89) | type testServiceStreamingOutputCallClient struct
method Recv (line 93) | func (x *testServiceStreamingOutputCallClient) Recv() (*StreamingOutpu...
type TestService_StreamingInputCallClient (line 110) | type TestService_StreamingInputCallClient interface
type testServiceStreamingInputCallClient (line 116) | type testServiceStreamingInputCallClient struct
method Send (line 120) | func (x *testServiceStreamingInputCallClient) Send(m *StreamingInputCa...
method CloseAndRecv (line 124) | func (x *testServiceStreamingInputCallClient) CloseAndRecv() (*Streami...
type TestService_FullDuplexCallClient (line 144) | type TestService_FullDuplexCallClient interface
type testServiceFullDuplexCallClient (line 150) | type testServiceFullDuplexCallClient struct
method Send (line 154) | func (x *testServiceFullDuplexCallClient) Send(m *StreamingOutputCallR...
method Recv (line 158) | func (x *testServiceFullDuplexCallClient) Recv() (*StreamingOutputCall...
type TestService_HalfDuplexCallClient (line 175) | type TestService_HalfDuplexCallClient interface
type testServiceHalfDuplexCallClient (line 181) | type testServiceHalfDuplexCallClient struct
method Send (line 185) | func (x *testServiceHalfDuplexCallClient) Send(m *StreamingOutputCallR...
method Recv (line 189) | func (x *testServiceHalfDuplexCallClient) Recv() (*StreamingOutputCall...
type TestServiceServer (line 200) | type TestServiceServer interface
type UnimplementedTestServiceServer (line 225) | type UnimplementedTestServiceServer struct
method EmptyCall (line 228) | func (UnimplementedTestServiceServer) EmptyCall(context.Context, *Empt...
method UnaryCall (line 231) | func (UnimplementedTestServiceServer) UnaryCall(context.Context, *Simp...
method StreamingOutputCall (line 234) | func (UnimplementedTestServiceServer) StreamingOutputCall(*StreamingOu...
method StreamingInputCall (line 237) | func (UnimplementedTestServiceServer) StreamingInputCall(TestService_S...
method FullDuplexCall (line 240) | func (UnimplementedTestServiceServer) FullDuplexCall(TestService_FullD...
method HalfDuplexCall (line 243) | func (UnimplementedTestServiceServer) HalfDuplexCall(TestService_HalfD...
method mustEmbedUnimplementedTestServiceServer (line 246) | func (UnimplementedTestServiceServer) mustEmbedUnimplementedTestServic...
type UnsafeTestServiceServer (line 251) | type UnsafeTestServiceServer interface
function RegisterTestServiceServer (line 255) | func RegisterTestServiceServer(s grpc.ServiceRegistrar, srv TestServiceS...
function _TestService_EmptyCall_Handler (line 259) | func _TestService_EmptyCall_Handler(srv interface{}, ctx context.Context...
function _TestService_UnaryCall_Handler (line 277) | func _TestService_UnaryCall_Handler(srv interface{}, ctx context.Context...
function _TestService_StreamingOutputCall_Handler (line 295) | func _TestService_StreamingOutputCall_Handler(srv interface{}, stream gr...
type TestService_StreamingOutputCallServer (line 303) | type TestService_StreamingOutputCallServer interface
type testServiceStreamingOutputCallServer (line 308) | type testServiceStreamingOutputCallServer struct
method Send (line 312) | func (x *testServiceStreamingOutputCallServer) Send(m *StreamingOutput...
function _TestService_StreamingInputCall_Handler (line 316) | func _TestService_StreamingInputCall_Handler(srv interface{}, stream grp...
type TestService_StreamingInputCallServer (line 320) | type TestService_StreamingInputCallServer interface
type testServiceStreamingInputCallServer (line 326) | type testServiceStreamingInputCallServer struct
method SendAndClose (line 330) | func (x *testServiceStreamingInputCallServer) SendAndClose(m *Streamin...
method Recv (line 334) | func (x *testServiceStreamingInputCallServer) Recv() (*StreamingInputC...
function _TestService_FullDuplexCall_Handler (line 342) | func _TestService_FullDuplexCall_Handler(srv interface{}, stream grpc.Se...
type TestService_FullDuplexCallServer (line 346) | type TestService_FullDuplexCallServer interface
type testServiceFullDuplexCallServer (line 352) | type testServiceFullDuplexCallServer struct
method Send (line 356) | func (x *testServiceFullDuplexCallServer) Send(m *StreamingOutputCallR...
method Recv (line 360) | func (x *testServiceFullDuplexCallServer) Recv() (*StreamingOutputCall...
function _TestService_HalfDuplexCall_Handler (line 368) | func _TestService_HalfDuplexCall_Handler(srv interface{}, stream grpc.Se...
type TestService_HalfDuplexCallServer (line 372) | type TestService_HalfDuplexCallServer interface
type testServiceHalfDuplexCallServer (line 378) | type testServiceHalfDuplexCallServer struct
method Send (line 382) | func (x *testServiceHalfDuplexCallServer) Send(m *StreamingOutputCallR...
method Recv (line 386) | func (x *testServiceHalfDuplexCallServer) Recv() (*StreamingOutputCall...
type UnimplementedServiceClient (line 440) | type UnimplementedServiceClient interface
type unimplementedServiceClient (line 445) | type unimplementedServiceClient struct
method UnimplementedCall (line 453) | func (c *unimplementedServiceClient) UnimplementedCall(ctx context.Con...
function NewUnimplementedServiceClient (line 449) | func NewUnimplementedServiceClient(cc grpc.ClientConnInterface) Unimplem...
type UnimplementedServiceServer (line 465) | type UnimplementedServiceServer interface
type UnimplementedUnimplementedServiceServer (line 472) | type UnimplementedUnimplementedServiceServer struct
method UnimplementedCall (line 475) | func (UnimplementedUnimplementedServiceServer) UnimplementedCall(conte...
method mustEmbedUnimplementedUnimplementedServiceServer (line 478) | func (UnimplementedUnimplementedServiceServer) mustEmbedUnimplementedU...
type UnsafeUnimplementedServiceServer (line 483) | type UnsafeUnimplementedServiceServer interface
function RegisterUnimplementedServiceServer (line 487) | func RegisterUnimplementedServiceServer(s grpc.ServiceRegistrar, srv Uni...
function _UnimplementedService_UnimplementedCall_Handler (line 491) | func _UnimplementedService_UnimplementedCall_Handler(srv interface{}, ct...
FILE: internal/testing/test_server.go
type TestServer (line 22) | type TestServer struct
method EmptyCall (line 27) | func (TestServer) EmptyCall(ctx context.Context, req *Empty) (*Empty, ...
method UnaryCall (line 43) | func (TestServer) UnaryCall(ctx context.Context, req *SimpleRequest) (...
method StreamingOutputCall (line 62) | func (TestServer) StreamingOutputCall(req *StreamingOutputCallRequest,...
method StreamingInputCall (line 100) | func (TestServer) StreamingInputCall(str TestService_StreamingInputCal...
method FullDuplexCall (line 135) | func (TestServer) FullDuplexCall(str TestService_FullDuplexCallServer)...
method HalfDuplexCall (line 178) | func (TestServer) HalfDuplexCall(str TestService_HalfDuplexCallServer)...
constant MetadataReplyHeaders (line 219) | MetadataReplyHeaders = "reply-with-headers"
constant MetadataReplyTrailers (line 223) | MetadataReplyTrailers = "reply-with-trailers"
constant MetadataFailEarly (line 226) | MetadataFailEarly = "fail-early"
constant MetadataFailLate (line 234) | MetadataFailLate = "fail-late"
function processMetadata (line 237) | func processMetadata(ctx context.Context) (metadata.MD, metadata.MD, cod...
function toCode (line 248) | func toCode(vals []string) codes.Code {
FILE: invoke.go
type InvocationEventHandler (line 27) | type InvocationEventHandler interface
type RequestMessageSupplier (line 46) | type RequestMessageSupplier
function InvokeRpc (line 53) | func InvokeRpc(ctx context.Context, source DescriptorSource, cc *grpc.Cl...
type RequestSupplier (line 71) | type RequestSupplier
function InvokeRPC (line 87) | func InvokeRPC(ctx context.Context, source DescriptorSource, ch grpcdyna...
function invokeUnary (line 153) | func invokeUnary(ctx context.Context, stub grpcdynamic.Stub, md *desc.Me...
function invokeClientStream (line 193) | func invokeClientStream(ctx context.Context, stub grpcdynamic.Stub, md *...
function invokeServerStream (line 247) | func invokeServerStream(ctx context.Context, stub grpcdynamic.Stub, md *...
function invokeBidi (line 300) | func invokeBidi(ctx context.Context, stub grpcdynamic.Stub, md *desc.Met...
type notFoundError (line 382) | type notFoundError
method Error (line 388) | func (e notFoundError) Error() string {
function notFound (line 384) | func notFound(kind, name string) error {
function isNotFoundError (line 392) | func isNotFoundError(err error) bool {
function parseSymbol (line 400) | func parseSymbol(svcAndMethod string) (string, string) {
FILE: tls_settings_test.go
function TestPlainText (line 18) | func TestPlainText(t *testing.T) {
function TestBasicTLS (line 28) | func TestBasicTLS(t *testing.T) {
function TestInsecureClientTLS (line 47) | func TestInsecureClientTLS(t *testing.T) {
function TestClientCertTLS (line 66) | func TestClientCertTLS(t *testing.T) {
function TestRequireClientCertTLS (line 85) | func TestRequireClientCertTLS(t *testing.T) {
function TestBrokenTLS_ClientPlainText (line 104) | func TestBrokenTLS_ClientPlainText(t *testing.T) {
function TestBrokenTLS_ServerPlainText (line 165) | func TestBrokenTLS_ServerPlainText(t *testing.T) {
function TestBrokenTLS_ServerUsesWrongCert (line 181) | func TestBrokenTLS_ServerUsesWrongCert(t *testing.T) {
function TestBrokenTLS_ClientHasExpiredCert (line 201) | func TestBrokenTLS_ClientHasExpiredCert(t *testing.T) {
function TestBrokenTLS_ServerHasExpiredCert (line 221) | func TestBrokenTLS_ServerHasExpiredCert(t *testing.T) {
function TestBrokenTLS_ClientNotTrusted (line 241) | func TestBrokenTLS_ClientNotTrusted(t *testing.T) {
function TestBrokenTLS_ServerNotTrusted (line 265) | func TestBrokenTLS_ServerNotTrusted(t *testing.T) {
function TestBrokenTLS_RequireClientCertButNonePresented (line 285) | func TestBrokenTLS_RequireClientCertButNonePresented(t *testing.T) {
function simpleTest (line 309) | func simpleTest(t *testing.T, cc *grpc.ClientConn) {
function createTestServerAndClient (line 319) | func createTestServerAndClient(serverCreds, clientCreds credentials.Tran...
type testEnv (line 355) | type testEnv struct
method Close (line 360) | func (e *testEnv) Close() {
Condensed preview — 75 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (489K chars).
[
{
"path": ".circleci/config.yml",
"chars": 816,
"preview": "shared_configs:\n simple_job_steps: &simple_job_steps\n - checkout\n - run:\n name: Run tests\n command:"
},
{
"path": ".github/dependabot.yml",
"chars": 145,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"gomod\"\n directory: \"/\"\n # Check for updates once a week\n schedule:\n"
},
{
"path": ".github/workflows/codesee-arch-diagram.yml",
"chars": 503,
"preview": "# This workflow was added by CodeSee. Learn more at https://codesee.io/\n# This is v2.0 of this workflow file\non:\n push:"
},
{
"path": ".gitignore",
"chars": 34,
"preview": "dist/\n.idea/\nVERSION\n.tmp/\n*.snap\n"
},
{
"path": ".goreleaser.yml",
"chars": 1447,
"preview": "builds:\n - binary: grpcurl\n main: ./cmd/grpcurl\n goos:\n - linux\n - darwin\n - windows\n goarch:\n "
},
{
"path": "Dockerfile",
"chars": 1144,
"preview": "FROM golang:1.25-alpine AS builder\nLABEL maintainer=\"Fullstory Engineering\"\n\n# create non-privileged group and user\nRUN "
},
{
"path": "LICENSE",
"chars": 1081,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2017 Fullstory, Inc\n\nPermission is hereby granted, free of charge, to any person ob"
},
{
"path": "Makefile",
"chars": 2739,
"preview": "dev_build_version=$(shell git describe --tags --always --dirty)\n\nexport PATH := $(shell pwd)/.tmp/protoc/bin:$(PATH)\nexp"
},
{
"path": "README.md",
"chars": 11934,
"preview": "# gRPCurl\n[](https://circleci.com"
},
{
"path": "cmd/grpcurl/grpcurl.go",
"chars": 32723,
"preview": "// Command grpcurl makes gRPC requests (a la cURL, but HTTP/2). It can use a supplied descriptor\n// file, protobuf sourc"
},
{
"path": "cmd/grpcurl/indent_test.go",
"chars": 1213,
"preview": "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() an"
},
{
"path": "cmd/grpcurl/unix.go",
"chars": 375,
"preview": "//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows\n// +build darwin dragonfly"
},
{
"path": "desc_source.go",
"chars": 12894,
"preview": "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"
},
{
"path": "desc_source_test.go",
"chars": 1908,
"preview": "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"
},
{
"path": "download_protoc.sh",
"chars": 1076,
"preview": "#!/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"
},
{
"path": "format.go",
"chars": 18237,
"preview": "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\"syn"
},
{
"path": "format_test.go",
"chars": 7606,
"preview": "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:igno"
},
{
"path": "go.mod",
"chars": 1268,
"preview": "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\tgit"
},
{
"path": "go.sum",
"chars": 5244,
"preview": "cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w=\ncel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbA"
},
{
"path": "grpcurl.go",
"chars": 24160,
"preview": "// Package grpcurl provides the core functionality exposed by the grpcurl command, for\n// dynamically connecting to a se"
},
{
"path": "grpcurl_test.go",
"chars": 29434,
"preview": "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\""
},
{
"path": "internal/testing/cmd/bankdemo/README.md",
"chars": 910,
"preview": "# bankdemo\n\nThe `bankdemo` program is an example gRPC server that was used to demo `grpcurl` at Gophercon 2018.\n\nIt demo"
},
{
"path": "internal/testing/cmd/bankdemo/auth.go",
"chars": 1074,
"preview": "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"
},
{
"path": "internal/testing/cmd/bankdemo/bank.go",
"chars": 7211,
"preview": "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/co"
},
{
"path": "internal/testing/cmd/bankdemo/bank.pb.go",
"chars": 47468,
"preview": "// 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"
},
{
"path": "internal/testing/cmd/bankdemo/bank.proto",
"chars": 3687,
"preview": "syntax = \"proto3\";\n\noption go_package = \".;main\";\n\nimport \"google/protobuf/empty.proto\";\nimport \"google/protobuf/timesta"
},
{
"path": "internal/testing/cmd/bankdemo/bank_grpc.pb.go",
"chars": 13689,
"preview": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage main\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.or"
},
{
"path": "internal/testing/cmd/bankdemo/chat.go",
"chars": 10039,
"preview": "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\""
},
{
"path": "internal/testing/cmd/bankdemo/db.go",
"chars": 4895,
"preview": "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."
},
{
"path": "internal/testing/cmd/bankdemo/main.go",
"chars": 4107,
"preview": "package main\n\n//go:generate protoc --go_out=. --go-grpc_out=. bank.proto support.proto\n\nimport (\n\t\"context\"\n\t\"encoding/j"
},
{
"path": "internal/testing/cmd/bankdemo/support.pb.go",
"chars": 33702,
"preview": "// 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"
},
{
"path": "internal/testing/cmd/bankdemo/support.proto",
"chars": 4435,
"preview": "syntax = \"proto3\";\n\noption go_package = \".;main\";\n\nimport \"google/protobuf/timestamp.proto\";\n\n// Support provides an int"
},
{
"path": "internal/testing/cmd/bankdemo/support_grpc.pb.go",
"chars": 6960,
"preview": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage main\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.or"
},
{
"path": "internal/testing/cmd/testserver/README.md",
"chars": 741,
"preview": "# testserver\n\nThe `testserver` program is a simple server that can be used for testing RPC clients such\nas `grpcurl`. It"
},
{
"path": "internal/testing/cmd/testserver/testserver.go",
"chars": 4959,
"preview": "// 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/"
},
{
"path": "internal/testing/cmd/testserver/unix.go",
"chars": 431,
"preview": "//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris\n// +build darwin dragonfly freebsd li"
},
{
"path": "internal/testing/example.proto",
"chars": 547,
"preview": "syntax = \"proto3\";\n\nimport \"google/protobuf/descriptor.proto\";\nimport \"google/protobuf/empty.proto\";\nimport \"google/prot"
},
{
"path": "internal/testing/example2.proto",
"chars": 132,
"preview": "syntax = \"proto3\";\n\nimport \"google/protobuf/any.proto\";\n\nmessage Extension {\n uint64 id = 1;\n google.protobuf.Any "
},
{
"path": "internal/testing/jsonpb_test_proto/test_objects.pb.go",
"chars": 14372,
"preview": "// 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"
},
{
"path": "internal/testing/jsonpb_test_proto/test_objects.proto",
"chars": 985,
"preview": "syntax = \"proto2\";\n\nimport \"google/protobuf/any.proto\";\nimport \"google/protobuf/duration.proto\";\nimport \"google/protobuf"
},
{
"path": "internal/testing/test.pb.go",
"chars": 37997,
"preview": "// NB: Copied from the gRPC Go repo: google.golang.org/grpc/interop/grpc_testing/test.proto\n\n// Copyright 2017 gRPC auth"
},
{
"path": "internal/testing/test.proto",
"chars": 5860,
"preview": "// NB: Copied from the gRPC Go repo: google.golang.org/grpc/interop/grpc_testing/test.proto\n\n// Copyright 2017 gRPC auth"
},
{
"path": "internal/testing/test_grpc.pb.go",
"chars": 18965,
"preview": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage testing\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang"
},
{
"path": "internal/testing/test_server.go",
"chars": 7626,
"preview": "package testing\n\n//go:generate protoc --go_out=. --go-grpc_out=. test.proto\n//go:generate protoc --descriptor_set_out=./"
},
{
"path": "internal/testing/tls/ca.crl",
"chars": 918,
"preview": "-----BEGIN X509 CRL-----\nMIICfDBmAgEBMA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNVBAMTAmNhFw0xNzA4MjUx\nNTQ1NTNaFw0yNzA4MjUxNTQ1NTNaMA"
},
{
"path": "internal/testing/tls/ca.crt",
"chars": 1744,
"preview": "-----BEGIN CERTIFICATE-----\nMIIE2jCCAsKgAwIBAgIBATANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJjYTAe\nFw0xNzA4MjUxNTQ1NTJaFw0yNzA"
},
{
"path": "internal/testing/tls/ca.key",
"chars": 3243,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKAIBAAKCAgEAnjw7iZyn9EtjtK7zT+M59OxSJ43a3kMm11Vnh1Fw8oQ7tH0k\nQW6COyAwlBhAzWGtfDC6jG7"
},
{
"path": "internal/testing/tls/client.crt",
"chars": 1484,
"preview": "-----BEGIN CERTIFICATE-----\nMIIEGjCCAgKgAwIBAgIRAPt0KCF12GYbCoUj7klj5/AwDQYJKoZIhvcNAQELBQAw\nDTELMAkGA1UEAxMCY2EwHhcNMTc"
},
{
"path": "internal/testing/tls/client.csr",
"chars": 887,
"preview": "-----BEGIN CERTIFICATE REQUEST-----\nMIICVjCCAT4CAQAwETEPMA0GA1UEAxMGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQE"
},
{
"path": "internal/testing/tls/client.key",
"chars": 1679,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAtScRDzrGcn/PPN7ZTwRGo6LchAra2DGePBbGBJkNV7yj6B93\nm+/hL+7UQVQLqcgfO10OT1P"
},
{
"path": "internal/testing/tls/expired.crt",
"chars": 1525,
"preview": "-----BEGIN CERTIFICATE-----\nMIIEODCCAiCgAwIBAgIQP6vqM4GEKFdwrZvr/s/5KTANBgkqhkiG9w0BAQsFADAN\nMQswCQYDVQQDEwJjYTAeFw0xNzA"
},
{
"path": "internal/testing/tls/expired.csr",
"chars": 948,
"preview": "-----BEGIN CERTIFICATE REQUEST-----\nMIIChDCCAWwCAQAwEjEQMA4GA1UEAxMHZXhwaXJlZDCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggE"
},
{
"path": "internal/testing/tls/expired.key",
"chars": 1675,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAw5RrtPGHuyBjJ4es8olW1uuG7U9gVH4+H2Zck62izc1T31Cz\nl3s4CUQMAFxJ2HmAOY+nu4g"
},
{
"path": "internal/testing/tls/other.crt",
"chars": 1525,
"preview": "-----BEGIN CERTIFICATE-----\nMIIEODCCAiCgAwIBAgIRAMcIOg3oRf7n7mR1BUqNnlcwDQYJKoZIhvcNAQELBQAw\nDTELMAkGA1UEAxMCY2EwHhcNMTc"
},
{
"path": "internal/testing/tls/other.csr",
"chars": 948,
"preview": "-----BEGIN CERTIFICATE REQUEST-----\nMIICgzCCAWsCAQAwEDEOMAwGA1UEAxMFb3RoZXIwggEiMA0GCSqGSIb3DQEBAQUA\nA4IBDwAwggEKAoIBAQD"
},
{
"path": "internal/testing/tls/other.key",
"chars": 1679,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAziU9ccrphXsYUx5KwcpWrfcsOfxm3QiRjmkY0K66Sw9re9gE\nVGTBOuuhspAOebCMt5pAhLL"
},
{
"path": "internal/testing/tls/server.crt",
"chars": 1521,
"preview": "-----BEGIN CERTIFICATE-----\nMIIENzCCAh+gAwIBAgIQSCdvhIY3KZ9w9YRcDH1oRzANBgkqhkiG9w0BAQsFADAN\nMQswCQYDVQQDEwJjYTAeFw0xNzA"
},
{
"path": "internal/testing/tls/server.csr",
"chars": 948,
"preview": "-----BEGIN CERTIFICATE REQUEST-----\nMIICgzCCAWsCAQAwETEPMA0GA1UEAxMGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQE"
},
{
"path": "internal/testing/tls/server.key",
"chars": 1679,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA2VUyEdkeNFaIaqak6x7UplOZ1RqJKCXdPzz4yJ7XtRuIuaw5\n+eR3c+4ScqVdFcyyo6DXmT3"
},
{
"path": "internal/testing/tls/wrong-ca.crl",
"chars": 926,
"preview": "-----BEGIN X509 CRL-----\nMIICgjBsAgEBMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMTCHdyb25nLWNhFw0x\nNzA4MjUxNTQ1NTdaFw0yNzA4MjUxNT"
},
{
"path": "internal/testing/tls/wrong-ca.crt",
"chars": 1761,
"preview": "-----BEGIN CERTIFICATE-----\nMIIE5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwh3cm9u\nZy1jYTAeFw0xNzA4MjUxNTQ1NTR"
},
{
"path": "internal/testing/tls/wrong-ca.key",
"chars": 3243,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEAzs91DHKfenUfIPigN6n4CsTSHGMkRpWQw9T5vIqUd3ZgMJLC\noEnj0OpVlFVcb9+P2/f5RYg"
},
{
"path": "internal/testing/tls/wrong-client.crt",
"chars": 1497,
"preview": "-----BEGIN CERTIFICATE-----\nMIIEJTCCAg2gAwIBAgIQINVcdjlrWhsKxwcXL4vbUDANBgkqhkiG9w0BAQsFADAT\nMREwDwYDVQQDEwh3cm9uZy1jYTA"
},
{
"path": "internal/testing/tls/wrong-client.csr",
"chars": 895,
"preview": "-----BEGIN CERTIFICATE REQUEST-----\nMIICXDCCAUQCAQAwFzEVMBMGA1UEAxMMd3JvbmctY2xpZW50MIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMII"
},
{
"path": "internal/testing/tls/wrong-client.key",
"chars": 1675,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA0cBd/JPy+40pzcV551kffzrlsXk8ttGzZHwJpQ51CP8dMznF\nKxvlOvfHoiRYr7lLC2cZ1ot"
},
{
"path": "invoke.go",
"chars": 13113,
"preview": "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/protob"
},
{
"path": "mk-test-files.sh",
"chars": 1445,
"preview": "#!/bin/bash\n\nset -e\n\ncd \"$(dirname $0)\"\n\n# Run this script to generate files used by tests.\n\necho \"Creating protosets..."
},
{
"path": "releasing/README.md",
"chars": 4452,
"preview": "# Releases of gRPCurl\n\nThis document provides instructions for building a release of `grpcurl`.\n\nThe release process con"
},
{
"path": "releasing/RELEASE_NOTES.md",
"chars": 751,
"preview": "## Changes\n\n### Command-line tool\n\n* _In this list, describe the changes to the command-line tool._\n* _Use one bullet pe"
},
{
"path": "releasing/do-release.sh",
"chars": 2293,
"preview": "#!/bin/bash\n\n# strict mode\nset -euo pipefail\nIFS=$'\\n\\t'\n\nif [[ -z ${DRY_RUN:-} ]]; then\n PREFIX=\"\"\nelse\n PREFIX=\""
},
{
"path": "snap/README.md",
"chars": 719,
"preview": "# packing and releasing\nTo pack the current branch to a snap package:\n\n`snapcraft pack`\n\nTo install the package locally:"
},
{
"path": "snap/snapcraft.yaml",
"chars": 983,
"preview": "name: grpcurl\nbase: core24\n# allow grpcurl part to call craftctl set-version\nadopt-info: grpcurl\nsummary: grpcurl is a c"
},
{
"path": "tls_settings_test.go",
"chars": 12228,
"preview": "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\"googl"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the fullstorydev/grpcurl GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 75 files (444.5 KB), approximately 158.8k tokens, and a symbol index with 796 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.