mirror of
https://github.com/fullstorydev/grpcurl.git
synced 2026-05-25 21:21:46 +03:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b65b3ee55 | ||
|
|
9ac7e3a34d | ||
|
|
76bbedeed0 | ||
|
|
b9d2d8cfa8 | ||
|
|
30b8cd1531 | ||
|
|
a8f79c8751 | ||
|
|
bf9b13d6c5 | ||
|
|
c3bde04cc1 | ||
|
|
2e9ba5024e | ||
|
|
127194b205 | ||
|
|
cd242fe1ed | ||
|
|
59a32e5eb0 | ||
|
|
7e1cd16164 | ||
|
|
de25c89822 | ||
|
|
f1d396c31e | ||
|
|
1986364acd | ||
|
|
bdf97bc934 | ||
|
|
2f55ac63a4 | ||
|
|
8d7770a962 | ||
|
|
1f34448998 | ||
|
|
06a970022e | ||
|
|
db90ec1160 | ||
|
|
9da71fbe53 | ||
|
|
9846afccbc | ||
|
|
ba5f667e13 | ||
|
|
54ffdcacda | ||
|
|
ceef817807 | ||
|
|
e544b9e66f | ||
|
|
41a6ac0479 | ||
|
|
f37ec641c5 | ||
|
|
8e51c5e2d3 | ||
|
|
8376c2f7bb | ||
|
|
0162fa9726 | ||
|
|
b8c67b7a4e | ||
|
|
ff114930fd | ||
|
|
5ad5edb29c | ||
|
|
44547153b3 | ||
|
|
2108c8f0b3 | ||
|
|
36008aa111 | ||
|
|
50833f1b21 | ||
|
|
939766fb42 | ||
|
|
b58182a88d | ||
|
|
8e2cf9b3c2 | ||
|
|
36f9e53dfd | ||
|
|
bfbbed1d42 | ||
|
|
153d36db8c | ||
|
|
2af40876fc | ||
|
|
0218a7db67 | ||
|
|
96cfd48e32 | ||
|
|
d30f3a01b7 | ||
|
|
0d669e78d0 | ||
|
|
9572bd4525 |
62
.circleci/config.yml
Normal file
62
.circleci/config.yml
Normal file
@@ -0,0 +1,62 @@
|
||||
shared_configs:
|
||||
simple_job_steps: &simple_job_steps
|
||||
- checkout
|
||||
- run:
|
||||
name: Run tests
|
||||
command: |
|
||||
make deps 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-14:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/golang:1.14
|
||||
steps: *simple_job_steps
|
||||
|
||||
build-1-15:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/golang:1.15
|
||||
steps: *simple_job_steps
|
||||
|
||||
build-1-16:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/golang:1.16
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-mod-v4-{{ checksum "go.sum" }}
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: go mod download
|
||||
- save_cache:
|
||||
key: go-mod-v4-{{ checksum "go.sum" }}
|
||||
paths:
|
||||
- "/go/pkg/mod"
|
||||
- run:
|
||||
name: Run tests
|
||||
command: |
|
||||
#mkdir -p /tmp/test-reports
|
||||
#gotestsum --junitfile /tmp/test-reports/unit-tests.xml
|
||||
make ci
|
||||
#- store_test_results:
|
||||
# path: /tmp/test-reports
|
||||
|
||||
build-1-17:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/golang:1.17
|
||||
steps: *simple_job_steps
|
||||
|
||||
workflows:
|
||||
pr-build-test:
|
||||
jobs:
|
||||
- build-1-14
|
||||
- build-1-15
|
||||
- build-1-16
|
||||
- build-1-17
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
dist/
|
||||
.idea/
|
||||
VERSION
|
||||
|
||||
@@ -8,17 +8,28 @@ builds:
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- arm64
|
||||
- s390x
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
- goos: darwin
|
||||
goarch: s390x
|
||||
- goos: windows
|
||||
goarch: s390x
|
||||
ldflags:
|
||||
- -s -w -X main.version=v{{.Version}}
|
||||
|
||||
archive:
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
replacements:
|
||||
amd64: x86_64
|
||||
386: x86_32
|
||||
darwin: osx
|
||||
files:
|
||||
- LICENSE
|
||||
archives:
|
||||
- format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
replacements:
|
||||
amd64: x86_64
|
||||
386: x86_32
|
||||
darwin: osx
|
||||
files:
|
||||
- LICENSE
|
||||
|
||||
21
.travis.yml
21
.travis.yml
@@ -1,21 +0,0 @@
|
||||
language: go
|
||||
sudo: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: "1.9"
|
||||
- go: "1.10"
|
||||
- go: "1.11"
|
||||
env:
|
||||
- GO111MODULE=off
|
||||
- VET=1
|
||||
- go: "1.11"
|
||||
env: GO111MODULE=on
|
||||
- go: "1.12"
|
||||
env: GO111MODULE=off
|
||||
- go: "1.12"
|
||||
env: GO111MODULE=on
|
||||
- go: tip
|
||||
|
||||
script:
|
||||
- if [[ "$VET" = 1 ]]; then make ci; else make deps test; fi
|
||||
@@ -1,10 +1,6 @@
|
||||
FROM golang:1.11.10-alpine as builder
|
||||
FROM golang:1.15-alpine as builder
|
||||
MAINTAINER FullStory Engineering
|
||||
|
||||
# currently, a module build requires gcc (so Go tool can build
|
||||
# module-aware versions of std library; it ships only w/ the
|
||||
# non-module versions)
|
||||
RUN apk update && apk add --no-cache ca-certificates git gcc g++ libc-dev
|
||||
# create non-privileged group and user
|
||||
RUN addgroup -S grpcurl && adduser -S grpcurl -G grpcurl
|
||||
|
||||
@@ -15,8 +11,6 @@ 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 GOOS=linux
|
||||
ENV GOARCH=amd64
|
||||
ENV GO111MODULE=on
|
||||
RUN go build -o /grpcurl \
|
||||
-ldflags "-w -extldflags \"-static\" -X \"main.version=$(cat VERSION)\"" \
|
||||
|
||||
20
Makefile
20
Makefile
@@ -22,7 +22,7 @@ install:
|
||||
|
||||
.PHONY: release
|
||||
release:
|
||||
@GO111MODULE=off go get github.com/goreleaser/goreleaser
|
||||
@go install github.com/goreleaser/goreleaser@v0.134.0
|
||||
goreleaser --rm-dist
|
||||
|
||||
.PHONY: docker
|
||||
@@ -42,39 +42,35 @@ checkgofmt:
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
# TODO: remove the ignored check; need it for now because it
|
||||
# is complaining about a deprecated comment added to grpc,
|
||||
# but it's not yet released. Once the new (non-deprecated)
|
||||
# API is included in a release, we can move to that new
|
||||
# version and fix the call site to no longer use deprecated
|
||||
# method.
|
||||
# This all works fine with Go modules, but without modules,
|
||||
# CI is just getting latest master for dependencies like grpc.
|
||||
.PHONY: staticcheck
|
||||
staticcheck:
|
||||
@go get honnef.co/go/tools/cmd/staticcheck
|
||||
@go install honnef.co/go/tools/cmd/staticcheck@v0.0.1-2020.1.4
|
||||
staticcheck ./...
|
||||
|
||||
.PHONY: ineffassign
|
||||
ineffassign:
|
||||
@go get github.com/gordonklaus/ineffassign
|
||||
@go install github.com/gordonklaus/ineffassign@7953dde2c7bf
|
||||
ineffassign .
|
||||
|
||||
.PHONY: predeclared
|
||||
predeclared:
|
||||
@go get github.com/nishanths/predeclared
|
||||
@go install github.com/nishanths/predeclared@86fad755b4d3
|
||||
predeclared .
|
||||
|
||||
# Intentionally omitted from CI, but target here for ad-hoc reports.
|
||||
.PHONY: golint
|
||||
golint:
|
||||
@go get golang.org/x/lint/golint
|
||||
# TODO: pin version
|
||||
@go install golang.org/x/lint/golint@latest
|
||||
golint -min_confidence 0.9 -set_exit_status ./...
|
||||
|
||||
# Intentionally omitted from CI, but target here for ad-hoc reports.
|
||||
.PHONY: errcheck
|
||||
errcheck:
|
||||
@go get github.com/kisielk/errcheck
|
||||
# TODO: pin version
|
||||
@go install github.com/kisielk/errcheck@latest
|
||||
errcheck ./...
|
||||
|
||||
.PHONY: test
|
||||
|
||||
60
README.md
60
README.md
@@ -1,5 +1,5 @@
|
||||
# gRPCurl
|
||||
[](https://travis-ci.org/fullstorydev/grpcurl/branches)
|
||||
[](https://circleci.com/gh/fullstorydev/grpcurl/tree/master)
|
||||
[](https://goreportcard.com/report/github.com/fullstorydev/grpcurl)
|
||||
|
||||
`grpcurl` is a command-line tool that lets you interact with gRPC servers. It's
|
||||
@@ -35,9 +35,9 @@ See also the [`grpcurl` talk at GopherCon 2018](https://www.youtube.com/watch?v=
|
||||
operate bi-directional streaming methods interactively by running `grpcurl` from an
|
||||
interactive terminal and using stdin as the request body!
|
||||
|
||||
`grpcurl` supports both plain-text and TLS servers and has numerous options for TLS
|
||||
configuration. It also supports mutual TLS, where the client is required to present a
|
||||
client certificate.
|
||||
`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
|
||||
@@ -49,32 +49,57 @@ files (containing compiled descriptors, produced by `protoc`) to `grpcurl`.
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
### From Source
|
||||
You can use the `go` tool to install `grpcurl`:
|
||||
### Docker
|
||||
|
||||
For platforms that support Docker, you can download an image that lets you run `grpcurl`:
|
||||
```shell
|
||||
go get github.com/fullstorydev/grpcurl
|
||||
go install github.com/fullstorydev/grpcurl/cmd/grpcurl
|
||||
# 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
|
||||
|
||||
### 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 this directory is already in your `$PATH`, then
|
||||
you should be good to go.
|
||||
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, you could have out-dated versions of `grpcurl`'s
|
||||
dependencies. You can update the dependencies by running `make updatedeps`. You can
|
||||
also use [`vgo`](https://github.com/golang/vgo) to install, which will use the right
|
||||
versions of dependencies. Or, if you are using Go 1.11, 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 in your `GOPATH`).
|
||||
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:
|
||||
@@ -91,6 +116,9 @@ that requires no client certs and supports server reflection is the simplest thi
|
||||
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
|
||||
|
||||
@@ -4,28 +4,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fullstorydev/grpcurl"
|
||||
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/jhump/protoreflect/grpcreflect"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/metadata"
|
||||
reflectpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
|
||||
"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"
|
||||
)
|
||||
|
||||
var version = "dev build <no version set>"
|
||||
// To avoid confusion between program error codes and the gRPC resonse
|
||||
// status codes 'Cancelled' and 'Unknown', 1 and 2 respectively,
|
||||
// the response status codes emitted use an offest of 64
|
||||
const statusCodeOffset = 64
|
||||
|
||||
const no_version = "dev build <no version set>"
|
||||
|
||||
var version = no_version
|
||||
|
||||
var (
|
||||
exit = os.Exit
|
||||
@@ -68,8 +83,15 @@ var (
|
||||
performed. This can be used to supply credentials/secrets without having
|
||||
to put them in command-line arguments.`))
|
||||
authority = flags.String("authority", "", prettify(`
|
||||
Value of :authority pseudo-header to be use with underlying HTTP/2
|
||||
requests. It defaults to the given address.`))
|
||||
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.`))
|
||||
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
|
||||
@@ -85,9 +107,16 @@ var (
|
||||
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
|
||||
unkown 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
|
||||
@@ -113,15 +142,23 @@ var (
|
||||
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.`))
|
||||
serverName = flags.String("servername", "", prettify(`
|
||||
Override server name when validating TLS certificate.`))
|
||||
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 requests to a server.`))
|
||||
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
|
||||
@@ -157,6 +194,12 @@ func init() {
|
||||
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.`))
|
||||
}
|
||||
|
||||
type multiString []string
|
||||
@@ -170,6 +213,49 @@ func (s *multiString) Set(value string) error {
|
||||
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
|
||||
}
|
||||
|
||||
func main() {
|
||||
flags.Usage = usage
|
||||
flags.Parse(os.Args[1:])
|
||||
@@ -208,7 +294,7 @@ func main() {
|
||||
fail(nil, "The -cert and -key arguments must be used together and both be present.")
|
||||
}
|
||||
if *format != "json" && *format != "text" {
|
||||
fail(nil, "The -format option must be 'json' or '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.")
|
||||
@@ -239,6 +325,14 @@ func main() {
|
||||
invoke = true
|
||||
}
|
||||
|
||||
verbosityLevel := 0
|
||||
if *verbose {
|
||||
verbosityLevel = 1
|
||||
}
|
||||
if *veryVerbose {
|
||||
verbosityLevel = 2
|
||||
}
|
||||
|
||||
var symbol string
|
||||
if invoke {
|
||||
if len(args) == 0 {
|
||||
@@ -277,11 +371,21 @@ func main() {
|
||||
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 := time.Duration(*maxTime * float64(time.Second))
|
||||
ctx, _ = context.WithTimeout(ctx, timeout)
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
dial := func() *grpc.ClientConn {
|
||||
@@ -302,22 +406,55 @@ func main() {
|
||||
if *maxMsgSz > 0 {
|
||||
opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*maxMsgSz)))
|
||||
}
|
||||
if *authority != "" {
|
||||
opts = append(opts, grpc.WithAuthority(*authority))
|
||||
}
|
||||
var creds credentials.TransportCredentials
|
||||
if !*plaintext {
|
||||
var err error
|
||||
creds, err = grpcurl.ClientTransportCredentials(*insecure, *cacert, *cert, *key)
|
||||
tlsConf, err := grpcurl.ClientTLSConfig(*insecure, *cacert, *cert, *key)
|
||||
if err != nil {
|
||||
fail(err, "Failed to configure transport credentials")
|
||||
fail(err, "Failed to create TLS config")
|
||||
}
|
||||
if *serverName != "" {
|
||||
if err := creds.OverrideServerName(*serverName); err != nil {
|
||||
fail(err, "Failed to override server name as %q", *serverName)
|
||||
|
||||
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 != "" {
|
||||
if err := creds.OverrideServerName(overrideName); err != nil {
|
||||
fail(err, "Failed to override server name as %q", overrideName)
|
||||
}
|
||||
}
|
||||
} else if *authority != "" {
|
||||
opts = append(opts, grpc.WithAuthority(*authority))
|
||||
}
|
||||
|
||||
grpcurlUA := "grpcurl/" + version
|
||||
if version == no_version {
|
||||
grpcurlUA = "grpcurl/dev-build (no version set)"
|
||||
}
|
||||
if *userAgent != "" {
|
||||
grpcurlUA = *userAgent + " " + grpcurlUA
|
||||
}
|
||||
opts = append(opts, grpc.WithUserAgent(grpcurlUA))
|
||||
|
||||
network := "tcp"
|
||||
if isUnixSocket != nil && isUnixSocket() {
|
||||
network = "unix"
|
||||
@@ -328,6 +465,13 @@ func main() {
|
||||
}
|
||||
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
|
||||
@@ -348,24 +492,33 @@ func main() {
|
||||
var cc *grpc.ClientConn
|
||||
var descSource grpcurl.DescriptorSource
|
||||
var refClient *grpcreflect.Client
|
||||
var fileSource grpcurl.DescriptorSource
|
||||
if len(protoset) > 0 {
|
||||
var err error
|
||||
descSource, err = grpcurl.DescriptorSourceFromProtoSets(protoset...)
|
||||
fileSource, err = grpcurl.DescriptorSourceFromProtoSets(protoset...)
|
||||
if err != nil {
|
||||
fail(err, "Failed to process proto descriptor sets.")
|
||||
}
|
||||
} else if len(protoFiles) > 0 {
|
||||
var err error
|
||||
descSource, err = grpcurl.DescriptorSourceFromProtoFiles(importPaths, protoFiles...)
|
||||
fileSource, err = grpcurl.DescriptorSourceFromProtoFiles(importPaths, protoFiles...)
|
||||
if err != nil {
|
||||
fail(err, "Failed to process proto source files.")
|
||||
}
|
||||
} else {
|
||||
}
|
||||
if reflection.val {
|
||||
md := grpcurl.MetadataFromHeaders(append(addlHeaders, reflHeaders...))
|
||||
refCtx := metadata.NewOutgoingContext(ctx, md)
|
||||
cc = dial()
|
||||
refClient = grpcreflect.NewClient(refCtx, reflectpb.NewServerReflectionClient(cc))
|
||||
descSource = grpcurl.DescriptorSourceFromServer(ctx, refClient)
|
||||
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
|
||||
@@ -463,7 +616,7 @@ func main() {
|
||||
} else {
|
||||
// see if it's a group
|
||||
for _, f := range parent.GetFields() {
|
||||
if f.GetType() == descpb.FieldDescriptorProto_TYPE_GROUP && f.GetMessageType() == d {
|
||||
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
|
||||
@@ -474,7 +627,7 @@ func main() {
|
||||
}
|
||||
case *desc.FieldDescriptor:
|
||||
elementType = "a field"
|
||||
if d.GetType() == descpb.FieldDescriptorProto_TYPE_GROUP {
|
||||
if d.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP {
|
||||
elementType = "a group field"
|
||||
} else if d.IsExtension() {
|
||||
elementType = "an extension"
|
||||
@@ -505,7 +658,8 @@ func main() {
|
||||
// for messages, also show a template in JSON, to make it easier to
|
||||
// create a request to invoke an RPC
|
||||
tmpl := grpcurl.MakeTemplate(dsc)
|
||||
_, formatter, err := grpcurl.RequestParserAndFormatterFor(grpcurl.Format(*format), descSource, true, false, nil)
|
||||
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)
|
||||
}
|
||||
@@ -536,16 +690,29 @@ func main() {
|
||||
// if not verbose output, then also include record delimiters
|
||||
// between each message, so output could potentially be piped
|
||||
// to another grpcurl process
|
||||
includeSeparators := !*verbose
|
||||
rf, formatter, err := grpcurl.RequestParserAndFormatterFor(grpcurl.Format(*format), descSource, *emitDefaults, includeSeparators, in)
|
||||
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.NewDefaultEventHandler(os.Stdout, descSource, formatter, *verbose)
|
||||
h := &grpcurl.DefaultEventHandler{
|
||||
Out: os.Stdout,
|
||||
Formatter: formatter,
|
||||
VerbosityLevel: verbosityLevel,
|
||||
}
|
||||
|
||||
err = grpcurl.InvokeRPC(ctx, descSource, cc, symbol, append(addlHeaders, rpcHeaders...), h, rf.Next)
|
||||
if err != nil {
|
||||
fail(err, "Error invoking method %q", symbol)
|
||||
if errStatus, ok := status.FromError(err); ok && *formatError {
|
||||
h.Status = errStatus
|
||||
} else {
|
||||
fail(err, "Error invoking method %q", symbol)
|
||||
}
|
||||
}
|
||||
reqSuffix := ""
|
||||
respSuffix := ""
|
||||
@@ -556,12 +723,16 @@ func main() {
|
||||
if h.NumResponses != 1 {
|
||||
respSuffix = "s"
|
||||
}
|
||||
if *verbose {
|
||||
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 {
|
||||
grpcurl.PrintStatus(os.Stderr, h.Status, formatter)
|
||||
exit(1)
|
||||
if *formatError {
|
||||
printFormattedStatus(os.Stderr, h.Status, formatter)
|
||||
} else {
|
||||
grpcurl.PrintStatus(os.Stderr, h.Status, formatter)
|
||||
}
|
||||
exit(statusCodeOffset + int(h.Status.Code()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -648,3 +819,28 @@ func writeProtoset(descSource grpcurl.DescriptorSource, symbols ...string) error
|
||||
defer f.Close()
|
||||
return grpcurl.WriteProtoset(f, 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
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
package grpcurl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/jhump/protoreflect/desc/protoparse"
|
||||
"github.com/jhump/protoreflect/dynamic"
|
||||
"github.com/jhump/protoreflect/grpcreflect"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
)
|
||||
|
||||
// ErrReflectionNotSupported is returned by DescriptorSource operations that
|
||||
@@ -39,13 +39,13 @@ type DescriptorSource interface {
|
||||
// DescriptorSourceFromProtoSets creates a DescriptorSource that is backed by the named files, whose contents
|
||||
// are encoded FileDescriptorSet protos.
|
||||
func DescriptorSourceFromProtoSets(fileNames ...string) (DescriptorSource, error) {
|
||||
files := &descpb.FileDescriptorSet{}
|
||||
files := &descriptorpb.FileDescriptorSet{}
|
||||
for _, fileName := range fileNames {
|
||||
b, err := ioutil.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load protoset file %q: %v", fileName, err)
|
||||
}
|
||||
var fs descpb.FileDescriptorSet
|
||||
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)
|
||||
@@ -76,8 +76,8 @@ func DescriptorSourceFromProtoFiles(importPaths []string, fileNames ...string) (
|
||||
}
|
||||
|
||||
// DescriptorSourceFromFileDescriptorSet creates a DescriptorSource that is backed by the FileDescriptorSet.
|
||||
func DescriptorSourceFromFileDescriptorSet(files *descpb.FileDescriptorSet) (DescriptorSource, error) {
|
||||
unresolved := map[string]*descpb.FileDescriptorProto{}
|
||||
func DescriptorSourceFromFileDescriptorSet(files *descriptorpb.FileDescriptorSet) (DescriptorSource, error) {
|
||||
unresolved := map[string]*descriptorpb.FileDescriptorProto{}
|
||||
for _, fd := range files.File {
|
||||
unresolved[fd.GetName()] = fd
|
||||
}
|
||||
@@ -91,7 +91,7 @@ func DescriptorSourceFromFileDescriptorSet(files *descpb.FileDescriptorSet) (Des
|
||||
return &fileSource{files: resolved}, nil
|
||||
}
|
||||
|
||||
func resolveFileDescriptor(unresolved map[string]*descpb.FileDescriptorProto, resolved map[string]*desc.FileDescriptor, filename string) (*desc.FileDescriptor, error) {
|
||||
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
|
||||
}
|
||||
@@ -275,12 +275,12 @@ func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string
|
||||
// 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([]*descpb.FileDescriptorProto, 0, 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(&descpb.FileDescriptorSet{File: allFilesSlice})
|
||||
b, err := proto.Marshal(&descriptorpb.FileDescriptorSet{File: allFilesSlice})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize file descriptor set: %v", err)
|
||||
}
|
||||
@@ -290,7 +290,7 @@ func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string
|
||||
return nil
|
||||
}
|
||||
|
||||
func addFilesToSet(allFiles []*descpb.FileDescriptorProto, expanded map[string]struct{}, fd *desc.FileDescriptor) []*descpb.FileDescriptorProto {
|
||||
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
|
||||
|
||||
@@ -5,21 +5,21 @@ import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||
"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("./testing/example.protoset")
|
||||
exampleProtoset, err := loadProtoset("./internal/testing/example.protoset")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load example.protoset: %v", err)
|
||||
}
|
||||
testProtoset, err := loadProtoset("./testing/test.protoset")
|
||||
testProtoset, err := loadProtoset("./internal/testing/test.protoset")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load test.protoset: %v", err)
|
||||
}
|
||||
|
||||
mergedProtoset := &descriptor.FileDescriptorSet{
|
||||
mergedProtoset := &descriptorpb.FileDescriptorSet{
|
||||
File: append(exampleProtoset.File, testProtoset.File...),
|
||||
}
|
||||
|
||||
@@ -29,29 +29,29 @@ func TestWriteProtoset(t *testing.T) {
|
||||
}
|
||||
|
||||
checkWriteProtoset(t, descSrc, exampleProtoset, "TestService")
|
||||
checkWriteProtoset(t, descSrc, testProtoset, "grpc.testing.TestService")
|
||||
checkWriteProtoset(t, descSrc, mergedProtoset, "TestService", "grpc.testing.TestService")
|
||||
checkWriteProtoset(t, descSrc, testProtoset, "testing.TestService")
|
||||
checkWriteProtoset(t, descSrc, mergedProtoset, "TestService", "testing.TestService")
|
||||
}
|
||||
|
||||
func loadProtoset(path string) (*descriptor.FileDescriptorSet, error) {
|
||||
func loadProtoset(path string) (*descriptorpb.FileDescriptorSet, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var protoset descriptor.FileDescriptorSet
|
||||
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 *descriptor.FileDescriptorSet, symbols ...string) {
|
||||
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 descriptor.FileDescriptorSet
|
||||
var result descriptorpb.FileDescriptorSet
|
||||
if err := proto.Unmarshal(buf.Bytes(), &result); err != nil {
|
||||
t.Fatalf("failed to unmarshal written protoset: %v", err)
|
||||
}
|
||||
|
||||
130
format.go
130
format.go
@@ -11,8 +11,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/jhump/protoreflect/dynamic"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -55,6 +55,15 @@ func NewJSONRequestParser(in io.Reader, resolver jsonpb.AnyResolver) RequestPars
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -69,7 +78,7 @@ func (f *jsonRequestParser) NumRequests() int {
|
||||
}
|
||||
|
||||
const (
|
||||
textSeparatorChar = 0x1e
|
||||
textSeparatorChar = '\x1e'
|
||||
)
|
||||
|
||||
type textRequestParser struct {
|
||||
@@ -179,7 +188,7 @@ func (tf *textFormatter) format(m proto.Message) (string, error) {
|
||||
|
||||
// no trailing newline needed
|
||||
str := buf.String()
|
||||
if str[len(str)-1] == '\n' {
|
||||
if len(str) > 0 && str[len(str)-1] == '\n' {
|
||||
str = str[:len(str)-1]
|
||||
}
|
||||
|
||||
@@ -296,6 +305,7 @@ func (r anyResolverWithFallback) Resolve(typeUrl string) (proto.Message, error)
|
||||
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
|
||||
@@ -342,31 +352,70 @@ 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) {
|
||||
switch format {
|
||||
case FormatJSON:
|
||||
resolver := AnyResolverFromDescriptorSource(descSource)
|
||||
return NewJSONRequestParser(in, resolver), NewJSONFormatter(emitJSONDefaultFields, anyResolverWithFallback{AnyResolver: resolver}), nil
|
||||
case FormatText:
|
||||
return NewTextRequestParser(in), NewTextFormatter(includeTextSeparator), nil
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("unknown format: %s", format)
|
||||
}
|
||||
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
|
||||
descSource DescriptorSource
|
||||
formatter func(proto.Message) (string, error)
|
||||
verbose bool
|
||||
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
|
||||
@@ -378,54 +427,65 @@ type DefaultEventHandler struct {
|
||||
// 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 compatability.
|
||||
// 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,
|
||||
descSource: descSource,
|
||||
formatter: formatter,
|
||||
verbose: verbose,
|
||||
Out: out,
|
||||
Formatter: formatter,
|
||||
VerbosityLevel: verbosityLevel,
|
||||
}
|
||||
}
|
||||
|
||||
var _ InvocationEventHandler = (*DefaultEventHandler)(nil)
|
||||
|
||||
func (h *DefaultEventHandler) OnResolveMethod(md *desc.MethodDescriptor) {
|
||||
if h.verbose {
|
||||
txt, err := GetDescriptorText(md, h.descSource)
|
||||
if h.VerbosityLevel > 0 {
|
||||
txt, err := GetDescriptorText(md, nil)
|
||||
if err == nil {
|
||||
fmt.Fprintf(h.out, "\nResolved method descriptor:\n%s\n", txt)
|
||||
fmt.Fprintf(h.Out, "\nResolved method descriptor:\n%s\n", txt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *DefaultEventHandler) OnSendHeaders(md metadata.MD) {
|
||||
if h.verbose {
|
||||
fmt.Fprintf(h.out, "\nRequest metadata to send:\n%s\n", MetadataToString(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.verbose {
|
||||
fmt.Fprintf(h.out, "\nResponse headers received:\n%s\n", MetadataToString(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.verbose {
|
||||
fmt.Fprint(h.out, "\nResponse contents:\n")
|
||||
if h.VerbosityLevel > 1 {
|
||||
fmt.Fprintf(h.Out, "\nEstimated response size: %d bytes\n", proto.Size(resp))
|
||||
}
|
||||
if respStr, err := h.formatter(resp); err != nil {
|
||||
fmt.Fprintf(h.out, "Failed to format response message %d: %v\n", h.NumResponses, err)
|
||||
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)
|
||||
fmt.Fprintln(h.Out, respStr)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *DefaultEventHandler) OnReceiveTrailers(stat *status.Status, md metadata.MD) {
|
||||
h.Status = stat
|
||||
if h.verbose {
|
||||
fmt.Fprintf(h.out, "\nResponse trailers received:\n%s\n", MetadataToString(md))
|
||||
if h.VerbosityLevel > 0 {
|
||||
fmt.Fprintf(h.Out, "\nResponse trailers received:\n%s\n", MetadataToString(md))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,15 +7,15 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes/struct"
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
func TestRequestParser(t *testing.T) {
|
||||
source, err := DescriptorSourceFromProtoSets("testing/example.protoset")
|
||||
source, err := DescriptorSourceFromProtoSets("internal/testing/example.protoset")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create descriptor source: %v", err)
|
||||
}
|
||||
@@ -69,7 +69,7 @@ func TestRequestParser(t *testing.T) {
|
||||
|
||||
for i, tc := range testCases {
|
||||
name := fmt.Sprintf("#%d, %s, %d message(s)", i+1, tc.format, len(tc.expectedOutput))
|
||||
rf, _, err := RequestParserAndFormatterFor(tc.format, source, false, false, strings.NewReader(tc.input))
|
||||
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
|
||||
@@ -97,7 +97,7 @@ func TestRequestParser(t *testing.T) {
|
||||
// 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("testing/example.protoset")
|
||||
source, err := DescriptorSourceFromProtoSets("internal/testing/example.protoset")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create descriptor source: %v", err)
|
||||
}
|
||||
@@ -120,20 +120,26 @@ func TestHandler(t *testing.T) {
|
||||
|
||||
for _, format := range []Format{FormatJSON, FormatText} {
|
||||
for _, numMessages := range []int{1, 3} {
|
||||
for _, verbose := range []bool{true, false} {
|
||||
for verbosityLevel := 0; verbosityLevel <= 2; verbosityLevel++ {
|
||||
name := fmt.Sprintf("%s, %d message(s)", format, numMessages)
|
||||
if verbose {
|
||||
name += ", verbose"
|
||||
if verbosityLevel > 0 {
|
||||
name += fmt.Sprintf(", verbosityLevel=%d", verbosityLevel)
|
||||
}
|
||||
|
||||
_, formatter, err := RequestParserAndFormatterFor(format, source, false, !verbose, nil)
|
||||
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 := NewDefaultEventHandler(&buf, source, formatter, verbose)
|
||||
h := &DefaultEventHandler{
|
||||
Out: &buf,
|
||||
Formatter: formatter,
|
||||
VerbosityLevel: verbosityLevel,
|
||||
}
|
||||
|
||||
h.OnResolveMethod(md)
|
||||
h.OnSendHeaders(reqHeaders)
|
||||
@@ -148,6 +154,9 @@ func TestHandler(t *testing.T) {
|
||||
expectedOutput += verbosePrefix
|
||||
}
|
||||
for i := 0; i < numMessages; i++ {
|
||||
if verbosityLevel > 1 {
|
||||
expectedOutput += verboseResponseSize
|
||||
}
|
||||
if verbose {
|
||||
expectedOutput += verboseResponseHeader
|
||||
}
|
||||
@@ -226,6 +235,9 @@ Response trailers received:
|
||||
a: 1
|
||||
b: 2
|
||||
c: 3
|
||||
`
|
||||
verboseResponseSize = `
|
||||
Estimated response size: 100 bytes
|
||||
`
|
||||
verboseResponseHeader = `
|
||||
Response contents:
|
||||
|
||||
14
go.mod
14
go.mod
@@ -1,8 +1,14 @@
|
||||
module github.com/fullstorydev/grpcurl
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.3.1
|
||||
github.com/jhump/protoreflect v1.5.0
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a
|
||||
google.golang.org/grpc v1.21.0
|
||||
cloud.google.com/go v0.56.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/golang/protobuf v1.4.2
|
||||
github.com/jhump/protoreflect v1.10.1
|
||||
google.golang.org/grpc v1.37.0
|
||||
google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||
)
|
||||
|
||||
319
go.sum
319
go.sum
@@ -1,30 +1,327 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.56.0 h1:WRz29PgAsVEyPSDHyk+0fpEkwEFyfhHn+JbksT6gIL4=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/jhump/protoreflect v1.5.0 h1:NgpVT+dX71c8hZnxHof2M7QDK7QtohIJ7DYycjnkyfc=
|
||||
github.com/jhump/protoreflect v1.5.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jhump/protoreflect v1.10.1 h1:iH+UZfsbRE6vpyZH7asAjTPWJf7RJbpZ9j/N3lDlKs0=
|
||||
github.com/jhump/protoreflect v1.10.1/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso=
|
||||
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12 h1:OwhZOOMuf7leLaSCuxtQ9FW7ui2L2L6UKOtKAUqovUQ=
|
||||
google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
134
grpcurl.go
134
grpcurl.go
@@ -8,6 +8,7 @@ package grpcurl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
@@ -20,18 +21,18 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/golang/protobuf/ptypes/struct"
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/jhump/protoreflect/desc/protoprint"
|
||||
"github.com/jhump/protoreflect/dynamic"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"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
|
||||
@@ -405,8 +406,9 @@ func makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescriptor) pr
|
||||
case "google.protobuf.Any":
|
||||
// empty type URL is not allowed by JSON representation
|
||||
// so we must give it a dummy type
|
||||
msg, _ := ptypes.MarshalAny(&empty.Empty{})
|
||||
return msg
|
||||
var any anypb.Any
|
||||
_ = anypb.MarshalFrom(&any, &emptypb.Empty{}, protov2.MarshalOptions{})
|
||||
return &any
|
||||
case "google.protobuf.Value":
|
||||
// unset kind is not allowed by JSON representation
|
||||
// so we must give it something
|
||||
@@ -461,42 +463,42 @@ func makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescriptor) pr
|
||||
for _, fd := range dm.GetMessageDescriptor().GetFields() {
|
||||
if fd.IsRepeated() {
|
||||
switch fd.GetType() {
|
||||
case descpb.FieldDescriptorProto_TYPE_FIXED32,
|
||||
descpb.FieldDescriptorProto_TYPE_UINT32:
|
||||
case descriptorpb.FieldDescriptorProto_TYPE_FIXED32,
|
||||
descriptorpb.FieldDescriptorProto_TYPE_UINT32:
|
||||
dm.AddRepeatedField(fd, uint32(0))
|
||||
|
||||
case descpb.FieldDescriptorProto_TYPE_SFIXED32,
|
||||
descpb.FieldDescriptorProto_TYPE_SINT32,
|
||||
descpb.FieldDescriptorProto_TYPE_INT32,
|
||||
descpb.FieldDescriptorProto_TYPE_ENUM:
|
||||
case descriptorpb.FieldDescriptorProto_TYPE_SFIXED32,
|
||||
descriptorpb.FieldDescriptorProto_TYPE_SINT32,
|
||||
descriptorpb.FieldDescriptorProto_TYPE_INT32,
|
||||
descriptorpb.FieldDescriptorProto_TYPE_ENUM:
|
||||
dm.AddRepeatedField(fd, int32(0))
|
||||
|
||||
case descpb.FieldDescriptorProto_TYPE_FIXED64,
|
||||
descpb.FieldDescriptorProto_TYPE_UINT64:
|
||||
case descriptorpb.FieldDescriptorProto_TYPE_FIXED64,
|
||||
descriptorpb.FieldDescriptorProto_TYPE_UINT64:
|
||||
dm.AddRepeatedField(fd, uint64(0))
|
||||
|
||||
case descpb.FieldDescriptorProto_TYPE_SFIXED64,
|
||||
descpb.FieldDescriptorProto_TYPE_SINT64,
|
||||
descpb.FieldDescriptorProto_TYPE_INT64:
|
||||
case descriptorpb.FieldDescriptorProto_TYPE_SFIXED64,
|
||||
descriptorpb.FieldDescriptorProto_TYPE_SINT64,
|
||||
descriptorpb.FieldDescriptorProto_TYPE_INT64:
|
||||
dm.AddRepeatedField(fd, int64(0))
|
||||
|
||||
case descpb.FieldDescriptorProto_TYPE_STRING:
|
||||
case descriptorpb.FieldDescriptorProto_TYPE_STRING:
|
||||
dm.AddRepeatedField(fd, "")
|
||||
|
||||
case descpb.FieldDescriptorProto_TYPE_BYTES:
|
||||
case descriptorpb.FieldDescriptorProto_TYPE_BYTES:
|
||||
dm.AddRepeatedField(fd, []byte{})
|
||||
|
||||
case descpb.FieldDescriptorProto_TYPE_BOOL:
|
||||
case descriptorpb.FieldDescriptorProto_TYPE_BOOL:
|
||||
dm.AddRepeatedField(fd, false)
|
||||
|
||||
case descpb.FieldDescriptorProto_TYPE_FLOAT:
|
||||
case descriptorpb.FieldDescriptorProto_TYPE_FLOAT:
|
||||
dm.AddRepeatedField(fd, float32(0))
|
||||
|
||||
case descpb.FieldDescriptorProto_TYPE_DOUBLE:
|
||||
case descriptorpb.FieldDescriptorProto_TYPE_DOUBLE:
|
||||
dm.AddRepeatedField(fd, float64(0))
|
||||
|
||||
case descpb.FieldDescriptorProto_TYPE_MESSAGE,
|
||||
descpb.FieldDescriptorProto_TYPE_GROUP:
|
||||
case descriptorpb.FieldDescriptorProto_TYPE_MESSAGE,
|
||||
descriptorpb.FieldDescriptorProto_TYPE_GROUP:
|
||||
dm.AddRepeatedField(fd, makeTemplate(fd.GetMessageType(), path))
|
||||
}
|
||||
} else if fd.GetMessageType() != nil {
|
||||
@@ -506,11 +508,25 @@ func makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescriptor) pr
|
||||
return dm
|
||||
}
|
||||
|
||||
// ClientTransportCredentials builds transport credentials for a gRPC client using the
|
||||
// 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 ClientTransportCredentials(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string) (credentials.TransportCredentials, error) {
|
||||
func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string) (*tls.Config, error) {
|
||||
var tlsConf tls.Config
|
||||
|
||||
if clientCertFile != "" {
|
||||
@@ -540,7 +556,7 @@ func ClientTransportCredentials(insecureSkipVerify bool, cacertFile, clientCertF
|
||||
tlsConf.RootCAs = certPool
|
||||
}
|
||||
|
||||
return credentials.NewTLS(&tlsConf), nil
|
||||
return &tlsConf, nil
|
||||
}
|
||||
|
||||
// ServerTransportCredentials builds transport credentials for a gRPC server using the
|
||||
@@ -605,33 +621,46 @@ func BlockingDial(ctx context.Context, network, address string, creds credential
|
||||
}
|
||||
}
|
||||
|
||||
// custom credentials and dialer will notify on error via the
|
||||
// writeResult function
|
||||
if creds != nil {
|
||||
creds = &errSignalingCreds{
|
||||
TransportCredentials: creds,
|
||||
writeResult: writeResult,
|
||||
}
|
||||
}
|
||||
dialer := func(ctx context.Context, address string) (net.Conn, error) {
|
||||
// NB: We *could* handle the TLS handshake ourselves, in the custom
|
||||
// dialer (instead of customizing both the dialer and the credentials).
|
||||
// But that requires using WithInsecure dial option (so that the gRPC
|
||||
// library doesn't *also* try to do a handshake). And that would mean
|
||||
// that the library would send the wrong ":scheme" metaheader to
|
||||
// servers: it would send "http" instead of "https" because it is
|
||||
// unaware that TLS is actually in use.
|
||||
conn, err := (&net.Dialer{}).DialContext(ctx, network, address)
|
||||
if err != nil {
|
||||
writeResult(err)
|
||||
return nil, err
|
||||
}
|
||||
if creds != nil {
|
||||
conn, _, err = creds.ClientHandshake(ctx, address, conn)
|
||||
if err != nil {
|
||||
writeResult(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return conn, nil
|
||||
return conn, err
|
||||
}
|
||||
|
||||
// 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 channel or fail-fast.
|
||||
// channel to either get the connection or fail-fast.
|
||||
go func() {
|
||||
opts = append(opts,
|
||||
grpc.WithBlock(),
|
||||
grpc.FailOnNonTempDialError(true),
|
||||
grpc.WithContextDialer(dialer),
|
||||
grpc.WithInsecure(), // we are handling TLS, so tell grpc not to
|
||||
)
|
||||
// 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.WithContextDialer(dialer))
|
||||
|
||||
if creds == nil {
|
||||
opts = append(opts, grpc.WithInsecure())
|
||||
} else {
|
||||
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||
}
|
||||
conn, err := grpc.DialContext(ctx, address, opts...)
|
||||
var res interface{}
|
||||
if err != nil {
|
||||
@@ -652,3 +681,18 @@ func BlockingDial(ctx context.Context, network, address string, creds credential
|
||||
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
|
||||
}
|
||||
|
||||
167
grpcurl_test.go
167
grpcurl_test.go
@@ -1,6 +1,7 @@
|
||||
package grpcurl_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -11,22 +12,20 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
jsonpbtest "github.com/golang/protobuf/jsonpb/jsonpb_test_proto"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/jhump/protoreflect/grpcreflect"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/interop/grpc_testing"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/reflection"
|
||||
reflectpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
. "github.com/fullstorydev/grpcurl"
|
||||
grpcurl_testing "github.com/fullstorydev/grpcurl/testing"
|
||||
grpcurl_testing "github.com/fullstorydev/grpcurl/internal/testing"
|
||||
jsonpbtest "github.com/fullstorydev/grpcurl/internal/testing/jsonpb_test_proto"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -52,18 +51,18 @@ type descSourceCase struct {
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
sourceProtoset, err = DescriptorSourceFromProtoSets("testing/test.protoset")
|
||||
sourceProtoset, err = DescriptorSourceFromProtoSets("internal/testing/test.protoset")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sourceProtoFiles, err = DescriptorSourceFromProtoFiles(nil, "testing/test.proto")
|
||||
sourceProtoFiles, err = DescriptorSourceFromProtoFiles([]string{"internal/testing"}, "test.proto")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create a server that includes the reflection service
|
||||
svrReflect := grpc.NewServer()
|
||||
grpc_testing.RegisterTestServiceServer(svrReflect, grpcurl_testing.TestServer{})
|
||||
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 {
|
||||
@@ -89,7 +88,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
// Also create a server that does *not* include the reflection service
|
||||
svrProtoset := grpc.NewServer()
|
||||
grpc_testing.RegisterTestServiceServer(svrProtoset, grpcurl_testing.TestServer{})
|
||||
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)
|
||||
@@ -141,7 +140,7 @@ func TestServerDoesNotSupportReflection(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProtosetWithImports(t *testing.T) {
|
||||
sourceProtoset, err := DescriptorSourceFromProtoSets("testing/example.protoset")
|
||||
sourceProtoset, err := DescriptorSourceFromProtoSets("internal/testing/example.protoset")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load protoset: %v", err)
|
||||
}
|
||||
@@ -178,11 +177,11 @@ func doTestListServices(t *testing.T, source DescriptorSource, includeReflection
|
||||
var expected []string
|
||||
if includeReflection {
|
||||
// when using server reflection, we see the TestService as well as the ServerReflection service
|
||||
expected = []string{"grpc.reflection.v1alpha.ServerReflection", "grpc.testing.TestService"}
|
||||
expected = []string{"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{"grpc.testing.TestService", "grpc.testing.UnimplementedService"}
|
||||
expected = []string{"testing.TestService", "testing.UnimplementedService"}
|
||||
}
|
||||
if !reflect.DeepEqual(expected, names) {
|
||||
t.Errorf("ListServices returned wrong results: wanted %v, got %v", expected, names)
|
||||
@@ -198,17 +197,17 @@ func TestListMethods(t *testing.T) {
|
||||
}
|
||||
|
||||
func doTestListMethods(t *testing.T, source DescriptorSource, includeReflection bool) {
|
||||
names, err := ListMethods(source, "grpc.testing.TestService")
|
||||
names, err := ListMethods(source, "testing.TestService")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list methods for TestService: %v", err)
|
||||
}
|
||||
expected := []string{
|
||||
"grpc.testing.TestService.EmptyCall",
|
||||
"grpc.testing.TestService.FullDuplexCall",
|
||||
"grpc.testing.TestService.HalfDuplexCall",
|
||||
"grpc.testing.TestService.StreamingInputCall",
|
||||
"grpc.testing.TestService.StreamingOutputCall",
|
||||
"grpc.testing.TestService.UnaryCall",
|
||||
"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)
|
||||
@@ -224,11 +223,11 @@ func doTestListMethods(t *testing.T, source DescriptorSource, includeReflection
|
||||
} 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, "grpc.testing.UnimplementedService")
|
||||
names, err = ListMethods(source, "testing.UnimplementedService")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list methods for ServerReflection: %v", err)
|
||||
}
|
||||
expected = []string{"grpc.testing.UnimplementedService.UnimplementedCall"}
|
||||
expected = []string{"testing.UnimplementedService.UnimplementedCall"}
|
||||
}
|
||||
if !reflect.DeepEqual(expected, names) {
|
||||
t.Errorf("ListMethods returned wrong results: wanted %v, got %v", expected, names)
|
||||
@@ -242,10 +241,14 @@ func doTestListMethods(t *testing.T, source DescriptorSource, includeReflection
|
||||
}
|
||||
|
||||
func TestGetAllFiles(t *testing.T) {
|
||||
expectedFiles := []string{"testing/test.proto"}
|
||||
expectedFiles := []string{"test.proto"}
|
||||
// server reflection picks up filename from linked in Go package,
|
||||
// which indicates "grpc_testing/test.proto", not our local copy.
|
||||
expectedFilesWithReflection := []string{"grpc_reflection_v1alpha/reflection.proto", "grpc_testing/test.proto"}
|
||||
expectedFilesWithReflection := [][]string{
|
||||
{"grpc_reflection_v1alpha/reflection.proto", "test.proto"},
|
||||
// depending on the version of grpc, the filenames could be prefixed with "interop/" and "reflection/"
|
||||
{"reflection/grpc_reflection_v1alpha/reflection.proto", "test.proto"},
|
||||
}
|
||||
|
||||
for _, ds := range descSources {
|
||||
t.Run(ds.name, func(t *testing.T) {
|
||||
@@ -254,22 +257,32 @@ func TestGetAllFiles(t *testing.T) {
|
||||
t.Fatalf("failed to get all files: %v", err)
|
||||
}
|
||||
names := fileNames(files)
|
||||
expected := expectedFiles
|
||||
match := false
|
||||
var expected []string
|
||||
if ds.includeRefl {
|
||||
expected = expectedFilesWithReflection
|
||||
for _, expectedNames := range expectedFilesWithReflection {
|
||||
expected = expectedNames
|
||||
if reflect.DeepEqual(expected, names) {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
expected = expectedFiles
|
||||
match = reflect.DeepEqual(expected, names)
|
||||
}
|
||||
if !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("testing/test.protoset", "testing/example.protoset")
|
||||
otherSourceProtoset, err := DescriptorSourceFromProtoSets("internal/testing/test.protoset", "internal/testing/example.protoset")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
otherSourceProtoFiles, err := DescriptorSourceFromProtoFiles(nil, "testing/test.proto", "testing/example.proto")
|
||||
otherSourceProtoFiles, err := DescriptorSourceFromProtoFiles([]string{"internal/testing"}, "test.proto", "example.proto")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -278,13 +291,13 @@ func TestGetAllFiles(t *testing.T) {
|
||||
{"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",
|
||||
"testing/example.proto",
|
||||
"testing/example2.proto",
|
||||
"testing/test.proto",
|
||||
"test.proto",
|
||||
}
|
||||
for _, ds := range otherDescSources {
|
||||
t.Run(ds.name, func(t *testing.T) {
|
||||
@@ -389,7 +402,7 @@ func TestDescribe(t *testing.T) {
|
||||
}
|
||||
|
||||
func doTestDescribe(t *testing.T, source DescriptorSource) {
|
||||
sym := "grpc.testing.TestService.EmptyCall"
|
||||
sym := "testing.TestService.EmptyCall"
|
||||
dsc, err := source.FindSymbol(sym)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get descriptor for %q: %v", sym, err)
|
||||
@@ -400,14 +413,14 @@ func doTestDescribe(t *testing.T, source DescriptorSource) {
|
||||
txt := proto.MarshalTextString(dsc.AsProto())
|
||||
expected :=
|
||||
`name: "EmptyCall"
|
||||
input_type: ".grpc.testing.Empty"
|
||||
output_type: ".grpc.testing.Empty"
|
||||
input_type: ".testing.Empty"
|
||||
output_type: ".testing.Empty"
|
||||
`
|
||||
if expected != txt {
|
||||
t.Errorf("descriptor mismatch: expected %s, got %s", expected, txt)
|
||||
}
|
||||
|
||||
sym = "grpc.testing.StreamingOutputCallResponse"
|
||||
sym = "testing.StreamingOutputCallResponse"
|
||||
dsc, err = source.FindSymbol(sym)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get descriptor for %q: %v", sym, err)
|
||||
@@ -423,7 +436,7 @@ field: <
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".grpc.testing.Payload"
|
||||
type_name: ".testing.Payload"
|
||||
json_name: "payload"
|
||||
>
|
||||
`
|
||||
@@ -479,12 +492,12 @@ func TestUnary(t *testing.T) {
|
||||
func doTestUnary(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
||||
// Success
|
||||
h := &handler{reqMessages: []string{payload1}}
|
||||
err := InvokeRpc(context.Background(), source, cc, "grpc.testing.TestService/UnaryCall", makeHeaders(codes.OK), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.UnaryCall", codes.OK, 1, 1) {
|
||||
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])
|
||||
}
|
||||
@@ -492,12 +505,12 @@ func doTestUnary(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
||||
|
||||
// Failure
|
||||
h = &handler{reqMessages: []string{payload1}}
|
||||
err = InvokeRpc(context.Background(), source, cc, "grpc.testing.TestService/UnaryCall", makeHeaders(codes.NotFound), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.UnaryCall", codes.NotFound, 1, 0)
|
||||
h.check(t, "testing.TestService.UnaryCall", codes.NotFound, 1, 0)
|
||||
}
|
||||
|
||||
func TestClientStream(t *testing.T) {
|
||||
@@ -511,12 +524,12 @@ func TestClientStream(t *testing.T) {
|
||||
func doTestClientStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
||||
// Success
|
||||
h := &handler{reqMessages: []string{payload1, payload2, payload3}}
|
||||
err := InvokeRpc(context.Background(), source, cc, "grpc.testing.TestService/StreamingInputCall", makeHeaders(codes.OK), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.StreamingInputCall", codes.OK, 3, 1) {
|
||||
if h.check(t, "testing.TestService.StreamingInputCall", codes.OK, 3, 1) {
|
||||
expected :=
|
||||
`{
|
||||
"aggregatedPayloadSize": 61
|
||||
@@ -528,21 +541,21 @@ func doTestClientStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSour
|
||||
|
||||
// Fail fast (server rejects as soon as possible)
|
||||
h = &handler{reqMessages: []string{payload1, payload2, payload3}}
|
||||
err = InvokeRpc(context.Background(), source, cc, "grpc.testing.TestService/StreamingInputCall", makeHeaders(codes.InvalidArgument), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.StreamingInputCall", codes.InvalidArgument, -3, 0)
|
||||
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, "grpc.testing.TestService/StreamingInputCall", makeHeaders(codes.Internal, true), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.StreamingInputCall", codes.Internal, 3, 0)
|
||||
h.check(t, "testing.TestService.StreamingInputCall", codes.Internal, 3, 0)
|
||||
}
|
||||
|
||||
func TestServerStream(t *testing.T) {
|
||||
@@ -554,9 +567,9 @@ func TestServerStream(t *testing.T) {
|
||||
}
|
||||
|
||||
func doTestServerStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
||||
req := &grpc_testing.StreamingOutputCallRequest{
|
||||
ResponseType: grpc_testing.PayloadType_COMPRESSABLE,
|
||||
ResponseParameters: []*grpc_testing.ResponseParameters{
|
||||
req := &grpcurl_testing.StreamingOutputCallRequest{
|
||||
ResponseType: grpcurl_testing.PayloadType_COMPRESSABLE,
|
||||
ResponseParameters: []*grpcurl_testing.ResponseParameters{
|
||||
{Size: 10}, {Size: 20}, {Size: 30}, {Size: 40}, {Size: 50},
|
||||
},
|
||||
}
|
||||
@@ -567,19 +580,19 @@ func doTestServerStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSour
|
||||
|
||||
// Success
|
||||
h := &handler{reqMessages: []string{payload}}
|
||||
err = InvokeRpc(context.Background(), source, cc, "grpc.testing.TestService/StreamingOutputCall", makeHeaders(codes.OK), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.StreamingOutputCall", codes.OK, 1, 5) {
|
||||
resp := &grpc_testing.StreamingOutputCallResponse{}
|
||||
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() != grpc_testing.PayloadType_COMPRESSABLE {
|
||||
t.Errorf("response %d has wrong payload type; expecting %v, got %v", i, grpc_testing.PayloadType_COMPRESSABLE, resp.Payload.Type)
|
||||
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))
|
||||
@@ -590,21 +603,21 @@ func doTestServerStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSour
|
||||
|
||||
// Fail fast (server rejects as soon as possible)
|
||||
h = &handler{reqMessages: []string{payload}}
|
||||
err = InvokeRpc(context.Background(), source, cc, "grpc.testing.TestService/StreamingOutputCall", makeHeaders(codes.Aborted), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.StreamingOutputCall", codes.Aborted, 1, 0)
|
||||
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, "grpc.testing.TestService/StreamingOutputCall", makeHeaders(codes.AlreadyExists, true), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.StreamingOutputCall", codes.AlreadyExists, 1, 5)
|
||||
h.check(t, "testing.TestService.StreamingOutputCall", codes.AlreadyExists, 1, 5)
|
||||
}
|
||||
|
||||
func TestHalfDuplexStream(t *testing.T) {
|
||||
@@ -620,12 +633,12 @@ func doTestHalfDuplexStream(t *testing.T, cc *grpc.ClientConn, source Descriptor
|
||||
|
||||
// Success
|
||||
h := &handler{reqMessages: reqs}
|
||||
err := InvokeRpc(context.Background(), source, cc, "grpc.testing.TestService/HalfDuplexCall", makeHeaders(codes.OK), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.HalfDuplexCall", codes.OK, 3, 3) {
|
||||
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)
|
||||
@@ -635,21 +648,21 @@ func doTestHalfDuplexStream(t *testing.T, cc *grpc.ClientConn, source Descriptor
|
||||
|
||||
// Fail fast (server rejects as soon as possible)
|
||||
h = &handler{reqMessages: reqs}
|
||||
err = InvokeRpc(context.Background(), source, cc, "grpc.testing.TestService/HalfDuplexCall", makeHeaders(codes.Canceled), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.HalfDuplexCall", codes.Canceled, -3, 0)
|
||||
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, "grpc.testing.TestService/HalfDuplexCall", makeHeaders(codes.DataLoss, true), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.HalfDuplexCall", codes.DataLoss, 3, 3)
|
||||
h.check(t, "testing.TestService.HalfDuplexCall", codes.DataLoss, 3, 3)
|
||||
}
|
||||
|
||||
func TestFullDuplexStream(t *testing.T) {
|
||||
@@ -662,11 +675,11 @@ func TestFullDuplexStream(t *testing.T) {
|
||||
|
||||
func doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
||||
reqs := make([]string, 3)
|
||||
req := &grpc_testing.StreamingOutputCallRequest{
|
||||
ResponseType: grpc_testing.PayloadType_RANDOM,
|
||||
req := &grpcurl_testing.StreamingOutputCallRequest{
|
||||
ResponseType: grpcurl_testing.PayloadType_RANDOM,
|
||||
}
|
||||
for i := range reqs {
|
||||
req.ResponseParameters = append(req.ResponseParameters, &grpc_testing.ResponseParameters{Size: int32((i + 1) * 10)})
|
||||
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)
|
||||
@@ -676,13 +689,13 @@ func doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source Descriptor
|
||||
|
||||
// Success
|
||||
h := &handler{reqMessages: reqs}
|
||||
err := InvokeRpc(context.Background(), source, cc, "grpc.testing.TestService/FullDuplexCall", makeHeaders(codes.OK), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.FullDuplexCall", codes.OK, 3, 6) {
|
||||
resp := &grpc_testing.StreamingOutputCallResponse{}
|
||||
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
|
||||
@@ -692,8 +705,8 @@ func doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source Descriptor
|
||||
if err := jsonpb.UnmarshalString(msg, resp); err != nil {
|
||||
t.Errorf("failed to parse response %d: %v", i+1, err)
|
||||
}
|
||||
if resp.Payload.GetType() != grpc_testing.PayloadType_RANDOM {
|
||||
t.Errorf("response %d has wrong payload type; expecting %v, got %v", i, grpc_testing.PayloadType_RANDOM, resp.Payload.Type)
|
||||
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))
|
||||
@@ -707,21 +720,21 @@ func doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source Descriptor
|
||||
|
||||
// Fail fast (server rejects as soon as possible)
|
||||
h = &handler{reqMessages: reqs}
|
||||
err = InvokeRpc(context.Background(), source, cc, "grpc.testing.TestService/FullDuplexCall", makeHeaders(codes.PermissionDenied), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.FullDuplexCall", codes.PermissionDenied, -3, 0)
|
||||
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, "grpc.testing.TestService/FullDuplexCall", makeHeaders(codes.ResourceExhausted, true), h, h.getRequestData)
|
||||
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, "grpc.testing.TestService.FullDuplexCall", codes.ResourceExhausted, 3, 6)
|
||||
h.check(t, "testing.TestService.FullDuplexCall", codes.ResourceExhausted, 3, 6)
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
1701
internal/testing/cmd/bankdemo/bank.pb.go
Normal file
1701
internal/testing/cmd/bankdemo/bank.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = "main";
|
||||
option go_package = ".;main";
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
@@ -1,14 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// chatServer implements the Support gRPC service, for providing
|
||||
@@ -134,7 +134,7 @@ func (s *chatServer) ChatCustomer(stream Support_ChatCustomerServer) error {
|
||||
}
|
||||
|
||||
entry := &ChatEntry{
|
||||
Date: ptypes.TimestampNow(),
|
||||
Date: timestamppb.Now(),
|
||||
Entry: &ChatEntry_CustomerMsg{
|
||||
CustomerMsg: req.Msg,
|
||||
},
|
||||
@@ -279,7 +279,7 @@ func (s *chatServer) ChatAgent(stream Support_ChatAgentServer) error {
|
||||
}
|
||||
|
||||
entry := &ChatEntry{
|
||||
Date: ptypes.TimestampNow(),
|
||||
Date: timestamppb.Now(),
|
||||
Entry: &ChatEntry_AgentMsg{
|
||||
AgentMsg: &AgentMessage{
|
||||
AgentName: agent,
|
||||
@@ -1,14 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// In-memory database that is periodically saved to a JSON file.
|
||||
@@ -43,7 +42,7 @@ func (a *accounts) openAccount(customer string, accountType Account_Type, initia
|
||||
acct.Transactions = append(acct.Transactions, &Transaction{
|
||||
AccountNumber: num,
|
||||
SeqNumber: 1,
|
||||
Date: ptypes.TimestampNow(),
|
||||
Date: timestamppb.Now(),
|
||||
AmountCents: initialBalanceCents,
|
||||
Desc: "initial deposit",
|
||||
})
|
||||
@@ -130,7 +129,7 @@ func (a *account) newTransaction(amountCents int32, desc string) (newBalance int
|
||||
a.BalanceCents = bal
|
||||
a.Transactions = append(a.Transactions, &Transaction{
|
||||
AccountNumber: a.AccountNumber,
|
||||
Date: ptypes.TimestampNow(),
|
||||
Date: timestamppb.Now(),
|
||||
AmountCents: amountCents,
|
||||
SeqNumber: uint64(len(a.Transactions) + 1),
|
||||
Desc: desc,
|
||||
@@ -139,16 +138,11 @@ func (a *account) newTransaction(amountCents int32, desc string) (newBalance int
|
||||
}
|
||||
|
||||
func (t *Transaction) MarshalJSON() ([]byte, error) {
|
||||
var jsm jsonpb.Marshaler
|
||||
var buf bytes.Buffer
|
||||
if err := jsm.Marshal(&buf, t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
return protojson.Marshal(t)
|
||||
}
|
||||
|
||||
func (t *Transaction) UnmarshalJSON(b []byte) error {
|
||||
return jsonpb.Unmarshal(bytes.NewReader(b), t)
|
||||
return protojson.Unmarshal(b, t)
|
||||
}
|
||||
|
||||
func (a *accounts) clone() *accounts {
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
//go:generate protoc --go_out=plugins=grpc:./ bank.proto support.proto
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -14,7 +15,6 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/peer"
|
||||
1254
internal/testing/cmd/bankdemo/support.pb.go
Normal file
1254
internal/testing/cmd/bankdemo/support.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = "main";
|
||||
option go_package = ".;main";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
@@ -9,17 +10,15 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/interop/grpc_testing"
|
||||
"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/testing"
|
||||
grpcurl_testing "github.com/fullstorydev/grpcurl/internal/testing"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -96,7 +95,7 @@ func main() {
|
||||
|
||||
svr := grpc.NewServer(opts...)
|
||||
|
||||
grpc_testing.RegisterTestServiceServer(svr, grpcurl_testing.TestServer{})
|
||||
grpcurl_testing.RegisterTestServiceServer(svr, grpcurl_testing.TestServer{})
|
||||
if !*noreflect {
|
||||
reflection.Register(svr)
|
||||
}
|
||||
@@ -3,7 +3,7 @@ syntax = "proto3";
|
||||
import "google/protobuf/descriptor.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "testing/example2.proto";
|
||||
import "example2.proto";
|
||||
|
||||
message TestRequest {
|
||||
repeated string file_names = 1;
|
||||
Binary file not shown.
344
internal/testing/jsonpb_test_proto/test_objects.pb.go
Normal file
344
internal/testing/jsonpb_test_proto/test_objects.pb.go
Normal file
@@ -0,0 +1,344 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0-devel
|
||||
// protoc v3.14.0
|
||||
// source: test_objects.proto
|
||||
|
||||
package jsonpb
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
anypb "google.golang.org/protobuf/types/known/anypb"
|
||||
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||
structpb "google.golang.org/protobuf/types/known/structpb"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
|
||||
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 KnownTypes struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
An *anypb.Any `protobuf:"bytes,14,opt,name=an" json:"an,omitempty"`
|
||||
Dur *durationpb.Duration `protobuf:"bytes,1,opt,name=dur" json:"dur,omitempty"`
|
||||
St *structpb.Struct `protobuf:"bytes,12,opt,name=st" json:"st,omitempty"`
|
||||
Ts *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=ts" json:"ts,omitempty"`
|
||||
Lv *structpb.ListValue `protobuf:"bytes,15,opt,name=lv" json:"lv,omitempty"`
|
||||
Val *structpb.Value `protobuf:"bytes,16,opt,name=val" json:"val,omitempty"`
|
||||
Dbl *wrapperspb.DoubleValue `protobuf:"bytes,3,opt,name=dbl" json:"dbl,omitempty"`
|
||||
Flt *wrapperspb.FloatValue `protobuf:"bytes,4,opt,name=flt" json:"flt,omitempty"`
|
||||
I64 *wrapperspb.Int64Value `protobuf:"bytes,5,opt,name=i64" json:"i64,omitempty"`
|
||||
U64 *wrapperspb.UInt64Value `protobuf:"bytes,6,opt,name=u64" json:"u64,omitempty"`
|
||||
I32 *wrapperspb.Int32Value `protobuf:"bytes,7,opt,name=i32" json:"i32,omitempty"`
|
||||
U32 *wrapperspb.UInt32Value `protobuf:"bytes,8,opt,name=u32" json:"u32,omitempty"`
|
||||
Bool *wrapperspb.BoolValue `protobuf:"bytes,9,opt,name=bool" json:"bool,omitempty"`
|
||||
Str *wrapperspb.StringValue `protobuf:"bytes,10,opt,name=str" json:"str,omitempty"`
|
||||
Bytes *wrapperspb.BytesValue `protobuf:"bytes,11,opt,name=bytes" json:"bytes,omitempty"`
|
||||
}
|
||||
|
||||
func (x *KnownTypes) Reset() {
|
||||
*x = KnownTypes{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_test_objects_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *KnownTypes) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*KnownTypes) ProtoMessage() {}
|
||||
|
||||
func (x *KnownTypes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_test_objects_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 KnownTypes.ProtoReflect.Descriptor instead.
|
||||
func (*KnownTypes) Descriptor() ([]byte, []int) {
|
||||
return file_test_objects_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetAn() *anypb.Any {
|
||||
if x != nil {
|
||||
return x.An
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetDur() *durationpb.Duration {
|
||||
if x != nil {
|
||||
return x.Dur
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetSt() *structpb.Struct {
|
||||
if x != nil {
|
||||
return x.St
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetTs() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.Ts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetLv() *structpb.ListValue {
|
||||
if x != nil {
|
||||
return x.Lv
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetVal() *structpb.Value {
|
||||
if x != nil {
|
||||
return x.Val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetDbl() *wrapperspb.DoubleValue {
|
||||
if x != nil {
|
||||
return x.Dbl
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetFlt() *wrapperspb.FloatValue {
|
||||
if x != nil {
|
||||
return x.Flt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetI64() *wrapperspb.Int64Value {
|
||||
if x != nil {
|
||||
return x.I64
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetU64() *wrapperspb.UInt64Value {
|
||||
if x != nil {
|
||||
return x.U64
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetI32() *wrapperspb.Int32Value {
|
||||
if x != nil {
|
||||
return x.I32
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetU32() *wrapperspb.UInt32Value {
|
||||
if x != nil {
|
||||
return x.U32
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetBool() *wrapperspb.BoolValue {
|
||||
if x != nil {
|
||||
return x.Bool
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetStr() *wrapperspb.StringValue {
|
||||
if x != nil {
|
||||
return x.Str
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KnownTypes) GetBytes() *wrapperspb.BytesValue {
|
||||
if x != nil {
|
||||
return x.Bytes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_test_objects_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_test_objects_proto_rawDesc = []byte{
|
||||
0x0a, 0x12, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x62, 0x1a, 0x19, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e,
|
||||
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 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, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xba, 0x05, 0x0a, 0x0a, 0x4b, 0x6e, 0x6f, 0x77, 0x6e,
|
||||
0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x02, 0x61, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x02, 0x61, 0x6e, 0x12, 0x2b, 0x0a, 0x03, 0x64,
|
||||
0x75, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x52, 0x03, 0x64, 0x75, 0x72, 0x12, 0x27, 0x0a, 0x02, 0x73, 0x74, 0x18, 0x0c,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x02, 0x73,
|
||||
0x74, 0x12, 0x2a, 0x0a, 0x02, 0x74, 0x73, 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, 0x02, 0x74, 0x73, 0x12, 0x2a, 0x0a,
|
||||
0x02, 0x6c, 0x76, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74,
|
||||
0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x02, 0x6c, 0x76, 0x12, 0x28, 0x0a, 0x03, 0x76, 0x61, 0x6c,
|
||||
0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03,
|
||||
0x76, 0x61, 0x6c, 0x12, 0x2e, 0x0a, 0x03, 0x64, 0x62, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03,
|
||||
0x64, 0x62, 0x6c, 0x12, 0x2d, 0x0a, 0x03, 0x66, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x66,
|
||||
0x6c, 0x74, 0x12, 0x2d, 0x0a, 0x03, 0x69, 0x36, 0x34, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x69, 0x36,
|
||||
0x34, 0x12, 0x2e, 0x0a, 0x03, 0x75, 0x36, 0x34, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
|
||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x75, 0x36,
|
||||
0x34, 0x12, 0x2d, 0x0a, 0x03, 0x69, 0x33, 0x32, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b,
|
||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x69, 0x33, 0x32,
|
||||
0x12, 0x2e, 0x0a, 0x03, 0x75, 0x33, 0x32, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||
0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x75, 0x33, 0x32,
|
||||
0x12, 0x2e, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
|
||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x6f, 0x6c,
|
||||
0x12, 0x2e, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x73, 0x74, 0x72,
|
||||
0x12, 0x31, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x62, 0x79,
|
||||
0x74, 0x65, 0x73, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x3b, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x62,
|
||||
}
|
||||
|
||||
var (
|
||||
file_test_objects_proto_rawDescOnce sync.Once
|
||||
file_test_objects_proto_rawDescData = file_test_objects_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_test_objects_proto_rawDescGZIP() []byte {
|
||||
file_test_objects_proto_rawDescOnce.Do(func() {
|
||||
file_test_objects_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_objects_proto_rawDescData)
|
||||
})
|
||||
return file_test_objects_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_test_objects_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_test_objects_proto_goTypes = []interface{}{
|
||||
(*KnownTypes)(nil), // 0: jsonpb.KnownTypes
|
||||
(*anypb.Any)(nil), // 1: google.protobuf.Any
|
||||
(*durationpb.Duration)(nil), // 2: google.protobuf.Duration
|
||||
(*structpb.Struct)(nil), // 3: google.protobuf.Struct
|
||||
(*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp
|
||||
(*structpb.ListValue)(nil), // 5: google.protobuf.ListValue
|
||||
(*structpb.Value)(nil), // 6: google.protobuf.Value
|
||||
(*wrapperspb.DoubleValue)(nil), // 7: google.protobuf.DoubleValue
|
||||
(*wrapperspb.FloatValue)(nil), // 8: google.protobuf.FloatValue
|
||||
(*wrapperspb.Int64Value)(nil), // 9: google.protobuf.Int64Value
|
||||
(*wrapperspb.UInt64Value)(nil), // 10: google.protobuf.UInt64Value
|
||||
(*wrapperspb.Int32Value)(nil), // 11: google.protobuf.Int32Value
|
||||
(*wrapperspb.UInt32Value)(nil), // 12: google.protobuf.UInt32Value
|
||||
(*wrapperspb.BoolValue)(nil), // 13: google.protobuf.BoolValue
|
||||
(*wrapperspb.StringValue)(nil), // 14: google.protobuf.StringValue
|
||||
(*wrapperspb.BytesValue)(nil), // 15: google.protobuf.BytesValue
|
||||
}
|
||||
var file_test_objects_proto_depIdxs = []int32{
|
||||
1, // 0: jsonpb.KnownTypes.an:type_name -> google.protobuf.Any
|
||||
2, // 1: jsonpb.KnownTypes.dur:type_name -> google.protobuf.Duration
|
||||
3, // 2: jsonpb.KnownTypes.st:type_name -> google.protobuf.Struct
|
||||
4, // 3: jsonpb.KnownTypes.ts:type_name -> google.protobuf.Timestamp
|
||||
5, // 4: jsonpb.KnownTypes.lv:type_name -> google.protobuf.ListValue
|
||||
6, // 5: jsonpb.KnownTypes.val:type_name -> google.protobuf.Value
|
||||
7, // 6: jsonpb.KnownTypes.dbl:type_name -> google.protobuf.DoubleValue
|
||||
8, // 7: jsonpb.KnownTypes.flt:type_name -> google.protobuf.FloatValue
|
||||
9, // 8: jsonpb.KnownTypes.i64:type_name -> google.protobuf.Int64Value
|
||||
10, // 9: jsonpb.KnownTypes.u64:type_name -> google.protobuf.UInt64Value
|
||||
11, // 10: jsonpb.KnownTypes.i32:type_name -> google.protobuf.Int32Value
|
||||
12, // 11: jsonpb.KnownTypes.u32:type_name -> google.protobuf.UInt32Value
|
||||
13, // 12: jsonpb.KnownTypes.bool:type_name -> google.protobuf.BoolValue
|
||||
14, // 13: jsonpb.KnownTypes.str:type_name -> google.protobuf.StringValue
|
||||
15, // 14: jsonpb.KnownTypes.bytes:type_name -> google.protobuf.BytesValue
|
||||
15, // [15:15] is the sub-list for method output_type
|
||||
15, // [15:15] is the sub-list for method input_type
|
||||
15, // [15:15] is the sub-list for extension type_name
|
||||
15, // [15:15] is the sub-list for extension extendee
|
||||
0, // [0:15] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_test_objects_proto_init() }
|
||||
func file_test_objects_proto_init() {
|
||||
if File_test_objects_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_test_objects_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*KnownTypes); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_test_objects_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_test_objects_proto_goTypes,
|
||||
DependencyIndexes: file_test_objects_proto_depIdxs,
|
||||
MessageInfos: file_test_objects_proto_msgTypes,
|
||||
}.Build()
|
||||
File_test_objects_proto = out.File
|
||||
file_test_objects_proto_rawDesc = nil
|
||||
file_test_objects_proto_goTypes = nil
|
||||
file_test_objects_proto_depIdxs = nil
|
||||
}
|
||||
30
internal/testing/jsonpb_test_proto/test_objects.proto
Normal file
30
internal/testing/jsonpb_test_proto/test_objects.proto
Normal file
@@ -0,0 +1,30 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/duration.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/wrappers.proto";
|
||||
|
||||
package jsonpb;
|
||||
|
||||
option go_package=".;jsonpb";
|
||||
|
||||
message KnownTypes {
|
||||
optional google.protobuf.Any an = 14;
|
||||
optional google.protobuf.Duration dur = 1;
|
||||
optional google.protobuf.Struct st = 12;
|
||||
optional google.protobuf.Timestamp ts = 2;
|
||||
optional google.protobuf.ListValue lv = 15;
|
||||
optional google.protobuf.Value val = 16;
|
||||
|
||||
optional google.protobuf.DoubleValue dbl = 3;
|
||||
optional google.protobuf.FloatValue flt = 4;
|
||||
optional google.protobuf.Int64Value i64 = 5;
|
||||
optional google.protobuf.UInt64Value u64 = 6;
|
||||
optional google.protobuf.Int32Value i32 = 7;
|
||||
optional google.protobuf.UInt32Value u32 = 8;
|
||||
optional google.protobuf.BoolValue bool = 9;
|
||||
optional google.protobuf.StringValue str = 10;
|
||||
optional google.protobuf.BytesValue bytes = 11;
|
||||
}
|
||||
1522
internal/testing/test.pb.go
Normal file
1522
internal/testing/test.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,9 @@
|
||||
// of unary/streaming requests/responses.
|
||||
syntax = "proto3";
|
||||
|
||||
package grpc.testing;
|
||||
package testing;
|
||||
|
||||
option go_package = ".;testing";
|
||||
|
||||
message Empty {}
|
||||
|
||||
@@ -172,5 +174,5 @@ service TestService {
|
||||
// that case.
|
||||
service UnimplementedService {
|
||||
// A call that no server should implement
|
||||
rpc UnimplementedCall(grpc.testing.Empty) returns (grpc.testing.Empty);
|
||||
rpc UnimplementedCall(Empty) returns (Empty);
|
||||
}
|
||||
BIN
internal/testing/test.protoset
Normal file
BIN
internal/testing/test.protoset
Normal file
Binary file not shown.
@@ -1,14 +1,17 @@
|
||||
package testing
|
||||
|
||||
//go:generate protoc --go_out=plugins=grpc:./ test.proto
|
||||
//go:generate protoc --descriptor_set_out=./test.protoset test.proto
|
||||
//go:generate protoc --descriptor_set_out=./example.protoset --include_imports example.proto
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/interop/grpc_testing"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
@@ -19,7 +22,7 @@ import (
|
||||
type TestServer struct{}
|
||||
|
||||
// EmptyCall accepts one empty request and issues one empty response.
|
||||
func (TestServer) EmptyCall(ctx context.Context, req *grpc_testing.Empty) (*grpc_testing.Empty, error) {
|
||||
func (TestServer) EmptyCall(ctx context.Context, req *Empty) (*Empty, error) {
|
||||
headers, trailers, failEarly, failLate := processMetadata(ctx)
|
||||
grpc.SetHeader(ctx, headers)
|
||||
grpc.SetTrailer(ctx, trailers)
|
||||
@@ -35,7 +38,7 @@ func (TestServer) EmptyCall(ctx context.Context, req *grpc_testing.Empty) (*grpc
|
||||
|
||||
// UnaryCall accepts one request and issues one response. The response includes
|
||||
// the client's payload as-is.
|
||||
func (TestServer) UnaryCall(ctx context.Context, req *grpc_testing.SimpleRequest) (*grpc_testing.SimpleResponse, error) {
|
||||
func (TestServer) UnaryCall(ctx context.Context, req *SimpleRequest) (*SimpleResponse, error) {
|
||||
headers, trailers, failEarly, failLate := processMetadata(ctx)
|
||||
grpc.SetHeader(ctx, headers)
|
||||
grpc.SetTrailer(ctx, trailers)
|
||||
@@ -46,7 +49,7 @@ func (TestServer) UnaryCall(ctx context.Context, req *grpc_testing.SimpleRequest
|
||||
return nil, status.Error(failLate, "fail")
|
||||
}
|
||||
|
||||
return &grpc_testing.SimpleResponse{
|
||||
return &SimpleResponse{
|
||||
Payload: req.Payload,
|
||||
}, nil
|
||||
}
|
||||
@@ -54,7 +57,7 @@ func (TestServer) UnaryCall(ctx context.Context, req *grpc_testing.SimpleRequest
|
||||
// StreamingOutputCall accepts one request and issues a sequence of responses
|
||||
// (streamed download). The server returns the payload with client desired type
|
||||
// and sizes as specified in the request's ResponseParameters.
|
||||
func (TestServer) StreamingOutputCall(req *grpc_testing.StreamingOutputCallRequest, str grpc_testing.TestService_StreamingOutputCallServer) error {
|
||||
func (TestServer) StreamingOutputCall(req *StreamingOutputCallRequest, str TestService_StreamingOutputCallServer) error {
|
||||
headers, trailers, failEarly, failLate := processMetadata(str.Context())
|
||||
str.SetHeader(headers)
|
||||
str.SetTrailer(trailers)
|
||||
@@ -62,7 +65,7 @@ func (TestServer) StreamingOutputCall(req *grpc_testing.StreamingOutputCallReque
|
||||
return status.Error(failEarly, "fail")
|
||||
}
|
||||
|
||||
rsp := &grpc_testing.StreamingOutputCallResponse{Payload: &grpc_testing.Payload{}}
|
||||
rsp := &StreamingOutputCallResponse{Payload: &Payload{}}
|
||||
for _, param := range req.ResponseParameters {
|
||||
if str.Context().Err() != nil {
|
||||
return str.Context().Err()
|
||||
@@ -92,7 +95,7 @@ func (TestServer) StreamingOutputCall(req *grpc_testing.StreamingOutputCallReque
|
||||
// StreamingInputCall accepts a sequence of requests and issues one response
|
||||
// (streamed upload). The server returns the aggregated size of client payloads
|
||||
// as the result.
|
||||
func (TestServer) StreamingInputCall(str grpc_testing.TestService_StreamingInputCallServer) error {
|
||||
func (TestServer) StreamingInputCall(str TestService_StreamingInputCallServer) error {
|
||||
headers, trailers, failEarly, failLate := processMetadata(str.Context())
|
||||
str.SetHeader(headers)
|
||||
str.SetTrailer(trailers)
|
||||
@@ -114,7 +117,7 @@ func (TestServer) StreamingInputCall(str grpc_testing.TestService_StreamingInput
|
||||
sz += len(req.Payload.Body)
|
||||
}
|
||||
}
|
||||
if err := str.SendAndClose(&grpc_testing.StreamingInputCallResponse{AggregatedPayloadSize: int32(sz)}); err != nil {
|
||||
if err := str.SendAndClose(&StreamingInputCallResponse{AggregatedPayloadSize: int32(sz)}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -127,7 +130,7 @@ func (TestServer) StreamingInputCall(str grpc_testing.TestService_StreamingInput
|
||||
// FullDuplexCall accepts a sequence of requests with each request served by the
|
||||
// server immediately. As one request could lead to multiple responses, this
|
||||
// interface demonstrates the idea of full duplexing.
|
||||
func (TestServer) FullDuplexCall(str grpc_testing.TestService_FullDuplexCallServer) error {
|
||||
func (TestServer) FullDuplexCall(str TestService_FullDuplexCallServer) error {
|
||||
headers, trailers, failEarly, failLate := processMetadata(str.Context())
|
||||
str.SetHeader(headers)
|
||||
str.SetTrailer(trailers)
|
||||
@@ -135,7 +138,7 @@ func (TestServer) FullDuplexCall(str grpc_testing.TestService_FullDuplexCallServ
|
||||
return status.Error(failEarly, "fail")
|
||||
}
|
||||
|
||||
rsp := &grpc_testing.StreamingOutputCallResponse{Payload: &grpc_testing.Payload{}}
|
||||
rsp := &StreamingOutputCallResponse{Payload: &Payload{}}
|
||||
for {
|
||||
if str.Context().Err() != nil {
|
||||
return str.Context().Err()
|
||||
@@ -170,7 +173,7 @@ func (TestServer) FullDuplexCall(str grpc_testing.TestService_FullDuplexCallServ
|
||||
// responses. The server buffers all the client requests and then serves them
|
||||
// in order. A stream of responses is returned to the client once the client
|
||||
// half-closes the stream.
|
||||
func (TestServer) HalfDuplexCall(str grpc_testing.TestService_HalfDuplexCallServer) error {
|
||||
func (TestServer) HalfDuplexCall(str TestService_HalfDuplexCallServer) error {
|
||||
headers, trailers, failEarly, failLate := processMetadata(str.Context())
|
||||
str.SetHeader(headers)
|
||||
str.SetTrailer(trailers)
|
||||
@@ -178,7 +181,7 @@ func (TestServer) HalfDuplexCall(str grpc_testing.TestService_HalfDuplexCallServ
|
||||
return status.Error(failEarly, "fail")
|
||||
}
|
||||
|
||||
var reqs []*grpc_testing.StreamingOutputCallRequest
|
||||
var reqs []*StreamingOutputCallRequest
|
||||
for {
|
||||
if str.Context().Err() != nil {
|
||||
return str.Context().Err()
|
||||
@@ -192,7 +195,7 @@ func (TestServer) HalfDuplexCall(str grpc_testing.TestService_HalfDuplexCallServ
|
||||
reqs = append(reqs, req)
|
||||
}
|
||||
}
|
||||
rsp := &grpc_testing.StreamingOutputCallResponse{}
|
||||
rsp := &StreamingOutputCallResponse{}
|
||||
for _, req := range reqs {
|
||||
rsp.Payload = req.Payload
|
||||
if err := str.Send(rsp); err != nil {
|
||||
@@ -251,4 +254,4 @@ func toCode(vals []string) codes.Code {
|
||||
return codes.Code(i)
|
||||
}
|
||||
|
||||
var _ grpc_testing.TestServiceServer = TestServer{}
|
||||
var _ TestServiceServer = TestServer{}
|
||||
36
invoke.go
36
invoke.go
@@ -2,19 +2,19 @@ package grpcurl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/jhump/protoreflect/dynamic"
|
||||
"github.com/jhump/protoreflect/dynamic/grpcdynamic"
|
||||
"github.com/jhump/protoreflect/grpcreflect"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
@@ -93,9 +93,17 @@ func InvokeRPC(ctx context.Context, source DescriptorSource, ch grpcdynamic.Chan
|
||||
if svc == "" || mth == "" {
|
||||
return fmt.Errorf("given method name %q is not in expected format: 'service/method' or 'service.method'", methodName)
|
||||
}
|
||||
|
||||
dsc, err := source.FindSymbol(svc)
|
||||
if err != nil {
|
||||
if isNotFoundError(err) {
|
||||
// return a gRPC status error if hasStatus is true
|
||||
errStatus, hasStatus := status.FromError(err)
|
||||
switch {
|
||||
case hasStatus && isNotFoundError(err):
|
||||
return status.Errorf(errStatus.Code(), "target server does not expose service %q: %s", svc, errStatus.Message())
|
||||
case hasStatus:
|
||||
return status.Errorf(errStatus.Code(), "failed to query for service descriptor %q: %s", svc, errStatus.Message())
|
||||
case isNotFoundError(err):
|
||||
return fmt.Errorf("target server does not expose service %q", svc)
|
||||
}
|
||||
return fmt.Errorf("failed to query for service descriptor %q: %v", svc, err)
|
||||
@@ -219,15 +227,19 @@ func invokeClientStream(ctx context.Context, stub grpcdynamic.Stub, md *desc.Met
|
||||
return fmt.Errorf("grpc call for %q failed: %v", md.GetFullyQualifiedName(), err)
|
||||
}
|
||||
|
||||
if respHeaders, err := str.Header(); err == nil {
|
||||
handler.OnReceiveHeaders(respHeaders)
|
||||
if str != nil {
|
||||
if respHeaders, err := str.Header(); err == nil {
|
||||
handler.OnReceiveHeaders(respHeaders)
|
||||
}
|
||||
}
|
||||
|
||||
if stat.Code() == codes.OK {
|
||||
handler.OnReceiveResponse(resp)
|
||||
}
|
||||
|
||||
handler.OnReceiveTrailers(stat, str.Trailer())
|
||||
if str != nil {
|
||||
handler.OnReceiveTrailers(stat, str.Trailer())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -326,8 +338,10 @@ func invokeBidi(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescr
|
||||
}()
|
||||
}
|
||||
|
||||
if respHeaders, err := str.Header(); err == nil {
|
||||
handler.OnReceiveHeaders(respHeaders)
|
||||
if str != nil {
|
||||
if respHeaders, err := str.Header(); err == nil {
|
||||
handler.OnReceiveHeaders(respHeaders)
|
||||
}
|
||||
}
|
||||
|
||||
// Download each response message
|
||||
@@ -354,7 +368,9 @@ func invokeBidi(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescr
|
||||
return fmt.Errorf("grpc call for %q failed: %v", md.GetFullyQualifiedName(), err)
|
||||
}
|
||||
|
||||
handler.OnReceiveTrailers(stat, str.Trailer())
|
||||
if str != nil {
|
||||
handler.OnReceiveTrailers(stat, str.Trailer())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ protoc testing/example.proto \
|
||||
--include_imports \
|
||||
--descriptor_set_out=testing/example.protoset
|
||||
|
||||
protoc testing/jsonpb_test_proto/test_objects.proto \
|
||||
--go_out=paths=source_relative:.
|
||||
|
||||
echo "Creating certs for TLS testing..."
|
||||
if ! hash certstrap 2>/dev/null; then
|
||||
# certstrap not found: try to install it
|
||||
|
||||
90
releasing/README.md
Normal file
90
releasing/README.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Releases of gRPCurl
|
||||
|
||||
This document provides instructions for building a release of `grpcurl`.
|
||||
|
||||
The release process consists of a handful of tasks:
|
||||
1. Drop a release tag in git.
|
||||
2. Build binaries for various platforms. This is done using the local `go` tool and uses `GOOS` and `GOARCH` environment variables to cross-compile for supported platforms.
|
||||
3. Creates a release in GitHub, uploads the binaries, and creates provisional release notes (in the form of a change log).
|
||||
4. Build a docker image for the new release.
|
||||
5. Push the docker image to Docker Hub, with both a version tag and the "latest" tag.
|
||||
6. Submits a PR to update the [Homebrew](https://brew.sh/) recipe with the latest version.
|
||||
|
||||
Most of this is automated via a script in this same directory. The main thing you will need is a GitHub personal access token, which will be used for creating the release in GitHub (so you need write access to the fullstorydev/grpcurl repo) and to open a Homebrew pull request.
|
||||
|
||||
## Creating a new release
|
||||
|
||||
So, to actually create a new release, just run the script in this directory.
|
||||
|
||||
First, you need a version number for the new release, following sem-ver format: `v<Major>.<Minor>.<Patch>`. Second, you need a personal access token for GitHub.
|
||||
|
||||
We'll use `v2.3.4` as an example version and `abcdef0123456789abcdef` as an example GitHub token:
|
||||
|
||||
```sh
|
||||
# from the root of the repo
|
||||
GITHUB_TOKEN=abcdef0123456789abcd \
|
||||
./releasing/do-release.sh v2.3.4
|
||||
```
|
||||
|
||||
Wasn't that easy! There is one last step: update the release notes in GitHub. By default, the script just records a change log of commit descriptions. Use that log (and, if necessary, drill into individual PRs included in the release) to flesh out notes in the format of the `RELEASE_NOTES.md` file _in this directory_. Then login to GitHub, go to the new release, edit the notes, and paste in the markdown you just wrote.
|
||||
|
||||
That should be all there is to it! If things go wrong and you have to re-do part of the process, see the sections below.
|
||||
|
||||
----
|
||||
|
||||
### GitHub Releases
|
||||
The GitHub release is the first step performed by the `do-release.sh` script. So generally, if there is an issue with that step, you can re-try the whole script.
|
||||
|
||||
Note, if running the script did something wrong, you may have to first login to GitHub and remove uploaded artifacts for a botched release attempt. In general, this is _very undesirable_. Releases should usually be considered immutable. Instead of removing uploaded assets and providing new ones, it is often better to remove uploaded assets (to make bad binaries no longer available) and then _release a new patch version_. (You can edit the release notes for the botched version explaining why there are no artifacts for it.)
|
||||
|
||||
The steps to do a GitHub-only release (vs. running the entire script) are the following:
|
||||
|
||||
```sh
|
||||
# from the root of the repo
|
||||
git tag v2.3.4
|
||||
GITHUB_TOKEN=abcdef0123456789abcdef \
|
||||
GO111MODULE=on \
|
||||
make release
|
||||
```
|
||||
|
||||
The `git tag ...` step is necessary because the release target requires that the current SHA have a sem-ver tag. That's the version it will use when creating the release.
|
||||
|
||||
This will create the release in GitHub with provisional release notes that just include a change log of commit messages. You still need to login to GitHub and revise those notes to adhere to the recommended format. (See `RELEASE_NOTES.md` in this directory.)
|
||||
|
||||
### Docker Hub Releases
|
||||
|
||||
To re-run only the Docker Hub release steps, we need to build an image with the right tag and then push to Docker Hub.
|
||||
|
||||
```sh
|
||||
# from the root of the repo
|
||||
echo v2.3.4 > VERSION
|
||||
docker build -t fullstorydev/grpcurl:v2.3.4 .
|
||||
# now that we have it built, push to Docker Hub
|
||||
docker push fullstorydev/grpcurl:v2.3.4
|
||||
# push "latest" tag, too
|
||||
docker tag fullstorydev/grpcurl:v2.3.4 fullstorydev/grpcurl:latest
|
||||
docker push fullstorydev/grpcurl:latest
|
||||
```
|
||||
|
||||
If the `docker push ...` steps fail, you may need to run `docker login`, enter your Docker Hub login credentials, and then try to push again.
|
||||
|
||||
### Homebrew Releases
|
||||
|
||||
The last step is to update the Homebrew recipe to use the latest version. First, we need to compute the SHA256 checksum for the source archive:
|
||||
|
||||
```sh
|
||||
# download the source archive from GitHub
|
||||
URL=https://github.com/fullstorydev/grpcurl/archive/v2.3.4.tar.gz
|
||||
curl -L -o tmp.tgz $URL
|
||||
# and compute the SHA
|
||||
SHA="$(sha256sum < tmp.tgz | awk '{ print $1 }')"
|
||||
```
|
||||
|
||||
To actually create the brew PR, you need your GitHub personal access token again, as well as the URL and SHA from the previous step:
|
||||
|
||||
```sh
|
||||
HOMEBREW_GITHUB_API_TOKEN=abcdef0123456789abcdef \
|
||||
brew bump-formula-pr --url $URL --sha256 $SHA grpcurl
|
||||
```
|
||||
|
||||
This creates a PR to bump the formula to the new version. When this PR is merged by brew maintainers, the new version becomes available!
|
||||
11
releasing/RELEASE_NOTES.md
Normal file
11
releasing/RELEASE_NOTES.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## Changes
|
||||
|
||||
### Command-line tool
|
||||
|
||||
* _In this list, describe the changes to the command-line tool._
|
||||
* _Use one bullet per change. Include both bug-fixes and improvements. Omit this section if there are no changes that impact the command-line tool._
|
||||
|
||||
### Go package "github.com/fullstorydev/grpcurl"
|
||||
|
||||
* _In this list, describe the changes to exported API in the main package in this repo: "github.com/fullstorydev/grpcurl". These will often be closely related to changes to the command-line tool, though not always: changes that only impact the cmd/grpcurl directory of this repo do not impact exported API._
|
||||
* _Use one bullet per change. Include both bug-fixes and improvements. Omit this section if there are no changes that impact the exported API._
|
||||
61
releasing/do-release.sh
Executable file
61
releasing/do-release.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
# strict mode
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
if [[ -z ${DRY_RUN:-} ]]; then
|
||||
PREFIX=""
|
||||
else
|
||||
PREFIX="echo"
|
||||
fi
|
||||
|
||||
# input validation
|
||||
if [[ -z ${GITHUB_TOKEN:-} ]]; then
|
||||
echo "GITHUB_TOKEN environment variable must be set before running." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ $# -ne 1 || $1 == "" ]]; then
|
||||
echo "This program requires one argument: the version number, in 'vM.N.P' format." >&2
|
||||
exit 1
|
||||
fi
|
||||
VERSION=$1
|
||||
|
||||
# Change to root of the repo
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# GitHub release
|
||||
|
||||
$PREFIX git tag "$VERSION"
|
||||
# make sure GITHUB_TOKEN is exported, for the benefit of this next command
|
||||
export GITHUB_TOKEN
|
||||
GO111MODULE=on $PREFIX make release
|
||||
# if that was successful, it could have touched go.mod and go.sum, so revert those
|
||||
$PREFIX git checkout go.mod go.sum
|
||||
|
||||
# Docker release
|
||||
|
||||
# make sure credentials are valid for later push steps; this might
|
||||
# be interactive since this will prompt for username and password
|
||||
# if there are no valid current credentials.
|
||||
$PREFIX docker login
|
||||
echo "$VERSION" > VERSION
|
||||
|
||||
# Docker Buildx support is included in Docker 19.03
|
||||
# Below step installs emulators for different architectures on the host
|
||||
# This enables running and building containers for below architectures mentioned using --platforms
|
||||
$PREFIX docker run --privileged --rm tonistiigi/binfmt:qemu-v6.1.0 --install all
|
||||
# Create a new builder instance
|
||||
export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
$PREFIX docker buildx create --use --name multiarch-builder --node multiarch-builder0
|
||||
# push to docker hub, both the given version as a tag and for "latest" tag
|
||||
$PREFIX docker buildx build --platform linux/amd64,linux/s390x,linux/arm64 --tag fullstorydev/grpcurl:${VERSION} --tag fullstorydev/grpcurl:latest --push --progress plain --no-cache .
|
||||
rm VERSION
|
||||
|
||||
# Homebrew release
|
||||
|
||||
URL="https://github.com/fullstorydev/grpcurl/archive/${VERSION}.tar.gz"
|
||||
curl -L -o tmp.tgz "$URL"
|
||||
SHA="$(sha256sum < tmp.tgz | awk '{ print $1 }')"
|
||||
rm tmp.tgz
|
||||
HOMEBREW_GITHUB_API_TOKEN="$GITHUB_TOKEN" $PREFIX brew bump-formula-pr --url "$URL" --sha256 "$SHA" grpcurl
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,19 +1,18 @@
|
||||
package grpcurl_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/interop/grpc_testing"
|
||||
|
||||
. "github.com/fullstorydev/grpcurl"
|
||||
grpcurl_testing "github.com/fullstorydev/grpcurl/testing"
|
||||
grpcurl_testing "github.com/fullstorydev/grpcurl/internal/testing"
|
||||
)
|
||||
|
||||
func TestPlainText(t *testing.T) {
|
||||
@@ -27,11 +26,11 @@ func TestPlainText(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBasicTLS(t *testing.T) {
|
||||
serverCreds, err := ServerTransportCredentials("", "testing/tls/server.crt", "testing/tls/server.key", false)
|
||||
serverCreds, err := ServerTransportCredentials("", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
clientCreds, err := ClientTransportCredentials(false, "testing/tls/ca.crt", "", "")
|
||||
clientCreds, err := ClientTransportCredentials(false, "internal/testing/tls/ca.crt", "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -46,7 +45,7 @@ func TestBasicTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInsecureClientTLS(t *testing.T) {
|
||||
serverCreds, err := ServerTransportCredentials("", "testing/tls/server.crt", "testing/tls/server.key", false)
|
||||
serverCreds, err := ServerTransportCredentials("", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -65,11 +64,11 @@ func TestInsecureClientTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientCertTLS(t *testing.T) {
|
||||
serverCreds, err := ServerTransportCredentials("testing/tls/ca.crt", "testing/tls/server.crt", "testing/tls/server.key", false)
|
||||
serverCreds, err := ServerTransportCredentials("internal/testing/tls/ca.crt", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
clientCreds, err := ClientTransportCredentials(false, "testing/tls/ca.crt", "testing/tls/client.crt", "testing/tls/client.key")
|
||||
clientCreds, err := ClientTransportCredentials(false, "internal/testing/tls/ca.crt", "internal/testing/tls/client.crt", "internal/testing/tls/client.key")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -84,11 +83,11 @@ func TestClientCertTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRequireClientCertTLS(t *testing.T) {
|
||||
serverCreds, err := ServerTransportCredentials("testing/tls/ca.crt", "testing/tls/server.crt", "testing/tls/server.key", true)
|
||||
serverCreds, err := ServerTransportCredentials("internal/testing/tls/ca.crt", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
clientCreds, err := ClientTransportCredentials(false, "testing/tls/ca.crt", "testing/tls/client.crt", "testing/tls/client.key")
|
||||
clientCreds, err := ClientTransportCredentials(false, "internal/testing/tls/ca.crt", "internal/testing/tls/client.crt", "internal/testing/tls/client.key")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -103,7 +102,7 @@ func TestRequireClientCertTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBrokenTLS_ClientPlainText(t *testing.T) {
|
||||
serverCreds, err := ServerTransportCredentials("", "testing/tls/server.crt", "testing/tls/server.key", false)
|
||||
serverCreds, err := ServerTransportCredentials("", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -148,8 +147,8 @@ func TestBrokenTLS_ClientPlainText(t *testing.T) {
|
||||
|
||||
// but request fails because server closes connection upon seeing request
|
||||
// bytes that are not a TLS handshake
|
||||
cl := grpc_testing.NewTestServiceClient(e.cc)
|
||||
_, err = cl.UnaryCall(context.Background(), &grpc_testing.SimpleRequest{})
|
||||
cl := grpcurl_testing.NewTestServiceClient(e.cc)
|
||||
_, err = cl.UnaryCall(context.Background(), &grpcurl_testing.SimpleRequest{})
|
||||
if err == nil {
|
||||
t.Fatal("expecting failure")
|
||||
}
|
||||
@@ -164,7 +163,7 @@ func TestBrokenTLS_ClientPlainText(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBrokenTLS_ServerPlainText(t *testing.T) {
|
||||
clientCreds, err := ClientTransportCredentials(false, "testing/tls/ca.crt", "", "")
|
||||
clientCreds, err := ClientTransportCredentials(false, "internal/testing/tls/ca.crt", "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -180,11 +179,11 @@ func TestBrokenTLS_ServerPlainText(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBrokenTLS_ServerUsesWrongCert(t *testing.T) {
|
||||
serverCreds, err := ServerTransportCredentials("", "testing/tls/other.crt", "testing/tls/other.key", false)
|
||||
serverCreds, err := ServerTransportCredentials("", "internal/testing/tls/other.crt", "internal/testing/tls/other.key", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
clientCreds, err := ClientTransportCredentials(false, "testing/tls/ca.crt", "", "")
|
||||
clientCreds, err := ClientTransportCredentials(false, "internal/testing/tls/ca.crt", "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -200,11 +199,11 @@ func TestBrokenTLS_ServerUsesWrongCert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBrokenTLS_ClientHasExpiredCert(t *testing.T) {
|
||||
serverCreds, err := ServerTransportCredentials("testing/tls/ca.crt", "testing/tls/server.crt", "testing/tls/server.key", false)
|
||||
serverCreds, err := ServerTransportCredentials("internal/testing/tls/ca.crt", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
clientCreds, err := ClientTransportCredentials(false, "testing/tls/ca.crt", "testing/tls/expired.crt", "testing/tls/expired.key")
|
||||
clientCreds, err := ClientTransportCredentials(false, "internal/testing/tls/ca.crt", "internal/testing/tls/expired.crt", "internal/testing/tls/expired.key")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -220,11 +219,11 @@ func TestBrokenTLS_ClientHasExpiredCert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBrokenTLS_ServerHasExpiredCert(t *testing.T) {
|
||||
serverCreds, err := ServerTransportCredentials("", "testing/tls/expired.crt", "testing/tls/expired.key", false)
|
||||
serverCreds, err := ServerTransportCredentials("", "internal/testing/tls/expired.crt", "internal/testing/tls/expired.key", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
clientCreds, err := ClientTransportCredentials(false, "testing/tls/ca.crt", "", "")
|
||||
clientCreds, err := ClientTransportCredentials(false, "internal/testing/tls/ca.crt", "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -240,11 +239,11 @@ func TestBrokenTLS_ServerHasExpiredCert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBrokenTLS_ClientNotTrusted(t *testing.T) {
|
||||
serverCreds, err := ServerTransportCredentials("testing/tls/ca.crt", "testing/tls/server.crt", "testing/tls/server.key", true)
|
||||
serverCreds, err := ServerTransportCredentials("internal/testing/tls/ca.crt", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
clientCreds, err := ClientTransportCredentials(false, "testing/tls/ca.crt", "testing/tls/wrong-client.crt", "testing/tls/wrong-client.key")
|
||||
clientCreds, err := ClientTransportCredentials(false, "internal/testing/tls/ca.crt", "internal/testing/tls/wrong-client.crt", "internal/testing/tls/wrong-client.key")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -260,11 +259,11 @@ func TestBrokenTLS_ClientNotTrusted(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBrokenTLS_ServerNotTrusted(t *testing.T) {
|
||||
serverCreds, err := ServerTransportCredentials("", "testing/tls/server.crt", "testing/tls/server.key", false)
|
||||
serverCreds, err := ServerTransportCredentials("", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
clientCreds, err := ClientTransportCredentials(false, "", "testing/tls/client.crt", "testing/tls/client.key")
|
||||
clientCreds, err := ClientTransportCredentials(false, "", "internal/testing/tls/client.crt", "internal/testing/tls/client.key")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -280,11 +279,11 @@ func TestBrokenTLS_ServerNotTrusted(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBrokenTLS_RequireClientCertButNonePresented(t *testing.T) {
|
||||
serverCreds, err := ServerTransportCredentials("testing/tls/ca.crt", "testing/tls/server.crt", "testing/tls/server.key", true)
|
||||
serverCreds, err := ServerTransportCredentials("internal/testing/tls/ca.crt", "internal/testing/tls/server.crt", "internal/testing/tls/server.key", true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
clientCreds, err := ClientTransportCredentials(false, "testing/tls/ca.crt", "", "")
|
||||
clientCreds, err := ClientTransportCredentials(false, "internal/testing/tls/ca.crt", "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create server creds: %v", err)
|
||||
}
|
||||
@@ -300,10 +299,10 @@ func TestBrokenTLS_RequireClientCertButNonePresented(t *testing.T) {
|
||||
}
|
||||
|
||||
func simpleTest(t *testing.T, cc *grpc.ClientConn) {
|
||||
cl := grpc_testing.NewTestServiceClient(cc)
|
||||
cl := grpcurl_testing.NewTestServiceClient(cc)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
_, err := cl.UnaryCall(ctx, &grpc_testing.SimpleRequest{}, grpc.WaitForReady(true))
|
||||
_, err := cl.UnaryCall(ctx, &grpcurl_testing.SimpleRequest{}, grpc.WaitForReady(true))
|
||||
if err != nil {
|
||||
t.Errorf("simple RPC failed: %v", err)
|
||||
}
|
||||
@@ -323,7 +322,7 @@ func createTestServerAndClient(serverCreds, clientCreds credentials.TransportCre
|
||||
svrOpts = []grpc.ServerOption{grpc.Creds(serverCreds)}
|
||||
}
|
||||
svr := grpc.NewServer(svrOpts...)
|
||||
grpc_testing.RegisterTestServiceServer(svr, grpcurl_testing.TestServer{})
|
||||
grpcurl_testing.RegisterTestServiceServer(svr, grpcurl_testing.TestServer{})
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return e, err
|
||||
|
||||
Reference in New Issue
Block a user