Compare commits
156 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
c54eac28fd | |
|
|
6caf0e77fa | |
|
|
1ad1dc15dd | |
|
|
f575e91b2c | |
|
|
ed672b2bc9 | |
|
|
eab6c910a6 | |
|
|
60e53f304f | |
|
|
f093930c85 | |
|
|
7ad93a42d9 | |
|
|
7155fb6211 | |
|
|
f28d506cea | |
|
|
58ccc6321e | |
|
|
b519ffc959 | |
|
|
3a8fa31879 | |
|
|
614b1687cf | |
|
|
30f87c1323 | |
|
|
78655b4786 | |
|
|
d00c28104b | |
|
|
9e3e083f29 | |
|
|
c32936d71e | |
|
|
f3c8ec2564 | |
|
|
7e1a6c9068 | |
|
|
b9a11e9fea | |
|
|
bc5cf811a0 | |
|
|
fb49f049e6 | |
|
|
cdb43b08fa | |
|
|
56181ba330 | |
|
|
46c38b351a | |
|
|
a05d48d6dd | |
|
|
fc63514da1 | |
|
|
e14d9f769a | |
|
|
239dde4a62 | |
|
|
80e833a557 | |
|
|
6fccd7757e | |
|
|
400fa5f2d3 | |
|
|
0e13e85e65 | |
|
|
07361b21ea | |
|
|
8e76884d21 | |
|
|
805ce40c63 | |
|
|
93ea011b36 | |
|
|
5592211a41 | |
|
|
184c8f70b5 | |
|
|
149a93e0ec | |
|
|
252b57fd45 | |
|
|
24b80dfed8 | |
|
|
334e3f56de | |
|
|
f4157743ed | |
|
|
79fb35f680 | |
|
|
7ccaf0a21f | |
|
|
6093b09afa | |
|
|
70c215f7e2 | |
|
|
28c0ee28f0 | |
|
|
bc2944de97 | |
|
|
7a845ca5e9 | |
|
|
c17f0782f7 | |
|
|
42f63028d4 | |
|
|
743e60a4c9 | |
|
|
b7a5d3bba8 | |
|
|
2a29c1e64b | |
|
|
9a59bed1d2 | |
|
|
ae7dadff19 | |
|
|
3961a33e7f | |
|
|
d5b8e4d4ce | |
|
|
0efcfa65f2 | |
|
|
fae58803d9 | |
|
|
1fda47eb90 | |
|
|
031cd3d1e7 | |
|
|
a5037bdf4a | |
|
|
4775fb574b | |
|
|
b96cb4ddd0 | |
|
|
006918123d | |
|
|
66a2405833 | |
|
|
dfd889a44b | |
|
|
a3a5bcd8ab | |
|
|
61a9c00d87 | |
|
|
85f1cbf7ad | |
|
|
dd2f60135c | |
|
|
81c624c41f | |
|
|
3826617999 | |
|
|
8d60b618ac | |
|
|
47b842a805 | |
|
|
898bdad041 | |
|
|
25c896aa59 | |
|
|
0d0992e6a2 | |
|
|
8093376ece | |
|
|
fec466efa6 | |
|
|
7f919e6459 | |
|
|
0df5c93e8d | |
|
|
353e0953cb | |
|
|
c9ac3da95f | |
|
|
ea24a77215 | |
|
|
c3fbeaff64 | |
|
|
7860209a53 | |
|
|
b953ea196a | |
|
|
683a7fb09c | |
|
|
8bb6eeb0d0 | |
|
|
805e231182 | |
|
|
aa5998a119 | |
|
|
b34b13bab3 | |
|
|
b890db745f | |
|
|
57a2cd1a09 | |
|
|
ae7473c7a7 | |
|
|
b3f576068c | |
|
|
8ee6c9423b | |
|
|
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 |
|
|
@ -0,0 +1,40 @@
|
||||||
|
shared_configs:
|
||||||
|
simple_job_steps: &simple_job_steps
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Run tests
|
||||||
|
command: |
|
||||||
|
make test
|
||||||
|
|
||||||
|
# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference
|
||||||
|
version: 2.1
|
||||||
|
jobs:
|
||||||
|
build-1-23:
|
||||||
|
working_directory: ~/repo
|
||||||
|
docker:
|
||||||
|
- image: cimg/go:1.23
|
||||||
|
steps: *simple_job_steps
|
||||||
|
|
||||||
|
build-1-24:
|
||||||
|
working_directory: ~/repo
|
||||||
|
docker:
|
||||||
|
- image: cimg/go:1.24
|
||||||
|
steps: *simple_job_steps
|
||||||
|
|
||||||
|
build-1-25:
|
||||||
|
working_directory: ~/repo
|
||||||
|
docker:
|
||||||
|
- image: cimg/go:1.25
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Run tests and linters
|
||||||
|
command: |
|
||||||
|
make ci
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
pr-build-test:
|
||||||
|
jobs:
|
||||||
|
- build-1-23
|
||||||
|
- build-1-24
|
||||||
|
- build-1-25
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "gomod"
|
||||||
|
directory: "/"
|
||||||
|
# Check for updates once a week
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
# This workflow was added by CodeSee. Learn more at https://codesee.io/
|
||||||
|
# This is v2.0 of this workflow file
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
name: CodeSee
|
||||||
|
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
codesee:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
|
name: Analyze the repo with CodeSee
|
||||||
|
steps:
|
||||||
|
- uses: Codesee-io/codesee-action@v2
|
||||||
|
with:
|
||||||
|
codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }}
|
||||||
|
|
@ -1,2 +1,5 @@
|
||||||
dist/
|
dist/
|
||||||
|
.idea/
|
||||||
VERSION
|
VERSION
|
||||||
|
.tmp/
|
||||||
|
*.snap
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,56 @@ builds:
|
||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
- 386
|
- 386
|
||||||
|
- arm
|
||||||
|
- arm64
|
||||||
|
- s390x
|
||||||
|
- ppc64le
|
||||||
|
goarm:
|
||||||
|
- 5
|
||||||
|
- 6
|
||||||
|
- 7
|
||||||
|
ignore:
|
||||||
|
- goos: darwin
|
||||||
|
goarch: 386
|
||||||
|
- goos: windows
|
||||||
|
goarch: arm64
|
||||||
|
- goos: darwin
|
||||||
|
goarch: arm
|
||||||
|
- goos: windows
|
||||||
|
goarch: arm
|
||||||
|
- goos: darwin
|
||||||
|
goarch: s390x
|
||||||
|
- goos: windows
|
||||||
|
goarch: s390x
|
||||||
|
- goos: darwin
|
||||||
|
goarch: ppc64le
|
||||||
|
- goos: windows
|
||||||
|
goarch: ppc64le
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w -X main.version=v{{.Version}}
|
- -s -w -X main.version=v{{.Version}}
|
||||||
|
|
||||||
archive:
|
archives:
|
||||||
format: tar.gz
|
- format: tar.gz
|
||||||
format_overrides:
|
name_template: >-
|
||||||
- goos: windows
|
{{ .Binary }}_{{ .Version }}_
|
||||||
format: zip
|
{{- if eq .Os "darwin" }}osx{{ else }}{{ .Os }}{{ end }}_
|
||||||
replacements:
|
{{- if eq .Arch "amd64" }}x86_64
|
||||||
amd64: x86_64
|
{{- else if eq .Arch "386" }}x86_32
|
||||||
386: x86_32
|
{{- else }}{{ .Arch }}{{ end }}
|
||||||
darwin: osx
|
{{- with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}
|
||||||
files:
|
format_overrides:
|
||||||
- LICENSE
|
- goos: windows
|
||||||
|
format: zip
|
||||||
|
files:
|
||||||
|
- LICENSE
|
||||||
|
|
||||||
|
nfpms:
|
||||||
|
- vendor: Fullstory
|
||||||
|
homepage: https://github.com/fullstorydev/grpcurl/
|
||||||
|
maintainer: Engineering at Fullstory <fixme@fixme>
|
||||||
|
description: 'Like cURL, but for gRPC: Command-line tool for interacting with gRPC servers'
|
||||||
|
license: MIT
|
||||||
|
id: nfpms
|
||||||
|
formats:
|
||||||
|
- deb
|
||||||
|
- rpm
|
||||||
|
|
|
||||||
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
|
|
||||||
19
Dockerfile
19
Dockerfile
|
|
@ -1,10 +1,6 @@
|
||||||
FROM golang:1.11.10-alpine as builder
|
FROM golang:1.25-alpine AS builder
|
||||||
MAINTAINER FullStory Engineering
|
LABEL 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
|
# create non-privileged group and user
|
||||||
RUN addgroup -S grpcurl && adduser -S grpcurl -G grpcurl
|
RUN addgroup -S grpcurl && adduser -S grpcurl -G grpcurl
|
||||||
|
|
||||||
|
|
@ -15,13 +11,20 @@ COPY cmd /tmp/fullstorydev/grpcurl/cmd
|
||||||
# and build a completely static binary (so we can use
|
# and build a completely static binary (so we can use
|
||||||
# scratch as basis for the final image)
|
# scratch as basis for the final image)
|
||||||
ENV CGO_ENABLED=0
|
ENV CGO_ENABLED=0
|
||||||
ENV GOOS=linux
|
|
||||||
ENV GOARCH=amd64
|
|
||||||
ENV GO111MODULE=on
|
ENV GO111MODULE=on
|
||||||
RUN go build -o /grpcurl \
|
RUN go build -o /grpcurl \
|
||||||
-ldflags "-w -extldflags \"-static\" -X \"main.version=$(cat VERSION)\"" \
|
-ldflags "-w -extldflags \"-static\" -X \"main.version=$(cat VERSION)\"" \
|
||||||
./cmd/grpcurl
|
./cmd/grpcurl
|
||||||
|
|
||||||
|
FROM alpine:3 AS alpine
|
||||||
|
WORKDIR /
|
||||||
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||||
|
COPY --from=builder /etc/passwd /etc/passwd
|
||||||
|
COPY --from=builder /grpcurl /bin/grpcurl
|
||||||
|
USER grpcurl
|
||||||
|
|
||||||
|
ENTRYPOINT ["/bin/grpcurl"]
|
||||||
|
|
||||||
# New FROM so we have a nice'n'tiny image
|
# New FROM so we have a nice'n'tiny image
|
||||||
FROM scratch
|
FROM scratch
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
|
|
|
||||||
4
LICENSE
4
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2017 FullStory, Inc
|
Copyright (c) 2017 Fullstory, Inc
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
|
||||||
61
Makefile
61
Makefile
|
|
@ -1,20 +1,29 @@
|
||||||
dev_build_version=$(shell git describe --tags --always --dirty)
|
dev_build_version=$(shell git describe --tags --always --dirty)
|
||||||
|
|
||||||
|
export PATH := $(shell pwd)/.tmp/protoc/bin:$(PATH)
|
||||||
|
export PROTOC_VERSION := 22.0
|
||||||
|
# Disable CGO for improved compatibility across distros
|
||||||
|
export CGO_ENABLED=0
|
||||||
|
export GOFLAGS=-trimpath
|
||||||
|
export GOWORK=off
|
||||||
|
|
||||||
# TODO: run golint and errcheck, but only to catch *new* violations and
|
# TODO: run golint and errcheck, but only to catch *new* violations and
|
||||||
# decide whether to change code or not (e.g. we need to be able to whitelist
|
# decide whether to change code or not (e.g. we need to be able to whitelist
|
||||||
# violations already in the code). They can be useful to catch errors, but
|
# violations already in the code). They can be useful to catch errors, but
|
||||||
# they are just too noisy to be a requirement for a CI -- we don't even *want*
|
# they are just too noisy to be a requirement for a CI -- we don't even *want*
|
||||||
# to fix some of the things they consider to be violations.
|
# to fix some of the things they consider to be violations.
|
||||||
.PHONY: ci
|
.PHONY: ci
|
||||||
ci: deps checkgofmt vet staticcheck ineffassign predeclared test
|
ci: deps checkgofmt checkgenerate vet staticcheck ineffassign predeclared test
|
||||||
|
|
||||||
.PHONY: deps
|
.PHONY: deps
|
||||||
deps:
|
deps:
|
||||||
go get -d -v -t ./...
|
go get -d -v -t ./...
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
.PHONY: updatedeps
|
.PHONY: updatedeps
|
||||||
updatedeps:
|
updatedeps:
|
||||||
go get -d -v -t -u -f ./...
|
go get -d -v -t -u -f ./...
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install:
|
install:
|
||||||
|
|
@ -22,8 +31,8 @@ install:
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: release
|
||||||
release:
|
release:
|
||||||
@GO111MODULE=off go get github.com/goreleaser/goreleaser
|
@go install github.com/goreleaser/goreleaser@v1.21.0
|
||||||
goreleaser --rm-dist
|
goreleaser release --clean
|
||||||
|
|
||||||
.PHONY: docker
|
.PHONY: docker
|
||||||
docker:
|
docker:
|
||||||
|
|
@ -31,6 +40,22 @@ docker:
|
||||||
docker build -t fullstorydev/grpcurl:$(dev_build_version) .
|
docker build -t fullstorydev/grpcurl:$(dev_build_version) .
|
||||||
@rm VERSION
|
@rm VERSION
|
||||||
|
|
||||||
|
.PHONY: generate
|
||||||
|
generate: .tmp/protoc/bin/protoc
|
||||||
|
@go install google.golang.org/protobuf/cmd/protoc-gen-go@a709e31e5d12
|
||||||
|
@go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1.0
|
||||||
|
@go install github.com/jhump/protoreflect/desc/sourceinfo/cmd/protoc-gen-gosrcinfo@v1.14.1
|
||||||
|
go generate ./...
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
|
.PHONY: checkgenerate
|
||||||
|
checkgenerate: generate
|
||||||
|
git status --porcelain -- '**/*.go'
|
||||||
|
@if [ -n "$$(git status --porcelain -- '**/*.go')" ]; then \
|
||||||
|
git diff -- '**/*.go'; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
.PHONY: checkgofmt
|
.PHONY: checkgofmt
|
||||||
checkgofmt:
|
checkgofmt:
|
||||||
gofmt -s -l .
|
gofmt -s -l .
|
||||||
|
|
@ -42,41 +67,37 @@ checkgofmt:
|
||||||
vet:
|
vet:
|
||||||
go 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
|
.PHONY: staticcheck
|
||||||
staticcheck:
|
staticcheck:
|
||||||
@go get honnef.co/go/tools/cmd/staticcheck
|
@go install honnef.co/go/tools/cmd/staticcheck@2025.1.1
|
||||||
staticcheck ./...
|
staticcheck -checks "inherit,-SA1019" ./...
|
||||||
|
|
||||||
.PHONY: ineffassign
|
.PHONY: ineffassign
|
||||||
ineffassign:
|
ineffassign:
|
||||||
@go get github.com/gordonklaus/ineffassign
|
@go install github.com/gordonklaus/ineffassign@7953dde2c7bf
|
||||||
ineffassign .
|
ineffassign .
|
||||||
|
|
||||||
.PHONY: predeclared
|
.PHONY: predeclared
|
||||||
predeclared:
|
predeclared:
|
||||||
@go get github.com/nishanths/predeclared
|
@go install github.com/nishanths/predeclared@51e8c974458a0f93dc03fe356f91ae1a6d791e6f
|
||||||
predeclared .
|
predeclared ./...
|
||||||
|
|
||||||
# Intentionally omitted from CI, but target here for ad-hoc reports.
|
# Intentionally omitted from CI, but target here for ad-hoc reports.
|
||||||
.PHONY: golint
|
.PHONY: golint
|
||||||
golint:
|
golint:
|
||||||
@go get golang.org/x/lint/golint
|
@go install golang.org/x/lint/golint@v0.0.0-20210508222113-6edffad5e616
|
||||||
golint -min_confidence 0.9 -set_exit_status ./...
|
golint -min_confidence 0.9 -set_exit_status ./...
|
||||||
|
|
||||||
# Intentionally omitted from CI, but target here for ad-hoc reports.
|
# Intentionally omitted from CI, but target here for ad-hoc reports.
|
||||||
.PHONY: errcheck
|
.PHONY: errcheck
|
||||||
errcheck:
|
errcheck:
|
||||||
@go get github.com/kisielk/errcheck
|
@go install github.com/kisielk/errcheck@v1.2.0
|
||||||
errcheck ./...
|
errcheck ./...
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test: deps
|
||||||
go test -race ./...
|
CGO_ENABLED=1 go test -race ./...
|
||||||
|
|
||||||
|
.tmp/protoc/bin/protoc: ./Makefile ./download_protoc.sh
|
||||||
|
./download_protoc.sh
|
||||||
|
|
||||||
|
|
|
||||||
85
README.md
85
README.md
|
|
@ -1,6 +1,7 @@
|
||||||
# gRPCurl
|
# gRPCurl
|
||||||
[](https://travis-ci.org/fullstorydev/grpcurl/branches)
|
[](https://circleci.com/gh/fullstorydev/grpcurl/tree/master)
|
||||||
[](https://goreportcard.com/report/github.com/fullstorydev/grpcurl)
|
[](https://goreportcard.com/report/github.com/fullstorydev/grpcurl)
|
||||||
|
[](https://snapcraft.io/grpcurl)
|
||||||
|
|
||||||
`grpcurl` is a command-line tool that lets you interact with gRPC servers. It's
|
`grpcurl` is a command-line tool that lets you interact with gRPC servers. It's
|
||||||
basically `curl` for gRPC servers.
|
basically `curl` for gRPC servers.
|
||||||
|
|
@ -14,7 +15,7 @@ This program accepts messages using JSON encoding, which is much more friendly f
|
||||||
humans and scripts.
|
humans and scripts.
|
||||||
|
|
||||||
With this tool you can also browse the schema for gRPC services, either by querying
|
With this tool you can also browse the schema for gRPC services, either by querying
|
||||||
a server that supports [server reflection](https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1alpha/reflection.proto),
|
a server that supports [server reflection](https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1/reflection.proto),
|
||||||
by reading proto source files, or by loading in compiled "protoset" files (files that contain
|
by reading proto source files, or by loading in compiled "protoset" files (files that contain
|
||||||
encoded file [descriptor protos](https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto)).
|
encoded file [descriptor protos](https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto)).
|
||||||
In fact, the way the tool transforms JSON request data into a binary encoded protobuf
|
In fact, the way the tool transforms JSON request data into a binary encoded protobuf
|
||||||
|
|
@ -35,9 +36,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
|
operate bi-directional streaming methods interactively by running `grpcurl` from an
|
||||||
interactive terminal and using stdin as the request body!
|
interactive terminal and using stdin as the request body!
|
||||||
|
|
||||||
`grpcurl` supports both plain-text and TLS servers and has numerous options for TLS
|
`grpcurl` supports both secure/TLS servers _and_ plain-text servers (i.e. no TLS) and has
|
||||||
configuration. It also supports mutual TLS, where the client is required to present a
|
numerous options for TLS configuration. It also supports mutual TLS, where the client is
|
||||||
client certificate.
|
required to present a client certificate.
|
||||||
|
|
||||||
As mentioned above, `grpcurl` works seamlessly if the server supports the reflection
|
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
|
service. If not, you can supply the `.proto` source files or you can supply protoset
|
||||||
|
|
@ -49,32 +50,63 @@ files (containing compiled descriptors, produced by `protoc`) to `grpcurl`.
|
||||||
|
|
||||||
Download the binary from the [releases](https://github.com/fullstorydev/grpcurl/releases) page.
|
Download the binary from the [releases](https://github.com/fullstorydev/grpcurl/releases) page.
|
||||||
|
|
||||||
|
### Homebrew (macOS)
|
||||||
|
|
||||||
On macOS, `grpcurl` is available via Homebrew:
|
On macOS, `grpcurl` is available via Homebrew:
|
||||||
```shell
|
```shell
|
||||||
brew install grpcurl
|
brew install grpcurl
|
||||||
```
|
```
|
||||||
|
|
||||||
### From Source
|
### Docker
|
||||||
You can use the `go` tool to install `grpcurl`:
|
|
||||||
|
For platforms that support Docker, you can download an image that lets you run `grpcurl`:
|
||||||
```shell
|
```shell
|
||||||
go get github.com/fullstorydev/grpcurl
|
# Download image
|
||||||
go install github.com/fullstorydev/grpcurl/cmd/grpcurl
|
docker pull fullstorydev/grpcurl:latest
|
||||||
|
# Run the tool
|
||||||
|
docker run fullstorydev/grpcurl api.grpc.me:443 list
|
||||||
|
```
|
||||||
|
Note that there are some pitfalls when using docker:
|
||||||
|
- If you need to interact with a server listening on the host's loopback network, you must specify the host as `host.docker.internal` instead of `localhost` (for Mac or Windows) _OR_ have the container use the host network with `-network="host"` (Linux only).
|
||||||
|
- If you need to provide proto source files or descriptor sets, you must mount the folder containing the files as a volume (`-v $(pwd):/protos`) and adjust the import paths to container paths accordingly.
|
||||||
|
- If you want to provide the request message via stdin, using the `-d @` option, you need to use the `-i` flag on the docker command.
|
||||||
|
|
||||||
|
### Other Packages
|
||||||
|
|
||||||
|
There are numerous other ways to install `grpcurl`, thanks to support from third parties that
|
||||||
|
have created recipes/packages for it. These include other ways to install `grpcurl` on a variety
|
||||||
|
of environments, including Windows and myriad Linux distributions.
|
||||||
|
|
||||||
|
You can see more details and the full list of other packages for `grpcurl` at _repology.org_:
|
||||||
|
https://repology.org/project/grpcurl/information
|
||||||
|
|
||||||
|
### Snap
|
||||||
|
|
||||||
|
You can install `grpcurl` using the snap package:
|
||||||
|
|
||||||
|
`snap install grpcurl`
|
||||||
|
|
||||||
|
### From Source
|
||||||
|
If you already have the [Go SDK](https://golang.org/doc/install) installed, you can use the `go`
|
||||||
|
tool to install `grpcurl`:
|
||||||
|
```shell
|
||||||
|
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
This installs the command into the `bin` sub-folder of wherever your `$GOPATH`
|
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
|
environment variable points. (If you have no `GOPATH` environment variable set,
|
||||||
you should be good to go.
|
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
|
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
|
`$GOPATH` and want to build from the sources, you can `cd` into the repo and then
|
||||||
run `make install`.
|
run `make install`.
|
||||||
|
|
||||||
If you encounter compile errors, you could have out-dated versions of `grpcurl`'s
|
If you encounter compile errors and are using a version of the Go SDK older than 1.13,
|
||||||
dependencies. You can update the dependencies by running `make updatedeps`. You can
|
you could have out-dated versions of `grpcurl`'s dependencies. You can update the
|
||||||
also use [`vgo`](https://github.com/golang/vgo) to install, which will use the right
|
dependencies by running `make updatedeps`. Or, if you are using Go 1.11 or 1.12, you
|
||||||
versions of dependencies. Or, if you are using Go 1.11, you can add `GO111MODULE=on`
|
can add `GO111MODULE=on` as a prefix to the commands above, which will also build using
|
||||||
as a prefix to the commands above, which will also build using the right versions of
|
the right versions of dependencies (vs. whatever you may already have in your `GOPATH`).
|
||||||
dependencies (vs. whatever you may already in your `GOPATH`).
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
The usage doc for the tool explains the numerous options:
|
The usage doc for the tool explains the numerous options:
|
||||||
|
|
@ -91,6 +123,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:
|
do with `grpcurl`. This minimal invocation sends an empty request body:
|
||||||
```shell
|
```shell
|
||||||
grpcurl grpc.server.com:443 my.custom.server.Service/Method
|
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
|
To send a non-empty request, use the `-d` argument. Note that all arguments must come
|
||||||
|
|
@ -117,6 +152,13 @@ grpcurl -d @ grpc.server.com:443 my.custom.server.Service/Method <<EOM
|
||||||
}
|
}
|
||||||
EOM
|
EOM
|
||||||
```
|
```
|
||||||
|
### Adding Headers/Metadata to Request
|
||||||
|
Adding of headers / metadata to a rpc request is possible via the `-H name:value` command line option. Multiple headers can be added in a similar fashion.
|
||||||
|
Example :
|
||||||
|
```shell
|
||||||
|
grpcurl -H header1:value1 -H header2:value2 -d '{"id": 1234, "tags": ["foo","bar"]}' grpc.server.com:443 my.custom.server.Service/Method
|
||||||
|
```
|
||||||
|
For more usage guide, check out the help docs via `grpcurl -help`
|
||||||
|
|
||||||
### Listing Services
|
### Listing Services
|
||||||
To list all services exposed by a server, use the "list" verb. When using `.proto` source
|
To list all services exposed by a server, use the "list" verb. When using `.proto` source
|
||||||
|
|
@ -131,6 +173,13 @@ grpcurl -protoset my-protos.bin list
|
||||||
|
|
||||||
# Using proto sources
|
# Using proto sources
|
||||||
grpcurl -import-path ../protos -proto my-stuff.proto list
|
grpcurl -import-path ../protos -proto my-stuff.proto list
|
||||||
|
|
||||||
|
# Export proto files (use -proto-out-dir to specify the output directory)
|
||||||
|
grpcurl -plaintext -proto-out-dir "out_protos" "localhost:8787" describe my.custom.server.Service
|
||||||
|
|
||||||
|
# Export protoset file (use -protoset-out to specify the output file)
|
||||||
|
grpcurl -plaintext -protoset-out "out.protoset" "localhost:8787" describe my.custom.server.Service
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The "list" verb also lets you see all methods in a particular service:
|
The "list" verb also lets you see all methods in a particular service:
|
||||||
|
|
@ -164,7 +213,7 @@ are needed to use them.
|
||||||
|
|
||||||
### Server Reflection
|
### Server Reflection
|
||||||
|
|
||||||
Without any additional command-line flags, `grpcurl` will try to use [server reflection](https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1alpha/reflection.proto).
|
Without any additional command-line flags, `grpcurl` will try to use [server reflection](https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1/reflection.proto).
|
||||||
|
|
||||||
Examples for how to set up server reflection can be found [here](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md#known-implementations).
|
Examples for how to set up server reflection can be found [here](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md#known-implementations).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
// +build go1.10
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
func indent() string {
|
|
||||||
// In Go 1.10 and up, the flag package automatically
|
|
||||||
// adds the right indentation.
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
// +build !go1.10
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
func indent() string {
|
|
||||||
// In Go 1.9 and older, we need to add indentation
|
|
||||||
// after newlines in the flag doc strings.
|
|
||||||
return " \t"
|
|
||||||
}
|
|
||||||
|
|
@ -4,28 +4,44 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fullstorydev/grpcurl"
|
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 required to use APIs in other grpcurl package
|
||||||
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
|
||||||
"github.com/jhump/protoreflect/desc"
|
|
||||||
"github.com/jhump/protoreflect/grpcreflect"
|
"github.com/jhump/protoreflect/grpcreflect"
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/credentials/alts"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
"google.golang.org/grpc/metadata"
|
"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 response
|
||||||
|
// status codes 'Cancelled' and 'Unknown', 1 and 2 respectively,
|
||||||
|
// the response status codes emitted use an offset of 64
|
||||||
|
const statusCodeOffset = 64
|
||||||
|
|
||||||
|
const noVersion = "dev build <no version set>"
|
||||||
|
|
||||||
|
var version = noVersion
|
||||||
|
|
||||||
var (
|
var (
|
||||||
exit = os.Exit
|
exit = os.Exit
|
||||||
|
|
@ -38,11 +54,14 @@ var (
|
||||||
Print usage instructions and exit.`))
|
Print usage instructions and exit.`))
|
||||||
printVersion = flags.Bool("version", false, prettify(`
|
printVersion = flags.Bool("version", false, prettify(`
|
||||||
Print version.`))
|
Print version.`))
|
||||||
|
|
||||||
plaintext = flags.Bool("plaintext", false, prettify(`
|
plaintext = flags.Bool("plaintext", false, prettify(`
|
||||||
Use plain-text HTTP/2 when connecting to server (no TLS).`))
|
Use plain-text HTTP/2 when connecting to server (no TLS).`))
|
||||||
insecure = flags.Bool("insecure", false, prettify(`
|
insecure = flags.Bool("insecure", false, prettify(`
|
||||||
Skip server certificate and domain verification. (NOT SECURE!) Not
|
Skip server certificate and domain verification. (NOT SECURE!) Not
|
||||||
valid with -plaintext option.`))
|
valid with -plaintext option.`))
|
||||||
|
|
||||||
|
// TLS Options
|
||||||
cacert = flags.String("cacert", "", prettify(`
|
cacert = flags.String("cacert", "", prettify(`
|
||||||
File containing trusted root certificates for verifying the server.
|
File containing trusted root certificates for verifying the server.
|
||||||
Ignored if -insecure is specified.`))
|
Ignored if -insecure is specified.`))
|
||||||
|
|
@ -52,6 +71,13 @@ var (
|
||||||
key = flags.String("key", "", prettify(`
|
key = flags.String("key", "", prettify(`
|
||||||
File containing client private key, to present to the server. Not valid
|
File containing client private key, to present to the server. Not valid
|
||||||
with -plaintext option. Must also provide -cert option.`))
|
with -plaintext option. Must also provide -cert option.`))
|
||||||
|
|
||||||
|
// ALTS Options
|
||||||
|
usealts = flags.Bool("alts", false, prettify(`
|
||||||
|
Use Application Layer Transport Security (ALTS) when connecting to server.`))
|
||||||
|
altsHandshakerServiceAddress = flags.String("alts-handshaker-service", "", prettify(`If set, this server will be used to do the ATLS handshaking.`))
|
||||||
|
altsTargetServiceAccounts multiString
|
||||||
|
|
||||||
protoset multiString
|
protoset multiString
|
||||||
protoFiles multiString
|
protoFiles multiString
|
||||||
importPaths multiString
|
importPaths multiString
|
||||||
|
|
@ -68,8 +94,16 @@ var (
|
||||||
performed. This can be used to supply credentials/secrets without having
|
performed. This can be used to supply credentials/secrets without having
|
||||||
to put them in command-line arguments.`))
|
to put them in command-line arguments.`))
|
||||||
authority = flags.String("authority", "", prettify(`
|
authority = flags.String("authority", "", prettify(`
|
||||||
Value of :authority pseudo-header to be use with underlying HTTP/2
|
The authoritative name of the remote server. This value is passed as the
|
||||||
requests. It defaults to the given address.`))
|
value of the ":authority" pseudo-header in the HTTP/2 protocol. When TLS
|
||||||
|
is used, this will also be used as the server name when verifying the
|
||||||
|
server's certificate. It defaults to the address that is provided in the
|
||||||
|
positional arguments, or 'localhost' in the case of a unix domain
|
||||||
|
socket.`))
|
||||||
|
userAgent = flags.String("user-agent", "", prettify(`
|
||||||
|
If set, the specified value will be added to the User-Agent header set
|
||||||
|
by the grpc-go library.
|
||||||
|
`))
|
||||||
data = flags.String("d", "", prettify(`
|
data = flags.String("d", "", prettify(`
|
||||||
Data for request contents. If the value is '@' then the request contents
|
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
|
are read from stdin. For calls that accept a stream of requests, the
|
||||||
|
|
@ -85,18 +119,27 @@ var (
|
||||||
ASCII character: 0x1E. The stream should not end in a record separator.
|
ASCII character: 0x1E. The stream should not end in a record separator.
|
||||||
If it does, it will be interpreted as a final, blank message after the
|
If it does, it will be interpreted as a final, blank message after the
|
||||||
separator.`))
|
separator.`))
|
||||||
|
allowUnknownFields = flags.Bool("allow-unknown-fields", false, prettify(`
|
||||||
|
When true, the request contents, if 'json' format is used, allows
|
||||||
|
unknown fields to be present. They will be ignored when parsing
|
||||||
|
the request.`))
|
||||||
connectTimeout = flags.Float64("connect-timeout", 0, prettify(`
|
connectTimeout = flags.Float64("connect-timeout", 0, prettify(`
|
||||||
The maximum time, in seconds, to wait for connection to be established.
|
The maximum time, in seconds, to wait for connection to be established.
|
||||||
Defaults to 10 seconds.`))
|
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(`
|
keepaliveTime = flags.Float64("keepalive-time", 0, prettify(`
|
||||||
If present, the maximum idle time in seconds, after which a keepalive
|
If present, the maximum idle time in seconds, after which a keepalive
|
||||||
probe is sent. If the connection remains idle and no keepalive response
|
probe is sent. If the connection remains idle and no keepalive response
|
||||||
is received for this same period then the connection is closed and the
|
is received for this same period then the connection is closed and the
|
||||||
operation fails.`))
|
operation fails.`))
|
||||||
maxTime = flags.Float64("max-time", 0, prettify(`
|
maxTime = flags.Float64("max-time", 0, prettify(`
|
||||||
The maximum total time the operation can take, in seconds. This is
|
The maximum total time the operation can take, in seconds. This sets a
|
||||||
useful for preventing batch jobs that use grpcurl from hanging due to
|
timeout on the gRPC context, allowing both client and server to give up
|
||||||
slow or bad network links or due to incorrect stream method usage.`))
|
after the deadline has past. This is useful for preventing batch jobs
|
||||||
|
that use grpcurl from hanging due to slow or bad network links or due
|
||||||
|
to incorrect stream method usage.`))
|
||||||
maxMsgSz = flags.Int("max-msg-sz", 0, prettify(`
|
maxMsgSz = flags.Int("max-msg-sz", 0, prettify(`
|
||||||
The maximum encoded size of a response message, in bytes, that grpcurl
|
The maximum encoded size of a response message, in bytes, that grpcurl
|
||||||
will accept. If not specified, defaults to 4,194,304 (4 megabytes).`))
|
will accept. If not specified, defaults to 4,194,304 (4 megabytes).`))
|
||||||
|
|
@ -109,19 +152,35 @@ var (
|
||||||
file if this option is given. When invoking an RPC and this option is
|
file if this option is given. When invoking an RPC and this option is
|
||||||
given, the method being invoked and its transitive dependencies will be
|
given, the method being invoked and its transitive dependencies will be
|
||||||
included in the output file.`))
|
included in the output file.`))
|
||||||
|
protoOut = flags.String("proto-out-dir", "", prettify(`
|
||||||
|
The name of a directory where the generated .proto files will be written.
|
||||||
|
With the list and describe verbs, the listed or described elements and
|
||||||
|
their transitive dependencies will be written as .proto files in the
|
||||||
|
specified directory if this option is given. When invoking an RPC and
|
||||||
|
this option is given, the method being invoked and its transitive
|
||||||
|
dependencies will be included in the generated .proto files in the
|
||||||
|
output directory.`))
|
||||||
msgTemplate = flags.Bool("msg-template", false, prettify(`
|
msgTemplate = flags.Bool("msg-template", false, prettify(`
|
||||||
When describing messages, show a template of input data.`))
|
When describing messages, show a template of input data.`))
|
||||||
verbose = flags.Bool("v", false, prettify(`
|
verbose = flags.Bool("v", false, prettify(`
|
||||||
Enable verbose output.`))
|
Enable verbose output.`))
|
||||||
|
veryVerbose = flags.Bool("vv", false, prettify(`
|
||||||
|
Enable very verbose output (includes timing data).`))
|
||||||
serverName = flags.String("servername", "", prettify(`
|
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() {
|
func init() {
|
||||||
flags.Var(&addlHeaders, "H", prettify(`
|
flags.Var(&addlHeaders, "H", prettify(`
|
||||||
Additional headers in 'name: value' format. May specify more than one
|
Additional headers in 'name: value' format. May specify more than one
|
||||||
via multiple flags. These headers will also be included in reflection
|
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(`
|
flags.Var(&rpcHeaders, "rpc-header", prettify(`
|
||||||
Additional RPC headers in 'name: value' format. May specify more than
|
Additional RPC headers in 'name: value' format. May specify more than
|
||||||
one via multiple flags. These headers will *only* be used when invoking
|
one via multiple flags. These headers will *only* be used when invoking
|
||||||
|
|
@ -157,6 +216,20 @@ func init() {
|
||||||
order given. If no import paths are given, all files (including all
|
order given. If no import paths are given, all files (including all
|
||||||
imports) must be provided as -proto flags, and grpcurl will attempt to
|
imports) must be provided as -proto flags, and grpcurl will attempt to
|
||||||
resolve all import statements from the set of file names given.`))
|
resolve all import statements from the set of file names given.`))
|
||||||
|
flags.Var(&reflection, "use-reflection", prettify(`
|
||||||
|
When true, server reflection will be used to determine the RPC schema.
|
||||||
|
Defaults to true unless a -proto or -protoset option is provided. If
|
||||||
|
-use-reflection is used in combination with a -proto or -protoset flag,
|
||||||
|
the provided descriptor sources will be used in addition to server
|
||||||
|
reflection to resolve messages and extensions.`))
|
||||||
|
flags.Var(&altsTargetServiceAccounts, "alts-target-service-account", prettify(`
|
||||||
|
The full email address of the service account that the server is
|
||||||
|
expected to be using when ALTS is used. You can specify this option
|
||||||
|
multiple times to indicate multiple allowed service accounts. If the
|
||||||
|
server authenticates with a service account that is not one of the
|
||||||
|
expected accounts, the RPC will not be issued. If no such arguments are
|
||||||
|
provided, no check will be performed, and the RPC will be issued
|
||||||
|
regardless of the server's service account.`))
|
||||||
}
|
}
|
||||||
|
|
||||||
type multiString []string
|
type multiString []string
|
||||||
|
|
@ -170,6 +243,75 @@ func (s *multiString) Set(value string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uses a file source as a fallback for resolving symbols and extensions, but
|
||||||
|
// only uses the reflection source for listing services
|
||||||
|
type compositeSource struct {
|
||||||
|
reflection grpcurl.DescriptorSource
|
||||||
|
file grpcurl.DescriptorSource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs compositeSource) ListServices() ([]string, error) {
|
||||||
|
return cs.reflection.ListServices()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs compositeSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) {
|
||||||
|
d, err := cs.reflection.FindSymbol(fullyQualifiedName)
|
||||||
|
if err == nil {
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
return cs.file.FindSymbol(fullyQualifiedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs compositeSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) {
|
||||||
|
exts, err := cs.reflection.AllExtensionsForType(typeName)
|
||||||
|
if err != nil {
|
||||||
|
// On error fall back to file source
|
||||||
|
return cs.file.AllExtensionsForType(typeName)
|
||||||
|
}
|
||||||
|
// Track the tag numbers from the reflection source
|
||||||
|
tags := make(map[int32]bool)
|
||||||
|
for _, ext := range exts {
|
||||||
|
tags[ext.GetNumber()] = true
|
||||||
|
}
|
||||||
|
fileExts, err := cs.file.AllExtensionsForType(typeName)
|
||||||
|
if err != nil {
|
||||||
|
return exts, nil
|
||||||
|
}
|
||||||
|
for _, ext := range fileExts {
|
||||||
|
// Prioritize extensions found via reflection
|
||||||
|
if !tags[ext.GetNumber()] {
|
||||||
|
exts = append(exts, ext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type timingData struct {
|
||||||
|
Title string
|
||||||
|
Start time.Time
|
||||||
|
Value time.Duration
|
||||||
|
Parent *timingData
|
||||||
|
Sub []*timingData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *timingData) Child(title string) *timingData {
|
||||||
|
if d == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
child := &timingData{Title: title, Start: time.Now()}
|
||||||
|
d.Sub = append(d.Sub, child)
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *timingData) Done() {
|
||||||
|
if d == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if d.Value == 0 {
|
||||||
|
d.Value = time.Since(d.Start)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flags.Usage = usage
|
flags.Usage = usage
|
||||||
flags.Parse(os.Args[1:])
|
flags.Parse(os.Args[1:])
|
||||||
|
|
@ -182,6 +324,9 @@ func main() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// default behavior is to use tls
|
||||||
|
usetls := !*plaintext && !*usealts
|
||||||
|
|
||||||
// Do extra validation on arguments and figure out what user asked us to do.
|
// Do extra validation on arguments and figure out what user asked us to do.
|
||||||
if *connectTimeout < 0 {
|
if *connectTimeout < 0 {
|
||||||
fail(nil, "The -connect-timeout argument must not be negative.")
|
fail(nil, "The -connect-timeout argument must not be negative.")
|
||||||
|
|
@ -195,20 +340,29 @@ func main() {
|
||||||
if *maxMsgSz < 0 {
|
if *maxMsgSz < 0 {
|
||||||
fail(nil, "The -max-msg-sz argument must not be negative.")
|
fail(nil, "The -max-msg-sz argument must not be negative.")
|
||||||
}
|
}
|
||||||
if *plaintext && *insecure {
|
if *plaintext && *usealts {
|
||||||
fail(nil, "The -plaintext and -insecure arguments are mutually exclusive.")
|
fail(nil, "The -plaintext and -alts arguments are mutually exclusive.")
|
||||||
}
|
}
|
||||||
if *plaintext && *cert != "" {
|
if *insecure && !usetls {
|
||||||
fail(nil, "The -plaintext and -cert arguments are mutually exclusive.")
|
fail(nil, "The -insecure argument can only be used with TLS.")
|
||||||
}
|
}
|
||||||
if *plaintext && *key != "" {
|
if *cert != "" && !usetls {
|
||||||
fail(nil, "The -plaintext and -key arguments are mutually exclusive.")
|
fail(nil, "The -cert argument can only be used with TLS.")
|
||||||
|
}
|
||||||
|
if *key != "" && !usetls {
|
||||||
|
fail(nil, "The -key argument can only be used with TLS.")
|
||||||
}
|
}
|
||||||
if (*key == "") != (*cert == "") {
|
if (*key == "") != (*cert == "") {
|
||||||
fail(nil, "The -cert and -key arguments must be used together and both be present.")
|
fail(nil, "The -cert and -key arguments must be used together and both be present.")
|
||||||
}
|
}
|
||||||
|
if *altsHandshakerServiceAddress != "" && !*usealts {
|
||||||
|
fail(nil, "The -alts-handshaker-service argument must be used with the -alts argument.")
|
||||||
|
}
|
||||||
|
if len(altsTargetServiceAccounts) > 0 && !*usealts {
|
||||||
|
fail(nil, "The -alts-target-service-account argument must be used with the -alts argument.")
|
||||||
|
}
|
||||||
if *format != "json" && *format != "text" {
|
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" {
|
if *emitDefaults && *format != "json" {
|
||||||
warn("The -emit-defaults is only used when using json format.")
|
warn("The -emit-defaults is only used when using json format.")
|
||||||
|
|
@ -239,6 +393,22 @@ func main() {
|
||||||
invoke = true
|
invoke = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verbosityLevel := 0
|
||||||
|
if *verbose {
|
||||||
|
verbosityLevel = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var rootTiming *timingData
|
||||||
|
if *veryVerbose {
|
||||||
|
verbosityLevel = 2
|
||||||
|
|
||||||
|
rootTiming = &timingData{Title: "Timing Data", Start: time.Now()}
|
||||||
|
defer func() {
|
||||||
|
rootTiming.Done()
|
||||||
|
dumpTiming(rootTiming, 0)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
var symbol string
|
var symbol string
|
||||||
if invoke {
|
if invoke {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
|
|
@ -277,23 +447,35 @@ func main() {
|
||||||
if len(importPaths) > 0 && len(protoFiles) == 0 {
|
if len(importPaths) > 0 && len(protoFiles) == 0 {
|
||||||
warn("The -import-path argument is not used unless -proto files are used.")
|
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()
|
ctx := context.Background()
|
||||||
if *maxTime > 0 {
|
if *maxTime > 0 {
|
||||||
timeout := time.Duration(*maxTime * float64(time.Second))
|
timeout := floatSecondsToDuration(*maxTime)
|
||||||
ctx, _ = context.WithTimeout(ctx, timeout)
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, timeout)
|
||||||
|
defer cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
dial := func() *grpc.ClientConn {
|
dial := func() *grpc.ClientConn {
|
||||||
|
dialTiming := rootTiming.Child("Dial")
|
||||||
|
defer dialTiming.Done()
|
||||||
dialTime := 10 * time.Second
|
dialTime := 10 * time.Second
|
||||||
if *connectTimeout > 0 {
|
if *connectTimeout > 0 {
|
||||||
dialTime = time.Duration(*connectTimeout * float64(time.Second))
|
dialTime = floatSecondsToDuration(*connectTimeout)
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithTimeout(ctx, dialTime)
|
ctx, cancel := context.WithTimeout(ctx, dialTime)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
var opts []grpc.DialOption
|
var opts []grpc.DialOption
|
||||||
if *keepaliveTime > 0 {
|
if *keepaliveTime > 0 {
|
||||||
timeout := time.Duration(*keepaliveTime * float64(time.Second))
|
timeout := floatSecondsToDuration(*keepaliveTime)
|
||||||
opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||||
Time: timeout,
|
Time: timeout,
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
|
|
@ -302,32 +484,92 @@ func main() {
|
||||||
if *maxMsgSz > 0 {
|
if *maxMsgSz > 0 {
|
||||||
opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*maxMsgSz)))
|
opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*maxMsgSz)))
|
||||||
}
|
}
|
||||||
if *authority != "" {
|
if isUnixSocket != nil && isUnixSocket() && !strings.HasPrefix(target, "unix://") {
|
||||||
opts = append(opts, grpc.WithAuthority(*authority))
|
// prepend unix:// to the address if it's not already there
|
||||||
|
// this is to maintain backwards compatibility because the custom dialer is replaced by
|
||||||
|
// the default dialer in grpc-go.
|
||||||
|
// https://github.com/fullstorydev/grpcurl/pull/480
|
||||||
|
target = "unix://" + target
|
||||||
}
|
}
|
||||||
var creds credentials.TransportCredentials
|
var creds credentials.TransportCredentials
|
||||||
if !*plaintext {
|
if *plaintext {
|
||||||
var err error
|
if *authority != "" {
|
||||||
creds, err = grpcurl.ClientTransportCredentials(*insecure, *cacert, *cert, *key)
|
opts = append(opts, grpc.WithAuthority(*authority))
|
||||||
if err != nil {
|
|
||||||
fail(err, "Failed to configure transport credentials")
|
|
||||||
}
|
}
|
||||||
if *serverName != "" {
|
} else if *usealts {
|
||||||
if err := creds.OverrideServerName(*serverName); err != nil {
|
clientOptions := alts.DefaultClientOptions()
|
||||||
fail(err, "Failed to override server name as %q", *serverName)
|
if len(altsTargetServiceAccounts) > 0 {
|
||||||
|
clientOptions.TargetServiceAccounts = altsTargetServiceAccounts
|
||||||
|
}
|
||||||
|
if *altsHandshakerServiceAddress != "" {
|
||||||
|
clientOptions.HandshakerServiceAddress = *altsHandshakerServiceAddress
|
||||||
|
}
|
||||||
|
creds = alts.NewClientCreds(clientOptions)
|
||||||
|
} else if usetls {
|
||||||
|
tlsTiming := dialTiming.Child("TLS Setup")
|
||||||
|
defer tlsTiming.Done()
|
||||||
|
|
||||||
|
tlsConf, err := grpcurl.ClientTLSConfig(*insecure, *cacert, *cert, *key)
|
||||||
|
if err != nil {
|
||||||
|
fail(err, "Failed to create TLS config")
|
||||||
|
}
|
||||||
|
|
||||||
|
sslKeylogFile := os.Getenv("SSLKEYLOGFILE")
|
||||||
|
if sslKeylogFile != "" {
|
||||||
|
w, err := os.OpenFile(sslKeylogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
|
||||||
|
if err != nil {
|
||||||
|
fail(err, "Could not open SSLKEYLOGFILE %s", sslKeylogFile)
|
||||||
|
}
|
||||||
|
tlsConf.KeyLogWriter = w
|
||||||
|
}
|
||||||
|
|
||||||
|
creds = credentials.NewTLS(tlsConf)
|
||||||
|
|
||||||
|
// can use either -servername or -authority; but not both
|
||||||
|
if *serverName != "" && *authority != "" {
|
||||||
|
if *serverName == *authority {
|
||||||
|
warn("Both -servername and -authority are present; prefer only -authority.")
|
||||||
|
} else {
|
||||||
|
fail(nil, "Cannot specify different values for -servername and -authority.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
overrideName := *serverName
|
||||||
|
if overrideName == "" {
|
||||||
|
overrideName = *authority
|
||||||
|
}
|
||||||
|
|
||||||
|
if overrideName != "" {
|
||||||
|
opts = append(opts, grpc.WithAuthority(overrideName))
|
||||||
|
}
|
||||||
|
tlsTiming.Done()
|
||||||
|
} else {
|
||||||
|
panic("Should have defaulted to use TLS.")
|
||||||
}
|
}
|
||||||
network := "tcp"
|
|
||||||
if isUnixSocket != nil && isUnixSocket() {
|
grpcurlUA := "grpcurl/" + version
|
||||||
network = "unix"
|
if version == noVersion {
|
||||||
|
grpcurlUA = "grpcurl/dev-build (no version set)"
|
||||||
}
|
}
|
||||||
cc, err := grpcurl.BlockingDial(ctx, network, target, creds, opts...)
|
if *userAgent != "" {
|
||||||
|
grpcurlUA = *userAgent + " " + grpcurlUA
|
||||||
|
}
|
||||||
|
opts = append(opts, grpc.WithUserAgent(grpcurlUA))
|
||||||
|
|
||||||
|
blockingDialTiming := dialTiming.Child("BlockingDial")
|
||||||
|
defer blockingDialTiming.Done()
|
||||||
|
cc, err := grpcurl.BlockingDial(ctx, "", target, creds, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fail(err, "Failed to dial target host %q", target)
|
fail(err, "Failed to dial target host %q", target)
|
||||||
}
|
}
|
||||||
return cc
|
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 {
|
if *expandHeaders {
|
||||||
var err error
|
var err error
|
||||||
|
|
@ -348,24 +590,34 @@ func main() {
|
||||||
var cc *grpc.ClientConn
|
var cc *grpc.ClientConn
|
||||||
var descSource grpcurl.DescriptorSource
|
var descSource grpcurl.DescriptorSource
|
||||||
var refClient *grpcreflect.Client
|
var refClient *grpcreflect.Client
|
||||||
|
var fileSource grpcurl.DescriptorSource
|
||||||
if len(protoset) > 0 {
|
if len(protoset) > 0 {
|
||||||
var err error
|
var err error
|
||||||
descSource, err = grpcurl.DescriptorSourceFromProtoSets(protoset...)
|
fileSource, err = grpcurl.DescriptorSourceFromProtoSets(protoset...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fail(err, "Failed to process proto descriptor sets.")
|
fail(err, "Failed to process proto descriptor sets.")
|
||||||
}
|
}
|
||||||
} else if len(protoFiles) > 0 {
|
} else if len(protoFiles) > 0 {
|
||||||
var err error
|
var err error
|
||||||
descSource, err = grpcurl.DescriptorSourceFromProtoFiles(importPaths, protoFiles...)
|
fileSource, err = grpcurl.DescriptorSourceFromProtoFiles(importPaths, protoFiles...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fail(err, "Failed to process proto source files.")
|
fail(err, "Failed to process proto source files.")
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
if reflection.val {
|
||||||
md := grpcurl.MetadataFromHeaders(append(addlHeaders, reflHeaders...))
|
md := grpcurl.MetadataFromHeaders(append(addlHeaders, reflHeaders...))
|
||||||
refCtx := metadata.NewOutgoingContext(ctx, md)
|
refCtx := metadata.NewOutgoingContext(ctx, md)
|
||||||
cc = dial()
|
cc = dial()
|
||||||
refClient = grpcreflect.NewClient(refCtx, reflectpb.NewServerReflectionClient(cc))
|
refClient = grpcreflect.NewClientAuto(refCtx, cc)
|
||||||
descSource = grpcurl.DescriptorSourceFromServer(ctx, refClient)
|
refClient.AllowMissingFileDescriptors()
|
||||||
|
reflSource := grpcurl.DescriptorSourceFromServer(ctx, refClient)
|
||||||
|
if fileSource != nil {
|
||||||
|
descSource = compositeSource{reflSource, fileSource}
|
||||||
|
} else {
|
||||||
|
descSource = reflSource
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
descSource = fileSource
|
||||||
}
|
}
|
||||||
|
|
||||||
// arrange for the RPCs to be cleanly shutdown
|
// arrange for the RPCs to be cleanly shutdown
|
||||||
|
|
@ -402,6 +654,9 @@ func main() {
|
||||||
if err := writeProtoset(descSource, svcs...); err != nil {
|
if err := writeProtoset(descSource, svcs...); err != nil {
|
||||||
fail(err, "Failed to write protoset to %s", *protosetOut)
|
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||||
}
|
}
|
||||||
|
if err := writeProtos(descSource, svcs...); err != nil {
|
||||||
|
fail(err, "Failed to write protos to %s", *protoOut)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
methods, err := grpcurl.ListMethods(descSource, symbol)
|
methods, err := grpcurl.ListMethods(descSource, symbol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -417,6 +672,9 @@ func main() {
|
||||||
if err := writeProtoset(descSource, symbol); err != nil {
|
if err := writeProtoset(descSource, symbol); err != nil {
|
||||||
fail(err, "Failed to write protoset to %s", *protosetOut)
|
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||||
}
|
}
|
||||||
|
if err := writeProtos(descSource, symbol); err != nil {
|
||||||
|
fail(err, "Failed to write protos to %s", *protoOut)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if describe {
|
} else if describe {
|
||||||
|
|
@ -463,7 +721,7 @@ func main() {
|
||||||
} else {
|
} else {
|
||||||
// see if it's a group
|
// see if it's a group
|
||||||
for _, f := range parent.GetFields() {
|
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
|
// found it: describe the map field instead
|
||||||
elementType = "the type of a group field"
|
elementType = "the type of a group field"
|
||||||
dsc = f
|
dsc = f
|
||||||
|
|
@ -474,7 +732,7 @@ func main() {
|
||||||
}
|
}
|
||||||
case *desc.FieldDescriptor:
|
case *desc.FieldDescriptor:
|
||||||
elementType = "a field"
|
elementType = "a field"
|
||||||
if d.GetType() == descpb.FieldDescriptorProto_TYPE_GROUP {
|
if d.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP {
|
||||||
elementType = "a group field"
|
elementType = "a group field"
|
||||||
} else if d.IsExtension() {
|
} else if d.IsExtension() {
|
||||||
elementType = "an extension"
|
elementType = "an extension"
|
||||||
|
|
@ -505,7 +763,8 @@ func main() {
|
||||||
// for messages, also show a template in JSON, to make it easier to
|
// for messages, also show a template in JSON, to make it easier to
|
||||||
// create a request to invoke an RPC
|
// create a request to invoke an RPC
|
||||||
tmpl := grpcurl.MakeTemplate(dsc)
|
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 {
|
if err != nil {
|
||||||
fail(err, "Failed to construct formatter for %q", *format)
|
fail(err, "Failed to construct formatter for %q", *format)
|
||||||
}
|
}
|
||||||
|
|
@ -520,6 +779,9 @@ func main() {
|
||||||
if err := writeProtoset(descSource, symbols...); err != nil {
|
if err := writeProtoset(descSource, symbols...); err != nil {
|
||||||
fail(err, "Failed to write protoset to %s", *protosetOut)
|
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||||
}
|
}
|
||||||
|
if err := writeProtos(descSource, symbol); err != nil {
|
||||||
|
fail(err, "Failed to write protos to %s", *protoOut)
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Invoke an RPC
|
// Invoke an RPC
|
||||||
|
|
@ -536,16 +798,31 @@ func main() {
|
||||||
// if not verbose output, then also include record delimiters
|
// if not verbose output, then also include record delimiters
|
||||||
// between each message, so output could potentially be piped
|
// between each message, so output could potentially be piped
|
||||||
// to another grpcurl process
|
// to another grpcurl process
|
||||||
includeSeparators := !*verbose
|
includeSeparators := verbosityLevel == 0
|
||||||
rf, formatter, err := grpcurl.RequestParserAndFormatterFor(grpcurl.Format(*format), descSource, *emitDefaults, includeSeparators, in)
|
options := grpcurl.FormatOptions{
|
||||||
|
EmitJSONDefaultFields: *emitDefaults,
|
||||||
|
IncludeTextSeparator: includeSeparators,
|
||||||
|
AllowUnknownFields: *allowUnknownFields,
|
||||||
|
}
|
||||||
|
rf, formatter, err := grpcurl.RequestParserAndFormatter(grpcurl.Format(*format), descSource, in, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fail(err, "Failed to construct request parser and formatter for %q", *format)
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
invokeTiming := rootTiming.Child("InvokeRPC")
|
||||||
err = grpcurl.InvokeRPC(ctx, descSource, cc, symbol, append(addlHeaders, rpcHeaders...), h, rf.Next)
|
err = grpcurl.InvokeRPC(ctx, descSource, cc, symbol, append(addlHeaders, rpcHeaders...), h, rf.Next)
|
||||||
|
invokeTiming.Done()
|
||||||
if err != nil {
|
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 := ""
|
reqSuffix := ""
|
||||||
respSuffix := ""
|
respSuffix := ""
|
||||||
|
|
@ -556,16 +833,31 @@ func main() {
|
||||||
if h.NumResponses != 1 {
|
if h.NumResponses != 1 {
|
||||||
respSuffix = "s"
|
respSuffix = "s"
|
||||||
}
|
}
|
||||||
if *verbose {
|
if verbosityLevel > 0 {
|
||||||
fmt.Printf("Sent %d request%s and received %d response%s\n", reqCount, reqSuffix, h.NumResponses, respSuffix)
|
fmt.Printf("Sent %d request%s and received %d response%s\n", reqCount, reqSuffix, h.NumResponses, respSuffix)
|
||||||
}
|
}
|
||||||
if h.Status.Code() != codes.OK {
|
if h.Status.Code() != codes.OK {
|
||||||
grpcurl.PrintStatus(os.Stderr, h.Status, formatter)
|
if *formatError {
|
||||||
exit(1)
|
printFormattedStatus(os.Stderr, h.Status, formatter)
|
||||||
|
} else {
|
||||||
|
grpcurl.PrintStatus(os.Stderr, h.Status, formatter)
|
||||||
|
}
|
||||||
|
exit(statusCodeOffset + int(h.Status.Code()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dumpTiming(td *timingData, lvl int) {
|
||||||
|
var ind strings.Builder
|
||||||
|
for x := 0; x < lvl; x++ {
|
||||||
|
ind.WriteString(" ")
|
||||||
|
}
|
||||||
|
fmt.Printf("%s%s: %s\n", ind.String(), td.Title, td.Value)
|
||||||
|
for _, sd := range td.Sub {
|
||||||
|
dumpTiming(sd, lvl+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func usage() {
|
func usage() {
|
||||||
fmt.Fprintf(os.Stderr, `Usage:
|
fmt.Fprintf(os.Stderr, `Usage:
|
||||||
%s [flags] [address] [list|describe] [symbol]
|
%s [flags] [address] [list|describe] [symbol]
|
||||||
|
|
@ -613,7 +905,7 @@ func prettify(docString string) string {
|
||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(parts[:j], "\n"+indent())
|
return strings.Join(parts[:j], "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func warn(msg string, args ...interface{}) {
|
func warn(msg string, args ...interface{}) {
|
||||||
|
|
@ -648,3 +940,44 @@ func writeProtoset(descSource grpcurl.DescriptorSource, symbols ...string) error
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
return grpcurl.WriteProtoset(f, descSource, symbols...)
|
return grpcurl.WriteProtoset(f, descSource, symbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeProtos(descSource grpcurl.DescriptorSource, symbols ...string) error {
|
||||||
|
if *protoOut == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return grpcurl.WriteProtoFiles(*protoOut, descSource, symbols...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionalBoolFlag struct {
|
||||||
|
set, val bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *optionalBoolFlag) String() string {
|
||||||
|
if !f.set {
|
||||||
|
return "unset"
|
||||||
|
}
|
||||||
|
return strconv.FormatBool(f.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *optionalBoolFlag) Set(s string) error {
|
||||||
|
v, err := strconv.ParseBool(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.set = true
|
||||||
|
f.val = v
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *optionalBoolFlag) IsBoolFlag() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func floatSecondsToDuration(seconds float64) time.Duration {
|
||||||
|
durationFloat := seconds * float64(time.Second)
|
||||||
|
if durationFloat > math.MaxInt64 {
|
||||||
|
// Avoid overflow
|
||||||
|
return math.MaxInt64
|
||||||
|
}
|
||||||
|
return time.Duration(durationFloat)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows
|
||||||
|
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
||||||
123
desc_source.go
123
desc_source.go
|
|
@ -1,21 +1,23 @@
|
||||||
package grpcurl
|
package grpcurl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||||
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||||
"github.com/jhump/protoreflect/desc"
|
"github.com/jhump/protoreflect/desc/protoparse" //lint:ignore SA1019 same as above
|
||||||
"github.com/jhump/protoreflect/desc/protoparse"
|
"github.com/jhump/protoreflect/desc/protoprint"
|
||||||
"github.com/jhump/protoreflect/dynamic"
|
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
|
||||||
"github.com/jhump/protoreflect/grpcreflect"
|
"github.com/jhump/protoreflect/grpcreflect"
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/descriptorpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrReflectionNotSupported is returned by DescriptorSource operations that
|
// ErrReflectionNotSupported is returned by DescriptorSource operations that
|
||||||
|
|
@ -39,13 +41,13 @@ type DescriptorSource interface {
|
||||||
// DescriptorSourceFromProtoSets creates a DescriptorSource that is backed by the named files, whose contents
|
// DescriptorSourceFromProtoSets creates a DescriptorSource that is backed by the named files, whose contents
|
||||||
// are encoded FileDescriptorSet protos.
|
// are encoded FileDescriptorSet protos.
|
||||||
func DescriptorSourceFromProtoSets(fileNames ...string) (DescriptorSource, error) {
|
func DescriptorSourceFromProtoSets(fileNames ...string) (DescriptorSource, error) {
|
||||||
files := &descpb.FileDescriptorSet{}
|
files := &descriptorpb.FileDescriptorSet{}
|
||||||
for _, fileName := range fileNames {
|
for _, fileName := range fileNames {
|
||||||
b, err := ioutil.ReadFile(fileName)
|
b, err := os.ReadFile(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not load protoset file %q: %v", fileName, err)
|
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)
|
err = proto.Unmarshal(b, &fs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse contents of protoset file %q: %v", fileName, err)
|
return nil, fmt.Errorf("could not parse contents of protoset file %q: %v", fileName, err)
|
||||||
|
|
@ -76,8 +78,8 @@ func DescriptorSourceFromProtoFiles(importPaths []string, fileNames ...string) (
|
||||||
}
|
}
|
||||||
|
|
||||||
// DescriptorSourceFromFileDescriptorSet creates a DescriptorSource that is backed by the FileDescriptorSet.
|
// DescriptorSourceFromFileDescriptorSet creates a DescriptorSource that is backed by the FileDescriptorSet.
|
||||||
func DescriptorSourceFromFileDescriptorSet(files *descpb.FileDescriptorSet) (DescriptorSource, error) {
|
func DescriptorSourceFromFileDescriptorSet(files *descriptorpb.FileDescriptorSet) (DescriptorSource, error) {
|
||||||
unresolved := map[string]*descpb.FileDescriptorProto{}
|
unresolved := map[string]*descriptorpb.FileDescriptorProto{}
|
||||||
for _, fd := range files.File {
|
for _, fd := range files.File {
|
||||||
unresolved[fd.GetName()] = fd
|
unresolved[fd.GetName()] = fd
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +93,7 @@ func DescriptorSourceFromFileDescriptorSet(files *descpb.FileDescriptorSet) (Des
|
||||||
return &fileSource{files: resolved}, nil
|
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 {
|
if r, ok := resolved[filename]; ok {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
@ -258,29 +260,19 @@ func reflectionSupport(err error) error {
|
||||||
// given output. The output will include descriptors for all files in which the
|
// given output. The output will include descriptors for all files in which the
|
||||||
// symbols are defined as well as their transitive dependencies.
|
// symbols are defined as well as their transitive dependencies.
|
||||||
func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string) error {
|
func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string) error {
|
||||||
// compute set of file descriptors
|
filenames, fds, err := getFileDescriptors(symbols, descSource)
|
||||||
filenames := make([]string, 0, len(symbols))
|
if err != nil {
|
||||||
fds := make(map[string]*desc.FileDescriptor, len(symbols))
|
return err
|
||||||
for _, sym := range symbols {
|
|
||||||
d, err := descSource.FindSymbol(sym)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to find descriptor for %q: %v", sym, err)
|
|
||||||
}
|
|
||||||
fd := d.GetFile()
|
|
||||||
if _, ok := fds[fd.GetName()]; !ok {
|
|
||||||
fds[fd.GetName()] = fd
|
|
||||||
filenames = append(filenames, fd.GetName())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// now expand that to include transitive dependencies in topologically sorted
|
// now expand that to include transitive dependencies in topologically sorted
|
||||||
// order (such that file always appears after its dependencies)
|
// order (such that file always appears after its dependencies)
|
||||||
expandedFiles := make(map[string]struct{}, len(fds))
|
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 {
|
for _, filename := range filenames {
|
||||||
allFilesSlice = addFilesToSet(allFilesSlice, expandedFiles, fds[filename])
|
allFilesSlice = addFilesToSet(allFilesSlice, expandedFiles, fds[filename])
|
||||||
}
|
}
|
||||||
// now we can serialize to file
|
// 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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to serialize file descriptor set: %v", err)
|
return fmt.Errorf("failed to serialize file descriptor set: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -290,7 +282,7 @@ func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string
|
||||||
return nil
|
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 {
|
if _, ok := expanded[fd.GetName()]; ok {
|
||||||
// already seen this one
|
// already seen this one
|
||||||
return allFiles
|
return allFiles
|
||||||
|
|
@ -302,3 +294,76 @@ func addFilesToSet(allFiles []*descpb.FileDescriptorProto, expanded map[string]s
|
||||||
}
|
}
|
||||||
return append(allFiles, fd.AsFileDescriptorProto())
|
return append(allFiles, fd.AsFileDescriptorProto())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteProtoFiles will use the given descriptor source to resolve all the given
|
||||||
|
// symbols and write proto files with their definitions to the given output directory.
|
||||||
|
func WriteProtoFiles(outProtoDirPath string, descSource DescriptorSource, symbols ...string) error {
|
||||||
|
filenames, fds, err := getFileDescriptors(symbols, descSource)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// now expand that to include transitive dependencies in topologically sorted
|
||||||
|
// order (such that file always appears after its dependencies)
|
||||||
|
expandedFiles := make(map[string]struct{}, len(fds))
|
||||||
|
allFileDescriptors := make([]*desc.FileDescriptor, 0, len(fds))
|
||||||
|
for _, filename := range filenames {
|
||||||
|
allFileDescriptors = addFilesToFileDescriptorList(allFileDescriptors, expandedFiles, fds[filename])
|
||||||
|
}
|
||||||
|
pr := protoprint.Printer{}
|
||||||
|
// now we can serialize to files
|
||||||
|
for i := range allFileDescriptors {
|
||||||
|
if err := writeProtoFile(outProtoDirPath, allFileDescriptors[i], &pr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeProtoFile(outProtoDirPath string, fd *desc.FileDescriptor, pr *protoprint.Printer) error {
|
||||||
|
outFile := filepath.Join(outProtoDirPath, fd.GetFullyQualifiedName())
|
||||||
|
outDir := filepath.Dir(outFile)
|
||||||
|
if err := os.MkdirAll(outDir, 0777); err != nil {
|
||||||
|
return fmt.Errorf("failed to create directory %q: %w", outDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(outFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create proto file %q: %w", outFile, err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
if err := pr.PrintProtoFile(fd, f); err != nil {
|
||||||
|
return fmt.Errorf("failed to write proto file %q: %w", outFile, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFileDescriptors(symbols []string, descSource DescriptorSource) ([]string, map[string]*desc.FileDescriptor, error) {
|
||||||
|
// compute set of file descriptors
|
||||||
|
filenames := make([]string, 0, len(symbols))
|
||||||
|
fds := make(map[string]*desc.FileDescriptor, len(symbols))
|
||||||
|
for _, sym := range symbols {
|
||||||
|
d, err := descSource.FindSymbol(sym)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to find descriptor for %q: %v", sym, err)
|
||||||
|
}
|
||||||
|
fd := d.GetFile()
|
||||||
|
if _, ok := fds[fd.GetName()]; !ok {
|
||||||
|
fds[fd.GetName()] = fd
|
||||||
|
filenames = append(filenames, fd.GetName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filenames, fds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFilesToFileDescriptorList(allFiles []*desc.FileDescriptor, expanded map[string]struct{}, fd *desc.FileDescriptor) []*desc.FileDescriptor {
|
||||||
|
if _, ok := expanded[fd.GetName()]; ok {
|
||||||
|
// already seen this one
|
||||||
|
return allFiles
|
||||||
|
}
|
||||||
|
expanded[fd.GetName()] = struct{}{}
|
||||||
|
// add all dependencies first
|
||||||
|
for _, dep := range fd.GetDependencies() {
|
||||||
|
allFiles = addFilesToFileDescriptorList(allFiles, expanded, dep)
|
||||||
|
}
|
||||||
|
return append(allFiles, fd)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,24 @@ package grpcurl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||||
"github.com/golang/protobuf/protoc-gen-go/descriptor"
|
"google.golang.org/protobuf/types/descriptorpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWriteProtoset(t *testing.T) {
|
func TestWriteProtoset(t *testing.T) {
|
||||||
exampleProtoset, err := loadProtoset("./testing/example.protoset")
|
exampleProtoset, err := loadProtoset("./internal/testing/example.protoset")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to load example.protoset: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to load test.protoset: %v", err)
|
t.Fatalf("failed to load test.protoset: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mergedProtoset := &descriptor.FileDescriptorSet{
|
mergedProtoset := &descriptorpb.FileDescriptorSet{
|
||||||
File: append(exampleProtoset.File, testProtoset.File...),
|
File: append(exampleProtoset.File, testProtoset.File...),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,29 +29,29 @@ func TestWriteProtoset(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkWriteProtoset(t, descSrc, exampleProtoset, "TestService")
|
checkWriteProtoset(t, descSrc, exampleProtoset, "TestService")
|
||||||
checkWriteProtoset(t, descSrc, testProtoset, "grpc.testing.TestService")
|
checkWriteProtoset(t, descSrc, testProtoset, "testing.TestService")
|
||||||
checkWriteProtoset(t, descSrc, mergedProtoset, "TestService", "grpc.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)
|
b, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var protoset descriptor.FileDescriptorSet
|
var protoset descriptorpb.FileDescriptorSet
|
||||||
if err := proto.Unmarshal(b, &protoset); err != nil {
|
if err := proto.Unmarshal(b, &protoset); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &protoset, nil
|
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
|
var buf bytes.Buffer
|
||||||
if err := WriteProtoset(&buf, descSrc, symbols...); err != nil {
|
if err := WriteProtoset(&buf, descSrc, symbols...); err != nil {
|
||||||
t.Fatalf("failed to write protoset: %v", err)
|
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 {
|
if err := proto.Unmarshal(buf.Bytes(), &result); err != nil {
|
||||||
t.Fatalf("failed to unmarshal written protoset: %v", err)
|
t.Fatalf("failed to unmarshal written protoset: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd $(dirname $0)
|
||||||
|
|
||||||
|
if [[ -z "$PROTOC_VERSION" ]]; then
|
||||||
|
echo "Set PROTOC_VERSION env var to indicate the version to download" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
PROTOC_OS="$(uname -s)"
|
||||||
|
PROTOC_ARCH="$(uname -m)"
|
||||||
|
case "${PROTOC_OS}" in
|
||||||
|
Darwin) PROTOC_OS="osx" ;;
|
||||||
|
Linux) PROTOC_OS="linux" ;;
|
||||||
|
*)
|
||||||
|
echo "Invalid value for uname -s: ${PROTOC_OS}" >&2
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
# This is for macs with M1 chips. Precompiled binaries for osx/amd64 are not available for download, so for that case
|
||||||
|
# we download the x86_64 version instead. This will work as long as rosetta2 is installed.
|
||||||
|
if [ "$PROTOC_OS" = "osx" ] && [ "$PROTOC_ARCH" = "arm64" ]; then
|
||||||
|
PROTOC_ARCH="x86_64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PROTOC="${PWD}/.tmp/protoc/bin/protoc"
|
||||||
|
|
||||||
|
if [[ "$(${PROTOC} --version 2>/dev/null)" != "libprotoc 3.${PROTOC_VERSION}" ]]; then
|
||||||
|
rm -rf ./.tmp/protoc
|
||||||
|
mkdir -p .tmp/protoc
|
||||||
|
curl -L "https://github.com/google/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-${PROTOC_OS}-${PROTOC_ARCH}.zip" > .tmp/protoc/protoc.zip
|
||||||
|
pushd ./.tmp/protoc && unzip protoc.zip && popd
|
||||||
|
fi
|
||||||
|
|
||||||
173
format.go
173
format.go
|
|
@ -11,10 +11,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/golang/protobuf/jsonpb"
|
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
|
||||||
"github.com/jhump/protoreflect/desc"
|
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||||
"github.com/jhump/protoreflect/dynamic"
|
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
@ -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 {
|
func (f *jsonRequestParser) Next(m proto.Message) error {
|
||||||
var msg json.RawMessage
|
var msg json.RawMessage
|
||||||
if err := f.dec.Decode(&msg); err != nil {
|
if err := f.dec.Decode(&msg); err != nil {
|
||||||
|
|
@ -69,7 +78,7 @@ func (f *jsonRequestParser) NumRequests() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
textSeparatorChar = 0x1e
|
textSeparatorChar = '\x1e'
|
||||||
)
|
)
|
||||||
|
|
||||||
type textRequestParser struct {
|
type textRequestParser struct {
|
||||||
|
|
@ -127,10 +136,25 @@ type Formatter func(proto.Message) (string, error)
|
||||||
func NewJSONFormatter(emitDefaults bool, resolver jsonpb.AnyResolver) Formatter {
|
func NewJSONFormatter(emitDefaults bool, resolver jsonpb.AnyResolver) Formatter {
|
||||||
marshaler := jsonpb.Marshaler{
|
marshaler := jsonpb.Marshaler{
|
||||||
EmitDefaults: emitDefaults,
|
EmitDefaults: emitDefaults,
|
||||||
Indent: " ",
|
|
||||||
AnyResolver: resolver,
|
AnyResolver: resolver,
|
||||||
}
|
}
|
||||||
return marshaler.MarshalToString
|
// Workaround for indentation issue in jsonpb with Any messages.
|
||||||
|
// Bug was originally fixed in https://github.com/golang/protobuf/pull/834
|
||||||
|
// but later re-introduced before the module was deprecated and frozen.
|
||||||
|
// If jsonpb is ever replaced with google.golang.org/protobuf/encoding/protojson
|
||||||
|
// this workaround will no longer be needed.
|
||||||
|
formatter := func(message proto.Message) (string, error) {
|
||||||
|
output, err := marshaler.MarshalToString(message)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := json.Indent(&buf, []byte(output), "", " "); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
return formatter
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTextFormatter returns a formatter that returns strings in the protobuf
|
// NewTextFormatter returns a formatter that returns strings in the protobuf
|
||||||
|
|
@ -179,7 +203,7 @@ func (tf *textFormatter) format(m proto.Message) (string, error) {
|
||||||
|
|
||||||
// no trailing newline needed
|
// no trailing newline needed
|
||||||
str := buf.String()
|
str := buf.String()
|
||||||
if str[len(str)-1] == '\n' {
|
if len(str) > 0 && str[len(str)-1] == '\n' {
|
||||||
str = str[:len(str)-1]
|
str = str[:len(str)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,10 +212,20 @@ func (tf *textFormatter) format(m proto.Message) (string, error) {
|
||||||
return str, nil
|
return str, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format of request data. The allowed values are 'json' or 'text'.
|
||||||
type Format string
|
type Format string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// FormatJSON specifies input data in JSON format. Multiple request values
|
||||||
|
// may be concatenated (messages with a JSON representation other than
|
||||||
|
// object must be separated by whitespace, such as a newline)
|
||||||
FormatJSON = Format("json")
|
FormatJSON = Format("json")
|
||||||
|
|
||||||
|
// FormatText specifies input data must be in the protobuf text format.
|
||||||
|
// Multiple request values must be separated by the "record separator"
|
||||||
|
// ASCII character: 0x1E. The stream should not end in a record separator.
|
||||||
|
// If it does, it will be interpreted as a final, blank message after the
|
||||||
|
// separator.
|
||||||
FormatText = Format("text")
|
FormatText = Format("text")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -255,11 +289,11 @@ func (r *anyResolver) Resolve(typeUrl string) (proto.Message, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown message: %s", typeUrl)
|
return nil, fmt.Errorf("unknown message: %s", typeUrl)
|
||||||
}
|
}
|
||||||
// populate any extensions for this message, too
|
// populate any extensions for this message, too (if there are any)
|
||||||
if exts, err := r.source.AllExtensionsForType(mname); err != nil {
|
if exts, err := r.source.AllExtensionsForType(mname); err == nil {
|
||||||
return nil, err
|
if err := r.er.AddExtension(exts...); err != nil {
|
||||||
} else if err := r.er.AddExtension(exts...); err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.mf == nil {
|
if r.mf == nil {
|
||||||
|
|
@ -296,6 +330,7 @@ func (r anyResolverWithFallback) Resolve(typeUrl string) (proto.Message, error)
|
||||||
if slash := strings.LastIndex(mname, "/"); slash >= 0 {
|
if slash := strings.LastIndex(mname, "/"); slash >= 0 {
|
||||||
mname = mname[slash+1:]
|
mname = mname[slash+1:]
|
||||||
}
|
}
|
||||||
|
//lint:ignore SA1019 new non-deprecated API requires other code changes; deferring...
|
||||||
mt := proto.MessageType(mname)
|
mt := proto.MessageType(mname)
|
||||||
if mt != nil {
|
if mt != nil {
|
||||||
return reflect.New(mt.Elem()).Interface().(proto.Message), nil
|
return reflect.New(mt.Elem()).Interface().(proto.Message), nil
|
||||||
|
|
@ -342,31 +377,70 @@ func (a *unknownAny) ProtoMessage() {
|
||||||
|
|
||||||
var _ proto.Message = (*unknownAny)(nil)
|
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
|
// RequestParserAndFormatterFor returns a request parser and formatter for the
|
||||||
// given format. The given descriptor source may be used for parsing message
|
// given format. The given descriptor source may be used for parsing message
|
||||||
// data (if needed by the format). The flags emitJSONDefaultFields and
|
// data (if needed by the format). The flags emitJSONDefaultFields and
|
||||||
// includeTextSeparator are options for JSON and protobuf text formats,
|
// includeTextSeparator are options for JSON and protobuf text formats,
|
||||||
// respectively. Requests will be parsed from the given in.
|
// 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) {
|
func RequestParserAndFormatterFor(format Format, descSource DescriptorSource, emitJSONDefaultFields, includeTextSeparator bool, in io.Reader) (RequestParser, Formatter, error) {
|
||||||
switch format {
|
return RequestParserAndFormatter(format, descSource, in, FormatOptions{
|
||||||
case FormatJSON:
|
EmitJSONDefaultFields: emitJSONDefaultFields,
|
||||||
resolver := AnyResolverFromDescriptorSource(descSource)
|
IncludeTextSeparator: includeTextSeparator,
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultEventHandler logs events to a writer. This is not thread-safe, but is
|
// 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
|
// safe for use with InvokeRPC as long as NumResponses and Status are not read
|
||||||
// until the call to InvokeRPC completes.
|
// until the call to InvokeRPC completes.
|
||||||
type DefaultEventHandler struct {
|
type DefaultEventHandler struct {
|
||||||
out io.Writer
|
Out io.Writer
|
||||||
descSource DescriptorSource
|
Formatter Formatter
|
||||||
formatter func(proto.Message) (string, error)
|
// 0 = default
|
||||||
verbose bool
|
// 1 = verbose
|
||||||
|
// 2 = very verbose
|
||||||
|
VerbosityLevel int
|
||||||
|
|
||||||
// NumResponses is the number of responses that have been received.
|
// NumResponses is the number of responses that have been received.
|
||||||
NumResponses int
|
NumResponses int
|
||||||
|
|
@ -378,54 +452,65 @@ type DefaultEventHandler struct {
|
||||||
// NewDefaultEventHandler returns an InvocationEventHandler that logs events to
|
// NewDefaultEventHandler returns an InvocationEventHandler that logs events to
|
||||||
// the given output. If verbose is true, all events are logged. Otherwise, only
|
// the given output. If verbose is true, all events are logged. Otherwise, only
|
||||||
// response messages are logged.
|
// response messages are logged.
|
||||||
|
//
|
||||||
|
// Deprecated: NewDefaultEventHandler exists for compatibility.
|
||||||
|
// It doesn't allow fine control over the `VerbosityLevel`
|
||||||
|
// and provides only 0 and 1 options (which corresponds to the `verbose` argument).
|
||||||
|
// Use DefaultEventHandler{} initializer directly.
|
||||||
func NewDefaultEventHandler(out io.Writer, descSource DescriptorSource, formatter Formatter, verbose bool) *DefaultEventHandler {
|
func NewDefaultEventHandler(out io.Writer, descSource DescriptorSource, formatter Formatter, verbose bool) *DefaultEventHandler {
|
||||||
|
verbosityLevel := 0
|
||||||
|
if verbose {
|
||||||
|
verbosityLevel = 1
|
||||||
|
}
|
||||||
return &DefaultEventHandler{
|
return &DefaultEventHandler{
|
||||||
out: out,
|
Out: out,
|
||||||
descSource: descSource,
|
Formatter: formatter,
|
||||||
formatter: formatter,
|
VerbosityLevel: verbosityLevel,
|
||||||
verbose: verbose,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ InvocationEventHandler = (*DefaultEventHandler)(nil)
|
var _ InvocationEventHandler = (*DefaultEventHandler)(nil)
|
||||||
|
|
||||||
func (h *DefaultEventHandler) OnResolveMethod(md *desc.MethodDescriptor) {
|
func (h *DefaultEventHandler) OnResolveMethod(md *desc.MethodDescriptor) {
|
||||||
if h.verbose {
|
if h.VerbosityLevel > 0 {
|
||||||
txt, err := GetDescriptorText(md, h.descSource)
|
txt, err := GetDescriptorText(md, nil)
|
||||||
if err == 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) {
|
func (h *DefaultEventHandler) OnSendHeaders(md metadata.MD) {
|
||||||
if h.verbose {
|
if h.VerbosityLevel > 0 {
|
||||||
fmt.Fprintf(h.out, "\nRequest metadata to send:\n%s\n", MetadataToString(md))
|
fmt.Fprintf(h.Out, "\nRequest metadata to send:\n%s\n", MetadataToString(md))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *DefaultEventHandler) OnReceiveHeaders(md metadata.MD) {
|
func (h *DefaultEventHandler) OnReceiveHeaders(md metadata.MD) {
|
||||||
if h.verbose {
|
if h.VerbosityLevel > 0 {
|
||||||
fmt.Fprintf(h.out, "\nResponse headers received:\n%s\n", MetadataToString(md))
|
fmt.Fprintf(h.Out, "\nResponse headers received:\n%s\n", MetadataToString(md))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *DefaultEventHandler) OnReceiveResponse(resp proto.Message) {
|
func (h *DefaultEventHandler) OnReceiveResponse(resp proto.Message) {
|
||||||
h.NumResponses++
|
h.NumResponses++
|
||||||
if h.verbose {
|
if h.VerbosityLevel > 1 {
|
||||||
fmt.Fprint(h.out, "\nResponse contents:\n")
|
fmt.Fprintf(h.Out, "\nEstimated response size: %d bytes\n", proto.Size(resp))
|
||||||
}
|
}
|
||||||
if respStr, err := h.formatter(resp); err != nil {
|
if h.VerbosityLevel > 0 {
|
||||||
fmt.Fprintf(h.out, "Failed to format response message %d: %v\n", h.NumResponses, err)
|
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 {
|
} else {
|
||||||
fmt.Fprintln(h.out, respStr)
|
fmt.Fprintln(h.Out, respStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *DefaultEventHandler) OnReceiveTrailers(stat *status.Status, md metadata.MD) {
|
func (h *DefaultEventHandler) OnReceiveTrailers(stat *status.Status, md metadata.MD) {
|
||||||
h.Status = stat
|
h.Status = stat
|
||||||
if h.verbose {
|
if h.VerbosityLevel > 0 {
|
||||||
fmt.Fprintf(h.out, "\nResponse trailers received:\n%s\n", MetadataToString(md))
|
fmt.Fprintf(h.Out, "\nResponse trailers received:\n%s\n", MetadataToString(md))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,15 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/golang/protobuf/jsonpb"
|
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
|
||||||
"github.com/golang/protobuf/ptypes/struct"
|
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||||
"github.com/jhump/protoreflect/desc"
|
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRequestParser(t *testing.T) {
|
func TestRequestParser(t *testing.T) {
|
||||||
source, err := DescriptorSourceFromProtoSets("testing/example.protoset")
|
source, err := DescriptorSourceFromProtoSets("internal/testing/example.protoset")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create descriptor source: %v", err)
|
t.Fatalf("failed to create descriptor source: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +69,7 @@ func TestRequestParser(t *testing.T) {
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
name := fmt.Sprintf("#%d, %s, %d message(s)", i+1, tc.format, len(tc.expectedOutput))
|
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 {
|
if err != nil {
|
||||||
t.Errorf("Failed to create parser and formatter: %v", err)
|
t.Errorf("Failed to create parser and formatter: %v", err)
|
||||||
continue
|
continue
|
||||||
|
|
@ -97,7 +97,7 @@ func TestRequestParser(t *testing.T) {
|
||||||
// Handler prints response data (and headers/trailers in verbose mode).
|
// 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.
|
// This verifies that we get the right output in both JSON and proto text modes.
|
||||||
func TestHandler(t *testing.T) {
|
func TestHandler(t *testing.T) {
|
||||||
source, err := DescriptorSourceFromProtoSets("testing/example.protoset")
|
source, err := DescriptorSourceFromProtoSets("internal/testing/example.protoset")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create descriptor source: %v", err)
|
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 _, format := range []Format{FormatJSON, FormatText} {
|
||||||
for _, numMessages := range []int{1, 3} {
|
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)
|
name := fmt.Sprintf("%s, %d message(s)", format, numMessages)
|
||||||
if verbose {
|
if verbosityLevel > 0 {
|
||||||
name += ", verbose"
|
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 {
|
if err != nil {
|
||||||
t.Errorf("Failed to create parser and formatter: %v", err)
|
t.Errorf("Failed to create parser and formatter: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
h := NewDefaultEventHandler(&buf, source, formatter, verbose)
|
h := &DefaultEventHandler{
|
||||||
|
Out: &buf,
|
||||||
|
Formatter: formatter,
|
||||||
|
VerbosityLevel: verbosityLevel,
|
||||||
|
}
|
||||||
|
|
||||||
h.OnResolveMethod(md)
|
h.OnResolveMethod(md)
|
||||||
h.OnSendHeaders(reqHeaders)
|
h.OnSendHeaders(reqHeaders)
|
||||||
|
|
@ -148,6 +154,9 @@ func TestHandler(t *testing.T) {
|
||||||
expectedOutput += verbosePrefix
|
expectedOutput += verbosePrefix
|
||||||
}
|
}
|
||||||
for i := 0; i < numMessages; i++ {
|
for i := 0; i < numMessages; i++ {
|
||||||
|
if verbosityLevel > 1 {
|
||||||
|
expectedOutput += verboseResponseSize
|
||||||
|
}
|
||||||
if verbose {
|
if verbose {
|
||||||
expectedOutput += verboseResponseHeader
|
expectedOutput += verboseResponseHeader
|
||||||
}
|
}
|
||||||
|
|
@ -226,6 +235,9 @@ Response trailers received:
|
||||||
a: 1
|
a: 1
|
||||||
b: 2
|
b: 2
|
||||||
c: 3
|
c: 3
|
||||||
|
`
|
||||||
|
verboseResponseSize = `
|
||||||
|
Estimated response size: 100 bytes
|
||||||
`
|
`
|
||||||
verboseResponseHeader = `
|
verboseResponseHeader = `
|
||||||
Response contents:
|
Response contents:
|
||||||
|
|
|
||||||
32
go.mod
32
go.mod
|
|
@ -1,8 +1,32 @@
|
||||||
module github.com/fullstorydev/grpcurl
|
module github.com/fullstorydev/grpcurl
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
|
|
||||||
|
toolchain go1.24.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang/protobuf v1.3.1
|
github.com/golang/protobuf v1.5.4
|
||||||
github.com/jhump/protoreflect v1.5.0
|
github.com/jhump/protoreflect v1.18.0
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a
|
google.golang.org/grpc v1.66.2
|
||||||
google.golang.org/grpc v1.21.0
|
google.golang.org/protobuf v1.36.11
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
cel.dev/expr v0.15.0 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect
|
||||||
|
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 // indirect
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect
|
||||||
|
github.com/jhump/protoreflect/v2 v2.0.0-beta.1 // indirect
|
||||||
|
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect
|
||||||
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||||
|
golang.org/x/net v0.38.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.27.0 // indirect
|
||||||
|
golang.org/x/sync v0.12.0 // indirect
|
||||||
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
|
golang.org/x/text v0.23.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
86
go.sum
86
go.sum
|
|
@ -1,30 +1,56 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/jhump/protoreflect v1.5.0 h1:NgpVT+dX71c8hZnxHof2M7QDK7QtohIJ7DYycjnkyfc=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/jhump/protoreflect v1.5.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
github.com/jhump/protoreflect v1.18.0 h1:TOz0MSR/0JOZ5kECB/0ufGnC2jdsgZ123Rd/k4Z5/2w=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
github.com/jhump/protoreflect v1.18.0/go.mod h1:ezWcltJIVF4zYdIFM+D/sHV4Oh5LNU08ORzCGfwvTz8=
|
||||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
github.com/jhump/protoreflect/v2 v2.0.0-beta.1 h1:Dw1rslK/VotaUGYsv53XVWITr+5RCPXfvvlGrM/+B6w=
|
||||||
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
github.com/jhump/protoreflect/v2 v2.0.0-beta.1/go.mod h1:D9LBEowZyv8/iSu97FU2zmXG3JxVTmNw21mu63niFzU=
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||||
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||||
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||||
|
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
|
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||||
|
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||||
|
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
|
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
||||||
|
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
|
||||||
|
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||||
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
||||||
181
grpcurl.go
181
grpcurl.go
|
|
@ -8,30 +8,34 @@ package grpcurl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||||
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||||
"github.com/golang/protobuf/ptypes"
|
|
||||||
"github.com/golang/protobuf/ptypes/empty"
|
|
||||||
"github.com/golang/protobuf/ptypes/struct"
|
|
||||||
"github.com/jhump/protoreflect/desc"
|
|
||||||
"github.com/jhump/protoreflect/desc/protoprint"
|
"github.com/jhump/protoreflect/desc/protoprint"
|
||||||
"github.com/jhump/protoreflect/dynamic"
|
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
xdsCredentials "google.golang.org/grpc/credentials/xds"
|
||||||
|
_ "google.golang.org/grpc/health" // import grpc/health to enable transparent client side checking
|
||||||
"google.golang.org/grpc/metadata"
|
"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
|
// ListServices uses the given descriptor source to return a sorted list of fully-qualified
|
||||||
|
|
@ -327,7 +331,7 @@ func fetchAllExtensions(source DescriptorSource, ext *dynamic.ExtensionRegistry,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fullConvertToDynamic attempts to convert the given message to a dynamic message as well
|
// fullyConvertToDynamic attempts to convert the given message to a dynamic message as well
|
||||||
// as any nested messages it may contain as field values. If the given message factory has
|
// as any nested messages it may contain as field values. If the given message factory has
|
||||||
// extensions registered that were not known when the given message was parsed, this effectively
|
// extensions registered that were not known when the given message was parsed, this effectively
|
||||||
// allows re-parsing to identify those extensions.
|
// allows re-parsing to identify those extensions.
|
||||||
|
|
@ -405,8 +409,9 @@ func makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescriptor) pr
|
||||||
case "google.protobuf.Any":
|
case "google.protobuf.Any":
|
||||||
// empty type URL is not allowed by JSON representation
|
// empty type URL is not allowed by JSON representation
|
||||||
// so we must give it a dummy type
|
// so we must give it a dummy type
|
||||||
msg, _ := ptypes.MarshalAny(&empty.Empty{})
|
var anyVal anypb.Any
|
||||||
return msg
|
_ = anypb.MarshalFrom(&anyVal, &emptypb.Empty{}, protov2.MarshalOptions{})
|
||||||
|
return &anyVal
|
||||||
case "google.protobuf.Value":
|
case "google.protobuf.Value":
|
||||||
// unset kind is not allowed by JSON representation
|
// unset kind is not allowed by JSON representation
|
||||||
// so we must give it something
|
// so we must give it something
|
||||||
|
|
@ -446,11 +451,9 @@ func makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescriptor) pr
|
||||||
dm := dynamic.NewMessage(md)
|
dm := dynamic.NewMessage(md)
|
||||||
|
|
||||||
// if the message is a recursive structure, we don't want to blow the stack
|
// if the message is a recursive structure, we don't want to blow the stack
|
||||||
for _, seen := range path {
|
if slices.Contains(path, md) {
|
||||||
if seen == md {
|
// already visited this type; avoid infinite recursion
|
||||||
// already visited this type; avoid infinite recursion
|
return dm
|
||||||
return dm
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
path = append(path, dm.GetMessageDescriptor())
|
path = append(path, dm.GetMessageDescriptor())
|
||||||
|
|
||||||
|
|
@ -461,42 +464,42 @@ func makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescriptor) pr
|
||||||
for _, fd := range dm.GetMessageDescriptor().GetFields() {
|
for _, fd := range dm.GetMessageDescriptor().GetFields() {
|
||||||
if fd.IsRepeated() {
|
if fd.IsRepeated() {
|
||||||
switch fd.GetType() {
|
switch fd.GetType() {
|
||||||
case descpb.FieldDescriptorProto_TYPE_FIXED32,
|
case descriptorpb.FieldDescriptorProto_TYPE_FIXED32,
|
||||||
descpb.FieldDescriptorProto_TYPE_UINT32:
|
descriptorpb.FieldDescriptorProto_TYPE_UINT32:
|
||||||
dm.AddRepeatedField(fd, uint32(0))
|
dm.AddRepeatedField(fd, uint32(0))
|
||||||
|
|
||||||
case descpb.FieldDescriptorProto_TYPE_SFIXED32,
|
case descriptorpb.FieldDescriptorProto_TYPE_SFIXED32,
|
||||||
descpb.FieldDescriptorProto_TYPE_SINT32,
|
descriptorpb.FieldDescriptorProto_TYPE_SINT32,
|
||||||
descpb.FieldDescriptorProto_TYPE_INT32,
|
descriptorpb.FieldDescriptorProto_TYPE_INT32,
|
||||||
descpb.FieldDescriptorProto_TYPE_ENUM:
|
descriptorpb.FieldDescriptorProto_TYPE_ENUM:
|
||||||
dm.AddRepeatedField(fd, int32(0))
|
dm.AddRepeatedField(fd, int32(0))
|
||||||
|
|
||||||
case descpb.FieldDescriptorProto_TYPE_FIXED64,
|
case descriptorpb.FieldDescriptorProto_TYPE_FIXED64,
|
||||||
descpb.FieldDescriptorProto_TYPE_UINT64:
|
descriptorpb.FieldDescriptorProto_TYPE_UINT64:
|
||||||
dm.AddRepeatedField(fd, uint64(0))
|
dm.AddRepeatedField(fd, uint64(0))
|
||||||
|
|
||||||
case descpb.FieldDescriptorProto_TYPE_SFIXED64,
|
case descriptorpb.FieldDescriptorProto_TYPE_SFIXED64,
|
||||||
descpb.FieldDescriptorProto_TYPE_SINT64,
|
descriptorpb.FieldDescriptorProto_TYPE_SINT64,
|
||||||
descpb.FieldDescriptorProto_TYPE_INT64:
|
descriptorpb.FieldDescriptorProto_TYPE_INT64:
|
||||||
dm.AddRepeatedField(fd, int64(0))
|
dm.AddRepeatedField(fd, int64(0))
|
||||||
|
|
||||||
case descpb.FieldDescriptorProto_TYPE_STRING:
|
case descriptorpb.FieldDescriptorProto_TYPE_STRING:
|
||||||
dm.AddRepeatedField(fd, "")
|
dm.AddRepeatedField(fd, "")
|
||||||
|
|
||||||
case descpb.FieldDescriptorProto_TYPE_BYTES:
|
case descriptorpb.FieldDescriptorProto_TYPE_BYTES:
|
||||||
dm.AddRepeatedField(fd, []byte{})
|
dm.AddRepeatedField(fd, []byte{})
|
||||||
|
|
||||||
case descpb.FieldDescriptorProto_TYPE_BOOL:
|
case descriptorpb.FieldDescriptorProto_TYPE_BOOL:
|
||||||
dm.AddRepeatedField(fd, false)
|
dm.AddRepeatedField(fd, false)
|
||||||
|
|
||||||
case descpb.FieldDescriptorProto_TYPE_FLOAT:
|
case descriptorpb.FieldDescriptorProto_TYPE_FLOAT:
|
||||||
dm.AddRepeatedField(fd, float32(0))
|
dm.AddRepeatedField(fd, float32(0))
|
||||||
|
|
||||||
case descpb.FieldDescriptorProto_TYPE_DOUBLE:
|
case descriptorpb.FieldDescriptorProto_TYPE_DOUBLE:
|
||||||
dm.AddRepeatedField(fd, float64(0))
|
dm.AddRepeatedField(fd, float64(0))
|
||||||
|
|
||||||
case descpb.FieldDescriptorProto_TYPE_MESSAGE,
|
case descriptorpb.FieldDescriptorProto_TYPE_MESSAGE,
|
||||||
descpb.FieldDescriptorProto_TYPE_GROUP:
|
descriptorpb.FieldDescriptorProto_TYPE_GROUP:
|
||||||
dm.AddRepeatedField(fd, makeTemplate(fd.GetMessageType(), path))
|
dm.AddRepeatedField(fd, makeTemplate(fd.GetMessageType(), path))
|
||||||
}
|
}
|
||||||
} else if fd.GetMessageType() != nil {
|
} else if fd.GetMessageType() != nil {
|
||||||
|
|
@ -506,11 +509,25 @@ func makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescriptor) pr
|
||||||
return dm
|
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
|
// 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
|
// 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.
|
// 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
|
var tlsConf tls.Config
|
||||||
|
|
||||||
if clientCertFile != "" {
|
if clientCertFile != "" {
|
||||||
|
|
@ -527,7 +544,7 @@ func ClientTransportCredentials(insecureSkipVerify bool, cacertFile, clientCertF
|
||||||
} else if cacertFile != "" {
|
} else if cacertFile != "" {
|
||||||
// Create a certificate pool from the certificate authority
|
// Create a certificate pool from the certificate authority
|
||||||
certPool := x509.NewCertPool()
|
certPool := x509.NewCertPool()
|
||||||
ca, err := ioutil.ReadFile(cacertFile)
|
ca, err := os.ReadFile(cacertFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read ca certificate: %v", err)
|
return nil, fmt.Errorf("could not read ca certificate: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -540,7 +557,7 @@ func ClientTransportCredentials(insecureSkipVerify bool, cacertFile, clientCertF
|
||||||
tlsConf.RootCAs = certPool
|
tlsConf.RootCAs = certPool
|
||||||
}
|
}
|
||||||
|
|
||||||
return credentials.NewTLS(&tlsConf), nil
|
return &tlsConf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerTransportCredentials builds transport credentials for a gRPC server using the
|
// ServerTransportCredentials builds transport credentials for a gRPC server using the
|
||||||
|
|
@ -564,7 +581,7 @@ func ServerTransportCredentials(cacertFile, serverCertFile, serverKeyFile string
|
||||||
if cacertFile != "" {
|
if cacertFile != "" {
|
||||||
// Create a certificate pool from the certificate authority
|
// Create a certificate pool from the certificate authority
|
||||||
certPool := x509.NewCertPool()
|
certPool := x509.NewCertPool()
|
||||||
ca, err := ioutil.ReadFile(cacertFile)
|
ca, err := os.ReadFile(cacertFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read ca certificate: %v", err)
|
return nil, fmt.Errorf("could not read ca certificate: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -591,7 +608,24 @@ func ServerTransportCredentials(cacertFile, serverCertFile, serverKeyFile string
|
||||||
// BlockingDial is a helper method to dial the given address, using optional TLS credentials,
|
// BlockingDial is a helper method to dial the given address, using optional TLS credentials,
|
||||||
// and blocking until the returned connection is ready. If the given credentials are nil, the
|
// and blocking until the returned connection is ready. If the given credentials are nil, the
|
||||||
// connection will be insecure (plain-text).
|
// connection will be insecure (plain-text).
|
||||||
|
// The network parameter should be left empty in most cases when your address is a RFC 3986
|
||||||
|
// compliant URI. The resolver from grpc-go will resolve the correct network type.
|
||||||
func BlockingDial(ctx context.Context, network, address string, creds credentials.TransportCredentials, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
func BlockingDial(ctx context.Context, network, address string, creds credentials.TransportCredentials, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||||
|
if creds == nil {
|
||||||
|
creds = insecure.NewCredentials()
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if strings.HasPrefix(address, "xds:///") {
|
||||||
|
// The xds:/// prefix is used to signal to the gRPC client to use an xDS server to resolve the
|
||||||
|
// target. The relevant credentials will be automatically pulled from the GRPC_XDS_BOOTSTRAP or
|
||||||
|
// GRPC_XDS_BOOTSTRAP_CONFIG env vars.
|
||||||
|
creds, err = xdsCredentials.NewClientCredentials(xdsCredentials.ClientOptions{FallbackCreds: creds})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// grpc.Dial doesn't provide any information on permanent connection errors (like
|
// grpc.Dial doesn't provide any information on permanent connection errors (like
|
||||||
// TLS handshake failures). So in order to provide good error messages, we need a
|
// TLS handshake failures). So in order to provide good error messages, we need a
|
||||||
// custom dialer that can provide that info. That means we manage the TLS handshake.
|
// custom dialer that can provide that info. That means we manage the TLS handshake.
|
||||||
|
|
@ -605,33 +639,53 @@ func BlockingDial(ctx context.Context, network, address string, creds credential
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dialer := func(ctx context.Context, address string) (net.Conn, error) {
|
// custom credentials and dialer will notify on error via the
|
||||||
conn, err := (&net.Dialer{}).DialContext(ctx, network, address)
|
// writeResult function
|
||||||
if err != nil {
|
creds = &errSignalingCreds{
|
||||||
writeResult(err)
|
TransportCredentials: creds,
|
||||||
return nil, err
|
writeResult: writeResult,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch network {
|
||||||
|
case "":
|
||||||
|
// no-op, use address as-is
|
||||||
|
case "tcp":
|
||||||
|
if strings.HasPrefix(address, "unix://") {
|
||||||
|
return nil, fmt.Errorf("tcp network type cannot use unix address %s", address)
|
||||||
}
|
}
|
||||||
if creds != nil {
|
case "unix":
|
||||||
conn, _, err = creds.ClientHandshake(ctx, address, conn)
|
if !strings.HasPrefix(address, "unix://") {
|
||||||
|
// prepend unix:// to the address if it's not already there
|
||||||
|
// this is to maintain backwards compatibility because the custom dialer is replaced by
|
||||||
|
// the default dialer in grpc-go.
|
||||||
|
// https://github.com/fullstorydev/grpcurl/pull/480
|
||||||
|
address = "unix://" + address
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// custom dialer for other networks
|
||||||
|
dialer := func(ctx context.Context, address string) (net.Conn, error) {
|
||||||
|
conn, err := (&net.Dialer{}).DialContext(ctx, network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// capture the error so we can provide a better message
|
||||||
writeResult(err)
|
writeResult(err)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
return conn, err
|
||||||
}
|
}
|
||||||
return conn, nil
|
opts = append([]grpc.DialOption{grpc.WithContextDialer(dialer)}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even with grpc.FailOnNonTempDialError, this call will usually timeout in
|
// 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
|
// 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
|
// 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() {
|
go func() {
|
||||||
opts = append(opts,
|
// We put grpc.FailOnNonTempDialError *before* the explicitly provided
|
||||||
grpc.WithBlock(),
|
// options so that it could be overridden.
|
||||||
grpc.FailOnNonTempDialError(true),
|
opts = append([]grpc.DialOption{grpc.FailOnNonTempDialError(true)}, opts...)
|
||||||
grpc.WithContextDialer(dialer),
|
// But we don't want caller to be able to override these two, so we put
|
||||||
grpc.WithInsecure(), // we are handling TLS, so tell grpc not to
|
// them *after* the explicitly provided options.
|
||||||
)
|
opts = append(opts, grpc.WithBlock(), grpc.WithTransportCredentials(creds))
|
||||||
|
|
||||||
conn, err := grpc.DialContext(ctx, address, opts...)
|
conn, err := grpc.DialContext(ctx, address, opts...)
|
||||||
var res interface{}
|
var res interface{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -652,3 +706,18 @@ func BlockingDial(ctx context.Context, network, address string, creds credential
|
||||||
return nil, ctx.Err()
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
175
grpcurl_test.go
175
grpcurl_test.go
|
|
@ -1,6 +1,7 @@
|
||||||
package grpcurl_test
|
package grpcurl_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -11,22 +12,20 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/jsonpb"
|
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||||
jsonpbtest "github.com/golang/protobuf/jsonpb/jsonpb_test_proto"
|
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||||
"github.com/jhump/protoreflect/desc"
|
|
||||||
"github.com/jhump/protoreflect/grpcreflect"
|
"github.com/jhump/protoreflect/grpcreflect"
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/interop/grpc_testing"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/reflection"
|
"google.golang.org/grpc/reflection"
|
||||||
reflectpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
|
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
. "github.com/fullstorydev/grpcurl"
|
. "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 (
|
var (
|
||||||
|
|
@ -52,18 +51,18 @@ type descSourceCase struct {
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
var err error
|
var err error
|
||||||
sourceProtoset, err = DescriptorSourceFromProtoSets("testing/test.protoset")
|
sourceProtoset, err = DescriptorSourceFromProtoSets("internal/testing/test.protoset")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
sourceProtoFiles, err = DescriptorSourceFromProtoFiles(nil, "testing/test.proto")
|
sourceProtoFiles, err = DescriptorSourceFromProtoFiles([]string{"internal/testing"}, "test.proto")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a server that includes the reflection service
|
// Create a server that includes the reflection service
|
||||||
svrReflect := grpc.NewServer()
|
svrReflect := grpc.NewServer()
|
||||||
grpc_testing.RegisterTestServiceServer(svrReflect, grpcurl_testing.TestServer{})
|
grpcurl_testing.RegisterTestServiceServer(svrReflect, grpcurl_testing.TestServer{})
|
||||||
reflection.Register(svrReflect)
|
reflection.Register(svrReflect)
|
||||||
var portReflect int
|
var portReflect int
|
||||||
if l, err := net.Listen("tcp", "127.0.0.1:0"); err != nil {
|
if l, err := net.Listen("tcp", "127.0.0.1:0"); err != nil {
|
||||||
|
|
@ -78,18 +77,18 @@ func TestMain(m *testing.M) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if ccReflect, err = grpc.DialContext(ctx, fmt.Sprintf("127.0.0.1:%d", portReflect),
|
if ccReflect, err = grpc.DialContext(ctx, fmt.Sprintf("127.0.0.1:%d", portReflect),
|
||||||
grpc.WithInsecure(), grpc.WithBlock()); err != nil {
|
grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
defer ccReflect.Close()
|
defer ccReflect.Close()
|
||||||
refClient := grpcreflect.NewClient(context.Background(), reflectpb.NewServerReflectionClient(ccReflect))
|
refClient := grpcreflect.NewClientAuto(context.Background(), ccReflect)
|
||||||
defer refClient.Reset()
|
defer refClient.Reset()
|
||||||
|
|
||||||
sourceReflect = DescriptorSourceFromServer(context.Background(), refClient)
|
sourceReflect = DescriptorSourceFromServer(context.Background(), refClient)
|
||||||
|
|
||||||
// Also create a server that does *not* include the reflection service
|
// Also create a server that does *not* include the reflection service
|
||||||
svrProtoset := grpc.NewServer()
|
svrProtoset := grpc.NewServer()
|
||||||
grpc_testing.RegisterTestServiceServer(svrProtoset, grpcurl_testing.TestServer{})
|
grpcurl_testing.RegisterTestServiceServer(svrProtoset, grpcurl_testing.TestServer{})
|
||||||
var portProtoset int
|
var portProtoset int
|
||||||
if l, err := net.Listen("tcp", "127.0.0.1:0"); err != nil {
|
if l, err := net.Listen("tcp", "127.0.0.1:0"); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -103,7 +102,7 @@ func TestMain(m *testing.M) {
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if ccNoReflect, err = grpc.DialContext(ctx, fmt.Sprintf("127.0.0.1:%d", portProtoset),
|
if ccNoReflect, err = grpc.DialContext(ctx, fmt.Sprintf("127.0.0.1:%d", portProtoset),
|
||||||
grpc.WithInsecure(), grpc.WithBlock()); err != nil {
|
grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
defer ccNoReflect.Close()
|
defer ccNoReflect.Close()
|
||||||
|
|
@ -118,7 +117,7 @@ func TestMain(m *testing.M) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerDoesNotSupportReflection(t *testing.T) {
|
func TestServerDoesNotSupportReflection(t *testing.T) {
|
||||||
refClient := grpcreflect.NewClient(context.Background(), reflectpb.NewServerReflectionClient(ccNoReflect))
|
refClient := grpcreflect.NewClientAuto(context.Background(), ccNoReflect)
|
||||||
defer refClient.Reset()
|
defer refClient.Reset()
|
||||||
|
|
||||||
refSource := DescriptorSourceFromServer(context.Background(), refClient)
|
refSource := DescriptorSourceFromServer(context.Background(), refClient)
|
||||||
|
|
@ -141,7 +140,7 @@ func TestServerDoesNotSupportReflection(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProtosetWithImports(t *testing.T) {
|
func TestProtosetWithImports(t *testing.T) {
|
||||||
sourceProtoset, err := DescriptorSourceFromProtoSets("testing/example.protoset")
|
sourceProtoset, err := DescriptorSourceFromProtoSets("internal/testing/example.protoset")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to load protoset: %v", err)
|
t.Fatalf("failed to load protoset: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -178,11 +177,11 @@ func doTestListServices(t *testing.T, source DescriptorSource, includeReflection
|
||||||
var expected []string
|
var expected []string
|
||||||
if includeReflection {
|
if includeReflection {
|
||||||
// when using server reflection, we see the TestService as well as the ServerReflection service
|
// 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.v1.ServerReflection", "grpc.reflection.v1alpha.ServerReflection", "testing.TestService"}
|
||||||
} else {
|
} else {
|
||||||
// without reflection, we see all services defined in the same test.proto file, which is the
|
// without reflection, we see all services defined in the same test.proto file, which is the
|
||||||
// TestService as well as UnimplementedService
|
// TestService as well as UnimplementedService
|
||||||
expected = []string{"grpc.testing.TestService", "grpc.testing.UnimplementedService"}
|
expected = []string{"testing.TestService", "testing.UnimplementedService"}
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(expected, names) {
|
if !reflect.DeepEqual(expected, names) {
|
||||||
t.Errorf("ListServices returned wrong results: wanted %v, got %v", 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) {
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to list methods for TestService: %v", err)
|
t.Fatalf("failed to list methods for TestService: %v", err)
|
||||||
}
|
}
|
||||||
expected := []string{
|
expected := []string{
|
||||||
"grpc.testing.TestService.EmptyCall",
|
"testing.TestService.EmptyCall",
|
||||||
"grpc.testing.TestService.FullDuplexCall",
|
"testing.TestService.FullDuplexCall",
|
||||||
"grpc.testing.TestService.HalfDuplexCall",
|
"testing.TestService.HalfDuplexCall",
|
||||||
"grpc.testing.TestService.StreamingInputCall",
|
"testing.TestService.StreamingInputCall",
|
||||||
"grpc.testing.TestService.StreamingOutputCall",
|
"testing.TestService.StreamingOutputCall",
|
||||||
"grpc.testing.TestService.UnaryCall",
|
"testing.TestService.UnaryCall",
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(expected, names) {
|
if !reflect.DeepEqual(expected, names) {
|
||||||
t.Errorf("ListMethods returned wrong results: wanted %v, got %v", expected, names)
|
t.Errorf("ListMethods returned wrong results: wanted %v, got %v", expected, names)
|
||||||
|
|
@ -216,19 +215,19 @@ func doTestListMethods(t *testing.T, source DescriptorSource, includeReflection
|
||||||
|
|
||||||
if includeReflection {
|
if includeReflection {
|
||||||
// when using server reflection, we see the TestService as well as the ServerReflection service
|
// when using server reflection, we see the TestService as well as the ServerReflection service
|
||||||
names, err = ListMethods(source, "grpc.reflection.v1alpha.ServerReflection")
|
names, err = ListMethods(source, "grpc.reflection.v1.ServerReflection")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to list methods for ServerReflection: %v", err)
|
t.Fatalf("failed to list methods for ServerReflection: %v", err)
|
||||||
}
|
}
|
||||||
expected = []string{"grpc.reflection.v1alpha.ServerReflection.ServerReflectionInfo"}
|
expected = []string{"grpc.reflection.v1.ServerReflection.ServerReflectionInfo"}
|
||||||
} else {
|
} else {
|
||||||
// without reflection, we see all services defined in the same test.proto file, which is the
|
// without reflection, we see all services defined in the same test.proto file, which is the
|
||||||
// TestService as well as UnimplementedService
|
// TestService as well as UnimplementedService
|
||||||
names, err = ListMethods(source, "grpc.testing.UnimplementedService")
|
names, err = ListMethods(source, "testing.UnimplementedService")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to list methods for ServerReflection: %v", err)
|
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) {
|
if !reflect.DeepEqual(expected, names) {
|
||||||
t.Errorf("ListMethods returned wrong results: wanted %v, got %v", expected, names)
|
t.Errorf("ListMethods returned wrong results: wanted %v, got %v", expected, names)
|
||||||
|
|
@ -242,10 +241,10 @@ func doTestListMethods(t *testing.T, source DescriptorSource, includeReflection
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAllFiles(t *testing.T) {
|
func TestGetAllFiles(t *testing.T) {
|
||||||
expectedFiles := []string{"testing/test.proto"}
|
expectedFiles := []string{"test.proto"}
|
||||||
// server reflection picks up filename from linked in Go package,
|
expectedFilesWithReflection := []string{
|
||||||
// which indicates "grpc_testing/test.proto", not our local copy.
|
"grpc/reflection/v1/reflection.proto", "grpc/reflection/v1alpha/reflection.proto", "test.proto",
|
||||||
expectedFilesWithReflection := []string{"grpc_reflection_v1alpha/reflection.proto", "grpc_testing/test.proto"}
|
}
|
||||||
|
|
||||||
for _, ds := range descSources {
|
for _, ds := range descSources {
|
||||||
t.Run(ds.name, func(t *testing.T) {
|
t.Run(ds.name, func(t *testing.T) {
|
||||||
|
|
@ -254,22 +253,26 @@ func TestGetAllFiles(t *testing.T) {
|
||||||
t.Fatalf("failed to get all files: %v", err)
|
t.Fatalf("failed to get all files: %v", err)
|
||||||
}
|
}
|
||||||
names := fileNames(files)
|
names := fileNames(files)
|
||||||
expected := expectedFiles
|
match := false
|
||||||
|
var expected []string
|
||||||
if ds.includeRefl {
|
if ds.includeRefl {
|
||||||
expected = expectedFilesWithReflection
|
expected = expectedFilesWithReflection
|
||||||
|
} else {
|
||||||
|
expected = expectedFiles
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(expected, names) {
|
match = reflect.DeepEqual(expected, names)
|
||||||
|
if !match {
|
||||||
t.Errorf("GetAllFiles returned wrong results: wanted %v, got %v", expected, names)
|
t.Errorf("GetAllFiles returned wrong results: wanted %v, got %v", expected, names)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// try cases with more complicated set of files
|
// 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 {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -278,13 +281,13 @@ func TestGetAllFiles(t *testing.T) {
|
||||||
{"proto[b]", otherSourceProtoFiles, false},
|
{"proto[b]", otherSourceProtoFiles, false},
|
||||||
}
|
}
|
||||||
expectedFiles = []string{
|
expectedFiles = []string{
|
||||||
|
"example.proto",
|
||||||
|
"example2.proto",
|
||||||
"google/protobuf/any.proto",
|
"google/protobuf/any.proto",
|
||||||
"google/protobuf/descriptor.proto",
|
"google/protobuf/descriptor.proto",
|
||||||
"google/protobuf/empty.proto",
|
"google/protobuf/empty.proto",
|
||||||
"google/protobuf/timestamp.proto",
|
"google/protobuf/timestamp.proto",
|
||||||
"testing/example.proto",
|
"test.proto",
|
||||||
"testing/example2.proto",
|
|
||||||
"testing/test.proto",
|
|
||||||
}
|
}
|
||||||
for _, ds := range otherDescSources {
|
for _, ds := range otherDescSources {
|
||||||
t.Run(ds.name, func(t *testing.T) {
|
t.Run(ds.name, func(t *testing.T) {
|
||||||
|
|
@ -389,7 +392,7 @@ func TestDescribe(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func doTestDescribe(t *testing.T, source DescriptorSource) {
|
func doTestDescribe(t *testing.T, source DescriptorSource) {
|
||||||
sym := "grpc.testing.TestService.EmptyCall"
|
sym := "testing.TestService.EmptyCall"
|
||||||
dsc, err := source.FindSymbol(sym)
|
dsc, err := source.FindSymbol(sym)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get descriptor for %q: %v", sym, err)
|
t.Fatalf("failed to get descriptor for %q: %v", sym, err)
|
||||||
|
|
@ -400,14 +403,14 @@ func doTestDescribe(t *testing.T, source DescriptorSource) {
|
||||||
txt := proto.MarshalTextString(dsc.AsProto())
|
txt := proto.MarshalTextString(dsc.AsProto())
|
||||||
expected :=
|
expected :=
|
||||||
`name: "EmptyCall"
|
`name: "EmptyCall"
|
||||||
input_type: ".grpc.testing.Empty"
|
input_type: ".testing.Empty"
|
||||||
output_type: ".grpc.testing.Empty"
|
output_type: ".testing.Empty"
|
||||||
`
|
`
|
||||||
if expected != txt {
|
if expected != txt {
|
||||||
t.Errorf("descriptor mismatch: expected %s, got %s", expected, txt)
|
t.Errorf("descriptor mismatch: expected %s, got %s", expected, txt)
|
||||||
}
|
}
|
||||||
|
|
||||||
sym = "grpc.testing.StreamingOutputCallResponse"
|
sym = "testing.StreamingOutputCallResponse"
|
||||||
dsc, err = source.FindSymbol(sym)
|
dsc, err = source.FindSymbol(sym)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get descriptor for %q: %v", sym, err)
|
t.Fatalf("failed to get descriptor for %q: %v", sym, err)
|
||||||
|
|
@ -423,7 +426,7 @@ field: <
|
||||||
number: 1
|
number: 1
|
||||||
label: LABEL_OPTIONAL
|
label: LABEL_OPTIONAL
|
||||||
type: TYPE_MESSAGE
|
type: TYPE_MESSAGE
|
||||||
type_name: ".grpc.testing.Payload"
|
type_name: ".testing.Payload"
|
||||||
json_name: "payload"
|
json_name: "payload"
|
||||||
>
|
>
|
||||||
`
|
`
|
||||||
|
|
@ -479,12 +482,12 @@ func TestUnary(t *testing.T) {
|
||||||
func doTestUnary(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
func doTestUnary(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
||||||
// Success
|
// Success
|
||||||
h := &handler{reqMessages: []string{payload1}}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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 {
|
if h.respMessages[0] != payload1 {
|
||||||
t.Errorf("unexpected response from RPC: expecting %s; got %s", payload1, h.respMessages[0])
|
t.Errorf("unexpected response from RPC: expecting %s; got %s", payload1, h.respMessages[0])
|
||||||
}
|
}
|
||||||
|
|
@ -492,12 +495,12 @@ func doTestUnary(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
||||||
|
|
||||||
// Failure
|
// Failure
|
||||||
h = &handler{reqMessages: []string{payload1}}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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) {
|
func TestClientStream(t *testing.T) {
|
||||||
|
|
@ -511,12 +514,12 @@ func TestClientStream(t *testing.T) {
|
||||||
func doTestClientStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
func doTestClientStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
||||||
// Success
|
// Success
|
||||||
h := &handler{reqMessages: []string{payload1, payload2, payload3}}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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 :=
|
expected :=
|
||||||
`{
|
`{
|
||||||
"aggregatedPayloadSize": 61
|
"aggregatedPayloadSize": 61
|
||||||
|
|
@ -528,21 +531,21 @@ func doTestClientStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSour
|
||||||
|
|
||||||
// Fail fast (server rejects as soon as possible)
|
// Fail fast (server rejects as soon as possible)
|
||||||
h = &handler{reqMessages: []string{payload1, payload2, payload3}}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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)
|
// Fail late (server waits until stream is complete to reject)
|
||||||
h = &handler{reqMessages: []string{payload1, payload2, payload3}}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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) {
|
func TestServerStream(t *testing.T) {
|
||||||
|
|
@ -554,9 +557,9 @@ func TestServerStream(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func doTestServerStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
func doTestServerStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
||||||
req := &grpc_testing.StreamingOutputCallRequest{
|
req := &grpcurl_testing.StreamingOutputCallRequest{
|
||||||
ResponseType: grpc_testing.PayloadType_COMPRESSABLE,
|
ResponseType: grpcurl_testing.PayloadType_COMPRESSABLE,
|
||||||
ResponseParameters: []*grpc_testing.ResponseParameters{
|
ResponseParameters: []*grpcurl_testing.ResponseParameters{
|
||||||
{Size: 10}, {Size: 20}, {Size: 30}, {Size: 40}, {Size: 50},
|
{Size: 10}, {Size: 20}, {Size: 30}, {Size: 40}, {Size: 50},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -567,19 +570,19 @@ func doTestServerStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSour
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
h := &handler{reqMessages: []string{payload}}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
t.Fatalf("unexpected error during RPC: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.check(t, "grpc.testing.TestService.StreamingOutputCall", codes.OK, 1, 5) {
|
if h.check(t, "testing.TestService.StreamingOutputCall", codes.OK, 1, 5) {
|
||||||
resp := &grpc_testing.StreamingOutputCallResponse{}
|
resp := &grpcurl_testing.StreamingOutputCallResponse{}
|
||||||
for i, msg := range h.respMessages {
|
for i, msg := range h.respMessages {
|
||||||
if err := jsonpb.UnmarshalString(msg, resp); err != nil {
|
if err := jsonpb.UnmarshalString(msg, resp); err != nil {
|
||||||
t.Errorf("failed to parse response %d: %v", i+1, err)
|
t.Errorf("failed to parse response %d: %v", i+1, err)
|
||||||
}
|
}
|
||||||
if resp.Payload.GetType() != grpc_testing.PayloadType_COMPRESSABLE {
|
if resp.Payload.GetType() != grpcurl_testing.PayloadType_COMPRESSABLE {
|
||||||
t.Errorf("response %d has wrong payload type; expecting %v, got %v", i, grpc_testing.PayloadType_COMPRESSABLE, resp.Payload.Type)
|
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 {
|
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))
|
t.Errorf("response %d has wrong payload size; expecting %d, got %d", i, (i+1)*10, len(resp.Payload.Body))
|
||||||
|
|
@ -590,21 +593,21 @@ func doTestServerStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSour
|
||||||
|
|
||||||
// Fail fast (server rejects as soon as possible)
|
// Fail fast (server rejects as soon as possible)
|
||||||
h = &handler{reqMessages: []string{payload}}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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)
|
// Fail late (server waits until stream is complete to reject)
|
||||||
h = &handler{reqMessages: []string{payload}}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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) {
|
func TestHalfDuplexStream(t *testing.T) {
|
||||||
|
|
@ -620,12 +623,12 @@ func doTestHalfDuplexStream(t *testing.T, cc *grpc.ClientConn, source Descriptor
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
h := &handler{reqMessages: reqs}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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 {
|
for i, resp := range h.respMessages {
|
||||||
if resp != reqs[i] {
|
if resp != reqs[i] {
|
||||||
t.Errorf("unexpected response %d from RPC:\nexpecting %q\ngot %q", i, reqs[i], resp)
|
t.Errorf("unexpected response %d from RPC:\nexpecting %q\ngot %q", i, reqs[i], resp)
|
||||||
|
|
@ -635,21 +638,21 @@ func doTestHalfDuplexStream(t *testing.T, cc *grpc.ClientConn, source Descriptor
|
||||||
|
|
||||||
// Fail fast (server rejects as soon as possible)
|
// Fail fast (server rejects as soon as possible)
|
||||||
h = &handler{reqMessages: reqs}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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)
|
// Fail late (server waits until stream is complete to reject)
|
||||||
h = &handler{reqMessages: reqs}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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) {
|
func TestFullDuplexStream(t *testing.T) {
|
||||||
|
|
@ -662,11 +665,11 @@ func TestFullDuplexStream(t *testing.T) {
|
||||||
|
|
||||||
func doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
func doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source DescriptorSource) {
|
||||||
reqs := make([]string, 3)
|
reqs := make([]string, 3)
|
||||||
req := &grpc_testing.StreamingOutputCallRequest{
|
req := &grpcurl_testing.StreamingOutputCallRequest{
|
||||||
ResponseType: grpc_testing.PayloadType_RANDOM,
|
ResponseType: grpcurl_testing.PayloadType_RANDOM,
|
||||||
}
|
}
|
||||||
for i := range reqs {
|
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)
|
payload, err := (&jsonpb.Marshaler{}).MarshalToString(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to construct request %d: %v", i, err)
|
t.Fatalf("failed to construct request %d: %v", i, err)
|
||||||
|
|
@ -676,13 +679,13 @@ func doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source Descriptor
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
h := &handler{reqMessages: reqs}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
t.Fatalf("unexpected error during RPC: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.check(t, "grpc.testing.TestService.FullDuplexCall", codes.OK, 3, 6) {
|
if h.check(t, "testing.TestService.FullDuplexCall", codes.OK, 3, 6) {
|
||||||
resp := &grpc_testing.StreamingOutputCallResponse{}
|
resp := &grpcurl_testing.StreamingOutputCallResponse{}
|
||||||
i := 0
|
i := 0
|
||||||
for j := 1; j < 3; j++ {
|
for j := 1; j < 3; j++ {
|
||||||
// three requests
|
// three requests
|
||||||
|
|
@ -692,8 +695,8 @@ func doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source Descriptor
|
||||||
if err := jsonpb.UnmarshalString(msg, resp); err != nil {
|
if err := jsonpb.UnmarshalString(msg, resp); err != nil {
|
||||||
t.Errorf("failed to parse response %d: %v", i+1, err)
|
t.Errorf("failed to parse response %d: %v", i+1, err)
|
||||||
}
|
}
|
||||||
if resp.Payload.GetType() != grpc_testing.PayloadType_RANDOM {
|
if resp.Payload.GetType() != grpcurl_testing.PayloadType_RANDOM {
|
||||||
t.Errorf("response %d has wrong payload type; expecting %v, got %v", i, grpc_testing.PayloadType_RANDOM, resp.Payload.Type)
|
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 {
|
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))
|
t.Errorf("response %d has wrong payload size; expecting %d, got %d", i, (k+1)*10, len(resp.Payload.Body))
|
||||||
|
|
@ -707,21 +710,21 @@ func doTestFullDuplexStream(t *testing.T, cc *grpc.ClientConn, source Descriptor
|
||||||
|
|
||||||
// Fail fast (server rejects as soon as possible)
|
// Fail fast (server rejects as soon as possible)
|
||||||
h = &handler{reqMessages: reqs}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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)
|
// Fail late (server waits until stream is complete to reject)
|
||||||
h = &handler{reqMessages: reqs}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error during RPC: %v", err)
|
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 {
|
type handler struct {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/ptypes"
|
|
||||||
"github.com/golang/protobuf/ptypes/empty"
|
"github.com/golang/protobuf/ptypes/empty"
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// bankServer implements the Bank gRPC service.
|
// bankServer implements the Bank gRPC service.
|
||||||
type bankServer struct {
|
type bankServer struct {
|
||||||
|
UnimplementedBankServer
|
||||||
allAccounts *accounts
|
allAccounts *accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,26 +72,29 @@ func (s *bankServer) GetTransactions(req *GetTransactionsRequest, stream Bank_Ge
|
||||||
|
|
||||||
var start, end time.Time
|
var start, end time.Time
|
||||||
if req.Start != nil {
|
if req.Start != nil {
|
||||||
start, err = ptypes.Timestamp(req.Start)
|
err := req.Start.CheckValid()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
start = req.Start.AsTime()
|
||||||
}
|
}
|
||||||
if req.End != nil {
|
if req.End != nil {
|
||||||
end, err = ptypes.Timestamp(req.End)
|
err := req.End.CheckValid()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
end = req.End.AsTime()
|
||||||
} else {
|
} else {
|
||||||
end = time.Date(9999, 12, 31, 23, 59, 59, 999999999, time.Local)
|
end = time.Date(9999, 12, 31, 23, 59, 59, 999999999, time.Local)
|
||||||
}
|
}
|
||||||
|
|
||||||
txns := acct.getTransactions()
|
txns := acct.getTransactions()
|
||||||
for _, txn := range txns {
|
for _, txn := range txns {
|
||||||
t, err := ptypes.Timestamp(txn.Date)
|
err := txn.Date.CheckValid()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
t := txn.Date.AsTime()
|
||||||
if (t.After(start) || t.Equal(start)) &&
|
if (t.After(start) || t.Equal(start)) &&
|
||||||
(t.Before(end) || t.Equal(end)) {
|
(t.Before(end) || t.Equal(end)) {
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
option go_package = "main";
|
option go_package = ".;main";
|
||||||
|
|
||||||
import "google/protobuf/empty.proto";
|
import "google/protobuf/empty.proto";
|
||||||
import "google/protobuf/timestamp.proto";
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
@ -0,0 +1,374 @@
|
||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
// BankClient is the client API for Bank service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type BankClient interface {
|
||||||
|
// OpenAccount creates an account with the type and given initial deposit
|
||||||
|
// as its balance.
|
||||||
|
OpenAccount(ctx context.Context, in *OpenAccountRequest, opts ...grpc.CallOption) (*Account, error)
|
||||||
|
// CloseAccount closes the indicated account. An account can only be
|
||||||
|
// closed if its balance is zero.
|
||||||
|
CloseAccount(ctx context.Context, in *CloseAccountRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
// GetAccounts lists all accounts for the current customer.
|
||||||
|
GetAccounts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetAccountsResponse, error)
|
||||||
|
// GetTransactions streams all transactions that match the given criteria.
|
||||||
|
// If the given start date is not specified, transactions since beginning
|
||||||
|
// of time are included. Similarly, if the given end date is not specified,
|
||||||
|
// transactions all the way to the presnet are included.
|
||||||
|
GetTransactions(ctx context.Context, in *GetTransactionsRequest, opts ...grpc.CallOption) (Bank_GetTransactionsClient, error)
|
||||||
|
// Deposit increases the balance of an account by depositing funds into it.
|
||||||
|
Deposit(ctx context.Context, in *DepositRequest, opts ...grpc.CallOption) (*BalanceResponse, error)
|
||||||
|
// Withdraw decreases the balance of an account by withdrawing funds from it.
|
||||||
|
Withdraw(ctx context.Context, in *WithdrawRequest, opts ...grpc.CallOption) (*BalanceResponse, error)
|
||||||
|
// Transfer moves money from one account to another. The source and destination
|
||||||
|
// accounts can be with this bank (e.g. "local" account numbers) or can be
|
||||||
|
// external accounts, identified by their ACH routing and account numbers.
|
||||||
|
Transfer(ctx context.Context, in *TransferRequest, opts ...grpc.CallOption) (*TransferResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type bankClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBankClient(cc grpc.ClientConnInterface) BankClient {
|
||||||
|
return &bankClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bankClient) OpenAccount(ctx context.Context, in *OpenAccountRequest, opts ...grpc.CallOption) (*Account, error) {
|
||||||
|
out := new(Account)
|
||||||
|
err := c.cc.Invoke(ctx, "/Bank/OpenAccount", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bankClient) CloseAccount(ctx context.Context, in *CloseAccountRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, "/Bank/CloseAccount", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bankClient) GetAccounts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetAccountsResponse, error) {
|
||||||
|
out := new(GetAccountsResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/Bank/GetAccounts", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bankClient) GetTransactions(ctx context.Context, in *GetTransactionsRequest, opts ...grpc.CallOption) (Bank_GetTransactionsClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, &Bank_ServiceDesc.Streams[0], "/Bank/GetTransactions", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &bankGetTransactionsClient{stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bank_GetTransactionsClient interface {
|
||||||
|
Recv() (*Transaction, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type bankGetTransactionsClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *bankGetTransactionsClient) Recv() (*Transaction, error) {
|
||||||
|
m := new(Transaction)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bankClient) Deposit(ctx context.Context, in *DepositRequest, opts ...grpc.CallOption) (*BalanceResponse, error) {
|
||||||
|
out := new(BalanceResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/Bank/Deposit", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bankClient) Withdraw(ctx context.Context, in *WithdrawRequest, opts ...grpc.CallOption) (*BalanceResponse, error) {
|
||||||
|
out := new(BalanceResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/Bank/Withdraw", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bankClient) Transfer(ctx context.Context, in *TransferRequest, opts ...grpc.CallOption) (*TransferResponse, error) {
|
||||||
|
out := new(TransferResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/Bank/Transfer", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BankServer is the server API for Bank service.
|
||||||
|
// All implementations must embed UnimplementedBankServer
|
||||||
|
// for forward compatibility
|
||||||
|
type BankServer interface {
|
||||||
|
// OpenAccount creates an account with the type and given initial deposit
|
||||||
|
// as its balance.
|
||||||
|
OpenAccount(context.Context, *OpenAccountRequest) (*Account, error)
|
||||||
|
// CloseAccount closes the indicated account. An account can only be
|
||||||
|
// closed if its balance is zero.
|
||||||
|
CloseAccount(context.Context, *CloseAccountRequest) (*emptypb.Empty, error)
|
||||||
|
// GetAccounts lists all accounts for the current customer.
|
||||||
|
GetAccounts(context.Context, *emptypb.Empty) (*GetAccountsResponse, error)
|
||||||
|
// GetTransactions streams all transactions that match the given criteria.
|
||||||
|
// If the given start date is not specified, transactions since beginning
|
||||||
|
// of time are included. Similarly, if the given end date is not specified,
|
||||||
|
// transactions all the way to the presnet are included.
|
||||||
|
GetTransactions(*GetTransactionsRequest, Bank_GetTransactionsServer) error
|
||||||
|
// Deposit increases the balance of an account by depositing funds into it.
|
||||||
|
Deposit(context.Context, *DepositRequest) (*BalanceResponse, error)
|
||||||
|
// Withdraw decreases the balance of an account by withdrawing funds from it.
|
||||||
|
Withdraw(context.Context, *WithdrawRequest) (*BalanceResponse, error)
|
||||||
|
// Transfer moves money from one account to another. The source and destination
|
||||||
|
// accounts can be with this bank (e.g. "local" account numbers) or can be
|
||||||
|
// external accounts, identified by their ACH routing and account numbers.
|
||||||
|
Transfer(context.Context, *TransferRequest) (*TransferResponse, error)
|
||||||
|
mustEmbedUnimplementedBankServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedBankServer must be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedBankServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedBankServer) OpenAccount(context.Context, *OpenAccountRequest) (*Account, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method OpenAccount not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedBankServer) CloseAccount(context.Context, *CloseAccountRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method CloseAccount not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedBankServer) GetAccounts(context.Context, *emptypb.Empty) (*GetAccountsResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetAccounts not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedBankServer) GetTransactions(*GetTransactionsRequest, Bank_GetTransactionsServer) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method GetTransactions not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedBankServer) Deposit(context.Context, *DepositRequest) (*BalanceResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method Deposit not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedBankServer) Withdraw(context.Context, *WithdrawRequest) (*BalanceResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method Withdraw not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedBankServer) Transfer(context.Context, *TransferRequest) (*TransferResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method Transfer not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedBankServer) mustEmbedUnimplementedBankServer() {}
|
||||||
|
|
||||||
|
// UnsafeBankServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to BankServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeBankServer interface {
|
||||||
|
mustEmbedUnimplementedBankServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterBankServer(s grpc.ServiceRegistrar, srv BankServer) {
|
||||||
|
s.RegisterService(&Bank_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Bank_OpenAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(OpenAccountRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(BankServer).OpenAccount(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/Bank/OpenAccount",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(BankServer).OpenAccount(ctx, req.(*OpenAccountRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Bank_CloseAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(CloseAccountRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(BankServer).CloseAccount(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/Bank/CloseAccount",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(BankServer).CloseAccount(ctx, req.(*CloseAccountRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Bank_GetAccounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(BankServer).GetAccounts(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/Bank/GetAccounts",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(BankServer).GetAccounts(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Bank_GetTransactions_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(GetTransactionsRequest)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(BankServer).GetTransactions(m, &bankGetTransactionsServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bank_GetTransactionsServer interface {
|
||||||
|
Send(*Transaction) error
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type bankGetTransactionsServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *bankGetTransactionsServer) Send(m *Transaction) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Bank_Deposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(DepositRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(BankServer).Deposit(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/Bank/Deposit",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(BankServer).Deposit(ctx, req.(*DepositRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Bank_Withdraw_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(WithdrawRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(BankServer).Withdraw(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/Bank/Withdraw",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(BankServer).Withdraw(ctx, req.(*WithdrawRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Bank_Transfer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(TransferRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(BankServer).Transfer(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/Bank/Transfer",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(BankServer).Transfer(ctx, req.(*TransferRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bank_ServiceDesc is the grpc.ServiceDesc for Bank service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var Bank_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "Bank",
|
||||||
|
HandlerType: (*BankServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "OpenAccount",
|
||||||
|
Handler: _Bank_OpenAccount_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "CloseAccount",
|
||||||
|
Handler: _Bank_CloseAccount_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetAccounts",
|
||||||
|
Handler: _Bank_GetAccounts_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "Deposit",
|
||||||
|
Handler: _Bank_Deposit_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "Withdraw",
|
||||||
|
Handler: _Bank_Withdraw_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "Transfer",
|
||||||
|
Handler: _Bank_Transfer_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "GetTransactions",
|
||||||
|
Handler: _Bank_GetTransactions_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: "bank.proto",
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,21 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/golang/protobuf/ptypes"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// chatServer implements the Support gRPC service, for providing
|
// chatServer implements the Support gRPC service, for providing
|
||||||
// a capability to connect customers and support agents in real-time
|
// a capability to connect customers and support agents in real-time
|
||||||
// chat.
|
// chat.
|
||||||
type chatServer struct {
|
type chatServer struct {
|
||||||
|
UnimplementedSupportServer
|
||||||
chatsBySession map[string]*session
|
chatsBySession map[string]*session
|
||||||
chatsAwaitingAgent []string
|
chatsAwaitingAgent []string
|
||||||
lastSession int32
|
lastSession int32
|
||||||
|
|
@ -134,7 +135,7 @@ func (s *chatServer) ChatCustomer(stream Support_ChatCustomerServer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := &ChatEntry{
|
entry := &ChatEntry{
|
||||||
Date: ptypes.TimestampNow(),
|
Date: timestamppb.Now(),
|
||||||
Entry: &ChatEntry_CustomerMsg{
|
Entry: &ChatEntry_CustomerMsg{
|
||||||
CustomerMsg: req.Msg,
|
CustomerMsg: req.Msg,
|
||||||
},
|
},
|
||||||
|
|
@ -279,7 +280,7 @@ func (s *chatServer) ChatAgent(stream Support_ChatAgentServer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := &ChatEntry{
|
entry := &ChatEntry{
|
||||||
Date: ptypes.TimestampNow(),
|
Date: timestamppb.Now(),
|
||||||
Entry: &ChatEntry_AgentMsg{
|
Entry: &ChatEntry_AgentMsg{
|
||||||
AgentMsg: &AgentMessage{
|
AgentMsg: &AgentMessage{
|
||||||
AgentName: agent,
|
AgentName: agent,
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/golang/protobuf/jsonpb"
|
|
||||||
"github.com/golang/protobuf/ptypes"
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"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.
|
// 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{
|
acct.Transactions = append(acct.Transactions, &Transaction{
|
||||||
AccountNumber: num,
|
AccountNumber: num,
|
||||||
SeqNumber: 1,
|
SeqNumber: 1,
|
||||||
Date: ptypes.TimestampNow(),
|
Date: timestamppb.Now(),
|
||||||
AmountCents: initialBalanceCents,
|
AmountCents: initialBalanceCents,
|
||||||
Desc: "initial deposit",
|
Desc: "initial deposit",
|
||||||
})
|
})
|
||||||
|
|
@ -130,7 +129,7 @@ func (a *account) newTransaction(amountCents int32, desc string) (newBalance int
|
||||||
a.BalanceCents = bal
|
a.BalanceCents = bal
|
||||||
a.Transactions = append(a.Transactions, &Transaction{
|
a.Transactions = append(a.Transactions, &Transaction{
|
||||||
AccountNumber: a.AccountNumber,
|
AccountNumber: a.AccountNumber,
|
||||||
Date: ptypes.TimestampNow(),
|
Date: timestamppb.Now(),
|
||||||
AmountCents: amountCents,
|
AmountCents: amountCents,
|
||||||
SeqNumber: uint64(len(a.Transactions) + 1),
|
SeqNumber: uint64(len(a.Transactions) + 1),
|
||||||
Desc: desc,
|
Desc: desc,
|
||||||
|
|
@ -139,16 +138,11 @@ func (a *account) newTransaction(amountCents int32, desc string) (newBalance int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transaction) MarshalJSON() ([]byte, error) {
|
func (t *Transaction) MarshalJSON() ([]byte, error) {
|
||||||
var jsm jsonpb.Marshaler
|
return protojson.Marshal(t)
|
||||||
var buf bytes.Buffer
|
|
||||||
if err := jsm.Marshal(&buf, t); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transaction) UnmarshalJSON(b []byte) error {
|
func (t *Transaction) UnmarshalJSON(b []byte) error {
|
||||||
return jsonpb.Unmarshal(bytes.NewReader(b), t)
|
return protojson.Unmarshal(b, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accounts) clone() *accounts {
|
func (a *accounts) clone() *accounts {
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
//go:generate protoc --go_out=plugins=grpc:./ bank.proto support.proto
|
//go:generate protoc --go_out=. --go-grpc_out=. bank.proto support.proto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
@ -14,7 +14,6 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/peer"
|
"google.golang.org/grpc/peer"
|
||||||
|
|
@ -130,7 +129,7 @@ type svr struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *svr) load() error {
|
func (s *svr) load() error {
|
||||||
accts, err := ioutil.ReadFile(s.datafile)
|
accts, err := os.ReadFile(s.datafile)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +161,7 @@ func (s *svr) flush() {
|
||||||
|
|
||||||
if b, err := json.Marshal(accounts); err != nil {
|
if b, err := json.Marshal(accounts); err != nil {
|
||||||
grpclog.Errorf("failed to save data to %q", s.datafile)
|
grpclog.Errorf("failed to save data to %q", s.datafile)
|
||||||
} else if err := ioutil.WriteFile(s.datafile, b, 0666); err != nil {
|
} else if err := os.WriteFile(s.datafile, b, 0666); err != nil {
|
||||||
grpclog.Errorf("failed to save data to %q", s.datafile)
|
grpclog.Errorf("failed to save data to %q", s.datafile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
option go_package = "main";
|
option go_package = ".;main";
|
||||||
|
|
||||||
import "google/protobuf/timestamp.proto";
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
// SupportClient is the client API for Support service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type SupportClient interface {
|
||||||
|
// ChatCustomer is used by a customer-facing app to send the customer's messages
|
||||||
|
// to a chat session. The customer is how initiates and terminates (via "hangup")
|
||||||
|
// a chat session. Only customers may invoke this method (e.g. requests must
|
||||||
|
// include customer auth credentials).
|
||||||
|
ChatCustomer(ctx context.Context, opts ...grpc.CallOption) (Support_ChatCustomerClient, error)
|
||||||
|
// ChatAgent is used by an agent-facing app to allow an agent to reply to a
|
||||||
|
// customer's messages in a chat session. The agent may accept a chat session,
|
||||||
|
// which defaults to the session awaiting an agent for the longest period of time
|
||||||
|
// (FIFO queue).
|
||||||
|
ChatAgent(ctx context.Context, opts ...grpc.CallOption) (Support_ChatAgentClient, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type supportClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSupportClient(cc grpc.ClientConnInterface) SupportClient {
|
||||||
|
return &supportClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *supportClient) ChatCustomer(ctx context.Context, opts ...grpc.CallOption) (Support_ChatCustomerClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, &Support_ServiceDesc.Streams[0], "/Support/ChatCustomer", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &supportChatCustomerClient{stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Support_ChatCustomerClient interface {
|
||||||
|
Send(*ChatCustomerRequest) error
|
||||||
|
Recv() (*ChatCustomerResponse, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type supportChatCustomerClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *supportChatCustomerClient) Send(m *ChatCustomerRequest) error {
|
||||||
|
return x.ClientStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *supportChatCustomerClient) Recv() (*ChatCustomerResponse, error) {
|
||||||
|
m := new(ChatCustomerResponse)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *supportClient) ChatAgent(ctx context.Context, opts ...grpc.CallOption) (Support_ChatAgentClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, &Support_ServiceDesc.Streams[1], "/Support/ChatAgent", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &supportChatAgentClient{stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Support_ChatAgentClient interface {
|
||||||
|
Send(*ChatAgentRequest) error
|
||||||
|
Recv() (*ChatAgentResponse, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type supportChatAgentClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *supportChatAgentClient) Send(m *ChatAgentRequest) error {
|
||||||
|
return x.ClientStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *supportChatAgentClient) Recv() (*ChatAgentResponse, error) {
|
||||||
|
m := new(ChatAgentResponse)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportServer is the server API for Support service.
|
||||||
|
// All implementations must embed UnimplementedSupportServer
|
||||||
|
// for forward compatibility
|
||||||
|
type SupportServer interface {
|
||||||
|
// ChatCustomer is used by a customer-facing app to send the customer's messages
|
||||||
|
// to a chat session. The customer is how initiates and terminates (via "hangup")
|
||||||
|
// a chat session. Only customers may invoke this method (e.g. requests must
|
||||||
|
// include customer auth credentials).
|
||||||
|
ChatCustomer(Support_ChatCustomerServer) error
|
||||||
|
// ChatAgent is used by an agent-facing app to allow an agent to reply to a
|
||||||
|
// customer's messages in a chat session. The agent may accept a chat session,
|
||||||
|
// which defaults to the session awaiting an agent for the longest period of time
|
||||||
|
// (FIFO queue).
|
||||||
|
ChatAgent(Support_ChatAgentServer) error
|
||||||
|
mustEmbedUnimplementedSupportServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedSupportServer must be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedSupportServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedSupportServer) ChatCustomer(Support_ChatCustomerServer) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method ChatCustomer not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedSupportServer) ChatAgent(Support_ChatAgentServer) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method ChatAgent not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedSupportServer) mustEmbedUnimplementedSupportServer() {}
|
||||||
|
|
||||||
|
// UnsafeSupportServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to SupportServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeSupportServer interface {
|
||||||
|
mustEmbedUnimplementedSupportServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterSupportServer(s grpc.ServiceRegistrar, srv SupportServer) {
|
||||||
|
s.RegisterService(&Support_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Support_ChatCustomer_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
return srv.(SupportServer).ChatCustomer(&supportChatCustomerServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Support_ChatCustomerServer interface {
|
||||||
|
Send(*ChatCustomerResponse) error
|
||||||
|
Recv() (*ChatCustomerRequest, error)
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type supportChatCustomerServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *supportChatCustomerServer) Send(m *ChatCustomerResponse) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *supportChatCustomerServer) Recv() (*ChatCustomerRequest, error) {
|
||||||
|
m := new(ChatCustomerRequest)
|
||||||
|
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Support_ChatAgent_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
return srv.(SupportServer).ChatAgent(&supportChatAgentServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Support_ChatAgentServer interface {
|
||||||
|
Send(*ChatAgentResponse) error
|
||||||
|
Recv() (*ChatAgentRequest, error)
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type supportChatAgentServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *supportChatAgentServer) Send(m *ChatAgentResponse) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *supportChatAgentServer) Recv() (*ChatAgentRequest, error) {
|
||||||
|
m := new(ChatAgentRequest)
|
||||||
|
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support_ServiceDesc is the grpc.ServiceDesc for Support service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var Support_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "Support",
|
||||||
|
HandlerType: (*SupportServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{},
|
||||||
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "ChatCustomer",
|
||||||
|
Handler: _Support_ChatCustomer_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "ChatAgent",
|
||||||
|
Handler: _Support_ChatAgent_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: "support.proto",
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
@ -9,17 +10,15 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/interop/grpc_testing"
|
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/reflection"
|
"google.golang.org/grpc/reflection"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/fullstorydev/grpcurl"
|
"github.com/fullstorydev/grpcurl"
|
||||||
grpcurl_testing "github.com/fullstorydev/grpcurl/testing"
|
grpcurl_testing "github.com/fullstorydev/grpcurl/internal/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -96,7 +95,7 @@ func main() {
|
||||||
|
|
||||||
svr := grpc.NewServer(opts...)
|
svr := grpc.NewServer(opts...)
|
||||||
|
|
||||||
grpc_testing.RegisterTestServiceServer(svr, grpcurl_testing.TestServer{})
|
grpcurl_testing.RegisterTestServiceServer(svr, grpcurl_testing.TestServer{})
|
||||||
if !*noreflect {
|
if !*noreflect {
|
||||||
reflection.Register(svr)
|
reflection.Register(svr)
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
@ -3,7 +3,7 @@ syntax = "proto3";
|
||||||
import "google/protobuf/descriptor.proto";
|
import "google/protobuf/descriptor.proto";
|
||||||
import "google/protobuf/empty.proto";
|
import "google/protobuf/empty.proto";
|
||||||
import "google/protobuf/timestamp.proto";
|
import "google/protobuf/timestamp.proto";
|
||||||
import "testing/example2.proto";
|
import "example2.proto";
|
||||||
|
|
||||||
message TestRequest {
|
message TestRequest {
|
||||||
repeated string file_names = 1;
|
repeated string file_names = 1;
|
||||||
Binary file not shown.
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -18,7 +18,9 @@
|
||||||
// of unary/streaming requests/responses.
|
// of unary/streaming requests/responses.
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
package grpc.testing;
|
package testing;
|
||||||
|
|
||||||
|
option go_package = ".;testing";
|
||||||
|
|
||||||
message Empty {}
|
message Empty {}
|
||||||
|
|
||||||
|
|
@ -172,5 +174,5 @@ service TestService {
|
||||||
// that case.
|
// that case.
|
||||||
service UnimplementedService {
|
service UnimplementedService {
|
||||||
// A call that no server should implement
|
// A call that no server should implement
|
||||||
rpc UnimplementedCall(grpc.testing.Empty) returns (grpc.testing.Empty);
|
rpc UnimplementedCall(Empty) returns (Empty);
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,523 @@
|
||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
// TestServiceClient is the client API for TestService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type TestServiceClient interface {
|
||||||
|
// One empty request followed by one empty response.
|
||||||
|
EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)
|
||||||
|
// One request followed by one response.
|
||||||
|
// The server returns the client payload as-is.
|
||||||
|
UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error)
|
||||||
|
// One request followed by a sequence of responses (streamed download).
|
||||||
|
// The server returns the payload with client desired type and sizes.
|
||||||
|
StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error)
|
||||||
|
// A sequence of requests followed by one response (streamed upload).
|
||||||
|
// The server returns the aggregated size of client payload as the result.
|
||||||
|
StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error)
|
||||||
|
// 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.
|
||||||
|
FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error)
|
||||||
|
// A sequence of requests followed by a sequence of responses.
|
||||||
|
// The server buffers all the client requests and then serves them in order. A
|
||||||
|
// stream of responses are returned to the client when the server starts with
|
||||||
|
// first request.
|
||||||
|
HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type testServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTestServiceClient(cc grpc.ClientConnInterface) TestServiceClient {
|
||||||
|
return &testServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testServiceClient) EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
|
||||||
|
out := new(Empty)
|
||||||
|
err := c.cc.Invoke(ctx, "/testing.TestService/EmptyCall", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testServiceClient) UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error) {
|
||||||
|
out := new(SimpleResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/testing.TestService/UnaryCall", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testServiceClient) StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, &TestService_ServiceDesc.Streams[0], "/testing.TestService/StreamingOutputCall", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &testServiceStreamingOutputCallClient{stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestService_StreamingOutputCallClient interface {
|
||||||
|
Recv() (*StreamingOutputCallResponse, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type testServiceStreamingOutputCallClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceStreamingOutputCallClient) Recv() (*StreamingOutputCallResponse, error) {
|
||||||
|
m := new(StreamingOutputCallResponse)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testServiceClient) StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, &TestService_ServiceDesc.Streams[1], "/testing.TestService/StreamingInputCall", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &testServiceStreamingInputCallClient{stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestService_StreamingInputCallClient interface {
|
||||||
|
Send(*StreamingInputCallRequest) error
|
||||||
|
CloseAndRecv() (*StreamingInputCallResponse, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type testServiceStreamingInputCallClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceStreamingInputCallClient) Send(m *StreamingInputCallRequest) error {
|
||||||
|
return x.ClientStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceStreamingInputCallClient) CloseAndRecv() (*StreamingInputCallResponse, error) {
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m := new(StreamingInputCallResponse)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testServiceClient) FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, &TestService_ServiceDesc.Streams[2], "/testing.TestService/FullDuplexCall", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &testServiceFullDuplexCallClient{stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestService_FullDuplexCallClient interface {
|
||||||
|
Send(*StreamingOutputCallRequest) error
|
||||||
|
Recv() (*StreamingOutputCallResponse, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type testServiceFullDuplexCallClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceFullDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
|
||||||
|
return x.ClientStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceFullDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
|
||||||
|
m := new(StreamingOutputCallResponse)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testServiceClient) HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, &TestService_ServiceDesc.Streams[3], "/testing.TestService/HalfDuplexCall", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &testServiceHalfDuplexCallClient{stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestService_HalfDuplexCallClient interface {
|
||||||
|
Send(*StreamingOutputCallRequest) error
|
||||||
|
Recv() (*StreamingOutputCallResponse, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type testServiceHalfDuplexCallClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceHalfDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
|
||||||
|
return x.ClientStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceHalfDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
|
||||||
|
m := new(StreamingOutputCallResponse)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestServiceServer is the server API for TestService service.
|
||||||
|
// All implementations must embed UnimplementedTestServiceServer
|
||||||
|
// for forward compatibility
|
||||||
|
type TestServiceServer interface {
|
||||||
|
// One empty request followed by one empty response.
|
||||||
|
EmptyCall(context.Context, *Empty) (*Empty, error)
|
||||||
|
// One request followed by one response.
|
||||||
|
// The server returns the client payload as-is.
|
||||||
|
UnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error)
|
||||||
|
// One request followed by a sequence of responses (streamed download).
|
||||||
|
// The server returns the payload with client desired type and sizes.
|
||||||
|
StreamingOutputCall(*StreamingOutputCallRequest, TestService_StreamingOutputCallServer) error
|
||||||
|
// A sequence of requests followed by one response (streamed upload).
|
||||||
|
// The server returns the aggregated size of client payload as the result.
|
||||||
|
StreamingInputCall(TestService_StreamingInputCallServer) error
|
||||||
|
// 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.
|
||||||
|
FullDuplexCall(TestService_FullDuplexCallServer) error
|
||||||
|
// A sequence of requests followed by a sequence of responses.
|
||||||
|
// The server buffers all the client requests and then serves them in order. A
|
||||||
|
// stream of responses are returned to the client when the server starts with
|
||||||
|
// first request.
|
||||||
|
HalfDuplexCall(TestService_HalfDuplexCallServer) error
|
||||||
|
mustEmbedUnimplementedTestServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedTestServiceServer must be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedTestServiceServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedTestServiceServer) EmptyCall(context.Context, *Empty) (*Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method EmptyCall not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedTestServiceServer) UnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method UnaryCall not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedTestServiceServer) StreamingOutputCall(*StreamingOutputCallRequest, TestService_StreamingOutputCallServer) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method StreamingOutputCall not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedTestServiceServer) StreamingInputCall(TestService_StreamingInputCallServer) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method StreamingInputCall not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedTestServiceServer) FullDuplexCall(TestService_FullDuplexCallServer) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method FullDuplexCall not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedTestServiceServer) HalfDuplexCall(TestService_HalfDuplexCallServer) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method HalfDuplexCall not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedTestServiceServer) mustEmbedUnimplementedTestServiceServer() {}
|
||||||
|
|
||||||
|
// UnsafeTestServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to TestServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeTestServiceServer interface {
|
||||||
|
mustEmbedUnimplementedTestServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterTestServiceServer(s grpc.ServiceRegistrar, srv TestServiceServer) {
|
||||||
|
s.RegisterService(&TestService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _TestService_EmptyCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(TestServiceServer).EmptyCall(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/testing.TestService/EmptyCall",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(TestServiceServer).EmptyCall(ctx, req.(*Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _TestService_UnaryCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SimpleRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(TestServiceServer).UnaryCall(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/testing.TestService/UnaryCall",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(TestServiceServer).UnaryCall(ctx, req.(*SimpleRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _TestService_StreamingOutputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(StreamingOutputCallRequest)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(TestServiceServer).StreamingOutputCall(m, &testServiceStreamingOutputCallServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestService_StreamingOutputCallServer interface {
|
||||||
|
Send(*StreamingOutputCallResponse) error
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type testServiceStreamingOutputCallServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceStreamingOutputCallServer) Send(m *StreamingOutputCallResponse) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _TestService_StreamingInputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
return srv.(TestServiceServer).StreamingInputCall(&testServiceStreamingInputCallServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestService_StreamingInputCallServer interface {
|
||||||
|
SendAndClose(*StreamingInputCallResponse) error
|
||||||
|
Recv() (*StreamingInputCallRequest, error)
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type testServiceStreamingInputCallServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceStreamingInputCallServer) SendAndClose(m *StreamingInputCallResponse) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceStreamingInputCallServer) Recv() (*StreamingInputCallRequest, error) {
|
||||||
|
m := new(StreamingInputCallRequest)
|
||||||
|
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _TestService_FullDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
return srv.(TestServiceServer).FullDuplexCall(&testServiceFullDuplexCallServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestService_FullDuplexCallServer interface {
|
||||||
|
Send(*StreamingOutputCallResponse) error
|
||||||
|
Recv() (*StreamingOutputCallRequest, error)
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type testServiceFullDuplexCallServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceFullDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceFullDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
|
||||||
|
m := new(StreamingOutputCallRequest)
|
||||||
|
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _TestService_HalfDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
return srv.(TestServiceServer).HalfDuplexCall(&testServiceHalfDuplexCallServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestService_HalfDuplexCallServer interface {
|
||||||
|
Send(*StreamingOutputCallResponse) error
|
||||||
|
Recv() (*StreamingOutputCallRequest, error)
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type testServiceHalfDuplexCallServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceHalfDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testServiceHalfDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
|
||||||
|
m := new(StreamingOutputCallRequest)
|
||||||
|
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestService_ServiceDesc is the grpc.ServiceDesc for TestService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var TestService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "testing.TestService",
|
||||||
|
HandlerType: (*TestServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "EmptyCall",
|
||||||
|
Handler: _TestService_EmptyCall_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "UnaryCall",
|
||||||
|
Handler: _TestService_UnaryCall_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "StreamingOutputCall",
|
||||||
|
Handler: _TestService_StreamingOutputCall_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "StreamingInputCall",
|
||||||
|
Handler: _TestService_StreamingInputCall_Handler,
|
||||||
|
ClientStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "FullDuplexCall",
|
||||||
|
Handler: _TestService_FullDuplexCall_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "HalfDuplexCall",
|
||||||
|
Handler: _TestService_HalfDuplexCall_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: "test.proto",
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedServiceClient is the client API for UnimplementedService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type UnimplementedServiceClient interface {
|
||||||
|
// A call that no server should implement
|
||||||
|
UnimplementedCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type unimplementedServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUnimplementedServiceClient(cc grpc.ClientConnInterface) UnimplementedServiceClient {
|
||||||
|
return &unimplementedServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *unimplementedServiceClient) UnimplementedCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
|
||||||
|
out := new(Empty)
|
||||||
|
err := c.cc.Invoke(ctx, "/testing.UnimplementedService/UnimplementedCall", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedServiceServer is the server API for UnimplementedService service.
|
||||||
|
// All implementations must embed UnimplementedUnimplementedServiceServer
|
||||||
|
// for forward compatibility
|
||||||
|
type UnimplementedServiceServer interface {
|
||||||
|
// A call that no server should implement
|
||||||
|
UnimplementedCall(context.Context, *Empty) (*Empty, error)
|
||||||
|
mustEmbedUnimplementedUnimplementedServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedUnimplementedServiceServer must be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedUnimplementedServiceServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedUnimplementedServiceServer) UnimplementedCall(context.Context, *Empty) (*Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method UnimplementedCall not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedUnimplementedServiceServer) mustEmbedUnimplementedUnimplementedServiceServer() {}
|
||||||
|
|
||||||
|
// UnsafeUnimplementedServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to UnimplementedServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeUnimplementedServiceServer interface {
|
||||||
|
mustEmbedUnimplementedUnimplementedServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterUnimplementedServiceServer(s grpc.ServiceRegistrar, srv UnimplementedServiceServer) {
|
||||||
|
s.RegisterService(&UnimplementedService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _UnimplementedService_UnimplementedCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(UnimplementedServiceServer).UnimplementedCall(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/testing.UnimplementedService/UnimplementedCall",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(UnimplementedServiceServer).UnimplementedCall(ctx, req.(*Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedService_ServiceDesc is the grpc.ServiceDesc for UnimplementedService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var UnimplementedService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "testing.UnimplementedService",
|
||||||
|
HandlerType: (*UnimplementedServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "UnimplementedCall",
|
||||||
|
Handler: _UnimplementedService_UnimplementedCall_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "test.proto",
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
package testing
|
package testing
|
||||||
|
|
||||||
|
//go:generate protoc --go_out=. --go-grpc_out=. 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 (
|
import (
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/interop/grpc_testing"
|
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
|
@ -16,10 +19,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestServer implements the TestService interface defined in example.proto.
|
// TestServer implements the TestService interface defined in example.proto.
|
||||||
type TestServer struct{}
|
type TestServer struct {
|
||||||
|
UnimplementedTestServiceServer
|
||||||
|
}
|
||||||
|
|
||||||
// EmptyCall accepts one empty request and issues one empty response.
|
// 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)
|
headers, trailers, failEarly, failLate := processMetadata(ctx)
|
||||||
grpc.SetHeader(ctx, headers)
|
grpc.SetHeader(ctx, headers)
|
||||||
grpc.SetTrailer(ctx, trailers)
|
grpc.SetTrailer(ctx, trailers)
|
||||||
|
|
@ -35,7 +40,7 @@ func (TestServer) EmptyCall(ctx context.Context, req *grpc_testing.Empty) (*grpc
|
||||||
|
|
||||||
// UnaryCall accepts one request and issues one response. The response includes
|
// UnaryCall accepts one request and issues one response. The response includes
|
||||||
// the client's payload as-is.
|
// 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)
|
headers, trailers, failEarly, failLate := processMetadata(ctx)
|
||||||
grpc.SetHeader(ctx, headers)
|
grpc.SetHeader(ctx, headers)
|
||||||
grpc.SetTrailer(ctx, trailers)
|
grpc.SetTrailer(ctx, trailers)
|
||||||
|
|
@ -46,7 +51,7 @@ func (TestServer) UnaryCall(ctx context.Context, req *grpc_testing.SimpleRequest
|
||||||
return nil, status.Error(failLate, "fail")
|
return nil, status.Error(failLate, "fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &grpc_testing.SimpleResponse{
|
return &SimpleResponse{
|
||||||
Payload: req.Payload,
|
Payload: req.Payload,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -54,7 +59,7 @@ func (TestServer) UnaryCall(ctx context.Context, req *grpc_testing.SimpleRequest
|
||||||
// StreamingOutputCall accepts one request and issues a sequence of responses
|
// StreamingOutputCall accepts one request and issues a sequence of responses
|
||||||
// (streamed download). The server returns the payload with client desired type
|
// (streamed download). The server returns the payload with client desired type
|
||||||
// and sizes as specified in the request's ResponseParameters.
|
// 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())
|
headers, trailers, failEarly, failLate := processMetadata(str.Context())
|
||||||
str.SetHeader(headers)
|
str.SetHeader(headers)
|
||||||
str.SetTrailer(trailers)
|
str.SetTrailer(trailers)
|
||||||
|
|
@ -62,7 +67,7 @@ func (TestServer) StreamingOutputCall(req *grpc_testing.StreamingOutputCallReque
|
||||||
return status.Error(failEarly, "fail")
|
return status.Error(failEarly, "fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
rsp := &grpc_testing.StreamingOutputCallResponse{Payload: &grpc_testing.Payload{}}
|
rsp := &StreamingOutputCallResponse{Payload: &Payload{}}
|
||||||
for _, param := range req.ResponseParameters {
|
for _, param := range req.ResponseParameters {
|
||||||
if str.Context().Err() != nil {
|
if str.Context().Err() != nil {
|
||||||
return str.Context().Err()
|
return str.Context().Err()
|
||||||
|
|
@ -92,7 +97,7 @@ func (TestServer) StreamingOutputCall(req *grpc_testing.StreamingOutputCallReque
|
||||||
// StreamingInputCall accepts a sequence of requests and issues one response
|
// StreamingInputCall accepts a sequence of requests and issues one response
|
||||||
// (streamed upload). The server returns the aggregated size of client payloads
|
// (streamed upload). The server returns the aggregated size of client payloads
|
||||||
// as the result.
|
// 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())
|
headers, trailers, failEarly, failLate := processMetadata(str.Context())
|
||||||
str.SetHeader(headers)
|
str.SetHeader(headers)
|
||||||
str.SetTrailer(trailers)
|
str.SetTrailer(trailers)
|
||||||
|
|
@ -114,7 +119,7 @@ func (TestServer) StreamingInputCall(str grpc_testing.TestService_StreamingInput
|
||||||
sz += len(req.Payload.Body)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +132,7 @@ func (TestServer) StreamingInputCall(str grpc_testing.TestService_StreamingInput
|
||||||
// FullDuplexCall accepts a sequence of requests with each request served by the
|
// FullDuplexCall accepts a sequence of requests with each request served by the
|
||||||
// server immediately. As one request could lead to multiple responses, this
|
// server immediately. As one request could lead to multiple responses, this
|
||||||
// interface demonstrates the idea of full duplexing.
|
// 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())
|
headers, trailers, failEarly, failLate := processMetadata(str.Context())
|
||||||
str.SetHeader(headers)
|
str.SetHeader(headers)
|
||||||
str.SetTrailer(trailers)
|
str.SetTrailer(trailers)
|
||||||
|
|
@ -135,7 +140,7 @@ func (TestServer) FullDuplexCall(str grpc_testing.TestService_FullDuplexCallServ
|
||||||
return status.Error(failEarly, "fail")
|
return status.Error(failEarly, "fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
rsp := &grpc_testing.StreamingOutputCallResponse{Payload: &grpc_testing.Payload{}}
|
rsp := &StreamingOutputCallResponse{Payload: &Payload{}}
|
||||||
for {
|
for {
|
||||||
if str.Context().Err() != nil {
|
if str.Context().Err() != nil {
|
||||||
return str.Context().Err()
|
return str.Context().Err()
|
||||||
|
|
@ -170,7 +175,7 @@ func (TestServer) FullDuplexCall(str grpc_testing.TestService_FullDuplexCallServ
|
||||||
// responses. The server buffers all the client requests and then serves them
|
// 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
|
// in order. A stream of responses is returned to the client once the client
|
||||||
// half-closes the stream.
|
// 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())
|
headers, trailers, failEarly, failLate := processMetadata(str.Context())
|
||||||
str.SetHeader(headers)
|
str.SetHeader(headers)
|
||||||
str.SetTrailer(trailers)
|
str.SetTrailer(trailers)
|
||||||
|
|
@ -178,7 +183,7 @@ func (TestServer) HalfDuplexCall(str grpc_testing.TestService_HalfDuplexCallServ
|
||||||
return status.Error(failEarly, "fail")
|
return status.Error(failEarly, "fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
var reqs []*grpc_testing.StreamingOutputCallRequest
|
var reqs []*StreamingOutputCallRequest
|
||||||
for {
|
for {
|
||||||
if str.Context().Err() != nil {
|
if str.Context().Err() != nil {
|
||||||
return str.Context().Err()
|
return str.Context().Err()
|
||||||
|
|
@ -192,7 +197,7 @@ func (TestServer) HalfDuplexCall(str grpc_testing.TestService_HalfDuplexCallServ
|
||||||
reqs = append(reqs, req)
|
reqs = append(reqs, req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rsp := &grpc_testing.StreamingOutputCallResponse{}
|
rsp := &StreamingOutputCallResponse{}
|
||||||
for _, req := range reqs {
|
for _, req := range reqs {
|
||||||
rsp.Payload = req.Payload
|
rsp.Payload = req.Payload
|
||||||
if err := str.Send(rsp); err != nil {
|
if err := str.Send(rsp); err != nil {
|
||||||
|
|
@ -251,4 +256,4 @@ func toCode(vals []string) codes.Code {
|
||||||
return codes.Code(i)
|
return codes.Code(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ grpc_testing.TestServiceServer = TestServer{}
|
var _ TestServiceServer = TestServer{}
|
||||||
50
invoke.go
50
invoke.go
|
|
@ -2,19 +2,19 @@ package grpcurl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/golang/protobuf/jsonpb"
|
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
|
||||||
"github.com/jhump/protoreflect/desc"
|
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||||
"github.com/jhump/protoreflect/dynamic"
|
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
|
||||||
"github.com/jhump/protoreflect/dynamic/grpcdynamic"
|
"github.com/jhump/protoreflect/dynamic/grpcdynamic"
|
||||||
"github.com/jhump/protoreflect/grpcreflect"
|
"github.com/jhump/protoreflect/grpcreflect"
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
@ -93,9 +93,17 @@ func InvokeRPC(ctx context.Context, source DescriptorSource, ch grpcdynamic.Chan
|
||||||
if svc == "" || mth == "" {
|
if svc == "" || mth == "" {
|
||||||
return fmt.Errorf("given method name %q is not in expected format: 'service/method' or 'service.method'", methodName)
|
return fmt.Errorf("given method name %q is not in expected format: 'service/method' or 'service.method'", methodName)
|
||||||
}
|
}
|
||||||
|
|
||||||
dsc, err := source.FindSymbol(svc)
|
dsc, err := source.FindSymbol(svc)
|
||||||
if err != nil {
|
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("target server does not expose service %q", svc)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("failed to query for service descriptor %q: %v", svc, err)
|
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)
|
return fmt.Errorf("grpc call for %q failed: %v", md.GetFullyQualifiedName(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if respHeaders, err := str.Header(); err == nil {
|
if str != nil {
|
||||||
handler.OnReceiveHeaders(respHeaders)
|
if respHeaders, err := str.Header(); err == nil {
|
||||||
|
handler.OnReceiveHeaders(respHeaders)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if stat.Code() == codes.OK {
|
if stat.Code() == codes.OK {
|
||||||
handler.OnReceiveResponse(resp)
|
handler.OnReceiveResponse(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.OnReceiveTrailers(stat, str.Trailer())
|
if str != nil {
|
||||||
|
handler.OnReceiveTrailers(stat, str.Trailer())
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -252,8 +264,10 @@ func invokeServerStream(ctx context.Context, stub grpcdynamic.Stub, md *desc.Met
|
||||||
// Now we can actually invoke the RPC!
|
// Now we can actually invoke the RPC!
|
||||||
str, err := stub.InvokeRpcServerStream(ctx, md, req)
|
str, err := stub.InvokeRpcServerStream(ctx, md, req)
|
||||||
|
|
||||||
if respHeaders, err := str.Header(); err == nil {
|
if str != nil {
|
||||||
handler.OnReceiveHeaders(respHeaders)
|
if respHeaders, err := str.Header(); err == nil {
|
||||||
|
handler.OnReceiveHeaders(respHeaders)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download each response message
|
// Download each response message
|
||||||
|
|
@ -276,7 +290,9 @@ func invokeServerStream(ctx context.Context, stub grpcdynamic.Stub, md *desc.Met
|
||||||
return fmt.Errorf("grpc call for %q failed: %v", md.GetFullyQualifiedName(), err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -326,8 +342,10 @@ func invokeBidi(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescr
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if respHeaders, err := str.Header(); err == nil {
|
if str != nil {
|
||||||
handler.OnReceiveHeaders(respHeaders)
|
if respHeaders, err := str.Header(); err == nil {
|
||||||
|
handler.OnReceiveHeaders(respHeaders)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download each response message
|
// Download each response message
|
||||||
|
|
@ -354,7 +372,9 @@ func invokeBidi(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescr
|
||||||
return fmt.Errorf("grpc call for %q failed: %v", md.GetFullyQualifiedName(), err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ protoc testing/example.proto \
|
||||||
--include_imports \
|
--include_imports \
|
||||||
--descriptor_set_out=testing/example.protoset
|
--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..."
|
echo "Creating certs for TLS testing..."
|
||||||
if ! hash certstrap 2>/dev/null; then
|
if ! hash certstrap 2>/dev/null; then
|
||||||
# certstrap not found: try to install it
|
# certstrap not found: try to install it
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
# 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, you can manually run through each step in the "Docker" section of `do_release.sh`.
|
||||||
|
|
||||||
|
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/refs/tags/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!
|
||||||
|
|
@ -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._
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/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,linux/ppc64le --tag fullstorydev/grpcurl:${VERSION} --tag fullstorydev/grpcurl:latest --push --progress plain --no-cache .
|
||||||
|
$PREFIX docker buildx build --platform linux/amd64,linux/s390x,linux/arm64,linux/ppc64le --tag fullstorydev/grpcurl:${VERSION}-alpine --tag fullstorydev/grpcurl:latest-alpine --push --progress plain --no-cache --target alpine .
|
||||||
|
rm VERSION
|
||||||
|
|
||||||
|
# Homebrew release
|
||||||
|
|
||||||
|
URL="https://github.com/fullstorydev/grpcurl/archive/refs/tags/${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
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
# packing and releasing
|
||||||
|
To pack the current branch to a snap package:
|
||||||
|
|
||||||
|
`snapcraft pack`
|
||||||
|
|
||||||
|
To install the package locally:
|
||||||
|
|
||||||
|
`snap install ./grpcurl_v[version tag]_amd64.snap --devmode`
|
||||||
|
|
||||||
|
To upload the snap to the edge channel:
|
||||||
|
|
||||||
|
`snapcraft upload --release edge ./grpcurl_v[version tag]_amd64.snap`
|
||||||
|
|
||||||
|
(you need to own the package name registration for this!)
|
||||||
|
|
||||||
|
# ownership
|
||||||
|
The snap's current owner is `pietro.pasotti@canonical.com`; who is very happy to support with maintaining the snap distribution and/or transfer its ownership to the developers.
|
||||||
|
|
||||||
|
Please reach out to me for questions regarding the snap; including:
|
||||||
|
- adding support for other architectures
|
||||||
|
- automating the release
|
||||||
|
|
||||||
|
Cheers and thanks for the awesome tool!
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
name: grpcurl
|
||||||
|
base: core24
|
||||||
|
# allow grpcurl part to call craftctl set-version
|
||||||
|
adopt-info: grpcurl
|
||||||
|
summary: grpcurl is a command-line tool that lets you interact with gRPC servers.
|
||||||
|
|
||||||
|
description: |
|
||||||
|
grpcurl is a command-line tool that lets you interact with gRPC servers.
|
||||||
|
It's basically curl for gRPC servers.
|
||||||
|
|
||||||
|
grade: stable
|
||||||
|
confinement: strict
|
||||||
|
license: MIT
|
||||||
|
|
||||||
|
platforms:
|
||||||
|
amd64:
|
||||||
|
build-on:
|
||||||
|
- amd64
|
||||||
|
build-for:
|
||||||
|
- amd64
|
||||||
|
arm64:
|
||||||
|
build-on:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
build-for:
|
||||||
|
- arm64
|
||||||
|
|
||||||
|
apps:
|
||||||
|
grpcurl:
|
||||||
|
command: grpcurl
|
||||||
|
plugs:
|
||||||
|
- network
|
||||||
|
|
||||||
|
parts:
|
||||||
|
grpcurl:
|
||||||
|
plugin: go
|
||||||
|
build-snaps: [go/latest/stable]
|
||||||
|
source: https://github.com/fullstorydev/grpcurl
|
||||||
|
source-type: git
|
||||||
|
override-build: |
|
||||||
|
tag="$(git describe --tags --abbrev=0)"
|
||||||
|
craftctl set version="$tag"
|
||||||
|
|
||||||
|
go build -o $CRAFT_PART_INSTALL/grpcurl ./cmd/grpcurl/grpcurl.go
|
||||||
|
|
||||||
|
# adjust the permissions
|
||||||
|
chmod 0755 $CRAFT_PART_INSTALL/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
|
package grpcurl_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/interop/grpc_testing"
|
|
||||||
|
|
||||||
. "github.com/fullstorydev/grpcurl"
|
. "github.com/fullstorydev/grpcurl"
|
||||||
grpcurl_testing "github.com/fullstorydev/grpcurl/testing"
|
grpcurl_testing "github.com/fullstorydev/grpcurl/internal/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPlainText(t *testing.T) {
|
func TestPlainText(t *testing.T) {
|
||||||
|
|
@ -27,11 +26,11 @@ func TestPlainText(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicTLS(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +45,7 @@ func TestBasicTLS(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInsecureClientTLS(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -65,11 +64,11 @@ func TestInsecureClientTLS(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClientCertTLS(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -84,11 +83,11 @@ func TestClientCertTLS(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRequireClientCertTLS(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +102,7 @@ func TestRequireClientCertTLS(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokenTLS_ClientPlainText(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
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
|
// but request fails because server closes connection upon seeing request
|
||||||
// bytes that are not a TLS handshake
|
// bytes that are not a TLS handshake
|
||||||
cl := grpc_testing.NewTestServiceClient(e.cc)
|
cl := grpcurl_testing.NewTestServiceClient(e.cc)
|
||||||
_, err = cl.UnaryCall(context.Background(), &grpc_testing.SimpleRequest{})
|
_, err = cl.UnaryCall(context.Background(), &grpcurl_testing.SimpleRequest{})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expecting failure")
|
t.Fatal("expecting failure")
|
||||||
}
|
}
|
||||||
|
|
@ -164,15 +163,15 @@ func TestBrokenTLS_ClientPlainText(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokenTLS_ServerPlainText(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e, err := createTestServerAndClient(nil, clientCreds)
|
e, err := createTestServerAndClient(nil, clientCreds)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expecting TLS failure setting up server and client")
|
|
||||||
e.Close()
|
e.Close()
|
||||||
|
t.Fatal("expecting TLS failure setting up server and client")
|
||||||
}
|
}
|
||||||
if !strings.Contains(err.Error(), "first record does not look like a TLS handshake") {
|
if !strings.Contains(err.Error(), "first record does not look like a TLS handshake") {
|
||||||
t.Fatalf("expecting TLS handshake failure, got: %v", err)
|
t.Fatalf("expecting TLS handshake failure, got: %v", err)
|
||||||
|
|
@ -180,19 +179,19 @@ func TestBrokenTLS_ServerPlainText(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokenTLS_ServerUsesWrongCert(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expecting TLS failure setting up server and client")
|
|
||||||
e.Close()
|
e.Close()
|
||||||
|
t.Fatal("expecting TLS failure setting up server and client")
|
||||||
}
|
}
|
||||||
if !strings.Contains(err.Error(), "certificate is valid for") {
|
if !strings.Contains(err.Error(), "certificate is valid for") {
|
||||||
t.Fatalf("expecting TLS certificate error, got: %v", err)
|
t.Fatalf("expecting TLS certificate error, got: %v", err)
|
||||||
|
|
@ -200,39 +199,39 @@ func TestBrokenTLS_ServerUsesWrongCert(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokenTLS_ClientHasExpiredCert(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expecting TLS failure setting up server and client")
|
|
||||||
e.Close()
|
e.Close()
|
||||||
|
t.Fatal("expecting TLS failure setting up server and client")
|
||||||
}
|
}
|
||||||
if !strings.Contains(err.Error(), "bad certificate") {
|
if !strings.Contains(err.Error(), "certificate") {
|
||||||
t.Fatalf("expecting TLS certificate error, got: %v", err)
|
t.Fatalf("expecting TLS certificate error, got: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokenTLS_ServerHasExpiredCert(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expecting TLS failure setting up server and client")
|
|
||||||
e.Close()
|
e.Close()
|
||||||
|
t.Fatal("expecting TLS failure setting up server and client")
|
||||||
}
|
}
|
||||||
if !strings.Contains(err.Error(), "certificate has expired or is not yet valid") {
|
if !strings.Contains(err.Error(), "certificate has expired or is not yet valid") {
|
||||||
t.Fatalf("expecting TLS certificate expired, got: %v", err)
|
t.Fatalf("expecting TLS certificate expired, got: %v", err)
|
||||||
|
|
@ -240,70 +239,78 @@ func TestBrokenTLS_ServerHasExpiredCert(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokenTLS_ClientNotTrusted(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expecting TLS failure setting up server and client")
|
|
||||||
e.Close()
|
e.Close()
|
||||||
|
t.Fatal("expecting TLS failure setting up server and client")
|
||||||
}
|
}
|
||||||
if !strings.Contains(err.Error(), "bad certificate") {
|
// Check for either the old error (Go <=1.24) or the new one (Go 1.25+)
|
||||||
t.Fatalf("expecting TLS certificate error, got: %v", err)
|
// Go 1.24: "bad certificate"
|
||||||
|
// Go 1.25: "handshake failure"
|
||||||
|
errMsg := err.Error()
|
||||||
|
if !strings.Contains(errMsg, "bad certificate") && !strings.Contains(errMsg, "handshake failure") {
|
||||||
|
t.Fatalf("expecting a specific TLS certificate or handshake error, got: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokenTLS_ServerNotTrusted(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expecting TLS failure setting up server and client")
|
|
||||||
e.Close()
|
e.Close()
|
||||||
|
t.Fatal("expecting TLS failure setting up server and client")
|
||||||
}
|
}
|
||||||
if !strings.Contains(err.Error(), "certificate signed by unknown authority") {
|
if !strings.Contains(err.Error(), "certificate") {
|
||||||
t.Fatalf("expecting TLS certificate error, got: %v", err)
|
t.Fatalf("expecting TLS certificate error, got: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokenTLS_RequireClientCertButNonePresented(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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create server creds: %v", err)
|
t.Fatalf("failed to create server creds: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
e, err := createTestServerAndClient(serverCreds, clientCreds)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expecting TLS failure setting up server and client")
|
|
||||||
e.Close()
|
e.Close()
|
||||||
|
t.Fatal("expecting TLS failure setting up server and client")
|
||||||
}
|
}
|
||||||
if !strings.Contains(err.Error(), "bad certificate") {
|
// Check for either the old error (Go <=1.24) or the new one (Go 1.25+)
|
||||||
t.Fatalf("expecting TLS certificate error, got: %v", err)
|
// Go 1.24: "bad certificate"
|
||||||
|
// Go 1.25: "handshake failure"
|
||||||
|
errMsg := err.Error()
|
||||||
|
if !strings.Contains(errMsg, "bad certificate") && !strings.Contains(errMsg, "handshake failure") {
|
||||||
|
t.Fatalf("expecting a specific TLS certificate or handshake error, got: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func simpleTest(t *testing.T, cc *grpc.ClientConn) {
|
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)
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
defer cancel()
|
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 {
|
if err != nil {
|
||||||
t.Errorf("simple RPC failed: %v", err)
|
t.Errorf("simple RPC failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -323,7 +330,7 @@ func createTestServerAndClient(serverCreds, clientCreds credentials.TransportCre
|
||||||
svrOpts = []grpc.ServerOption{grpc.Creds(serverCreds)}
|
svrOpts = []grpc.ServerOption{grpc.Creds(serverCreds)}
|
||||||
}
|
}
|
||||||
svr := grpc.NewServer(svrOpts...)
|
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")
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return e, err
|
return e, err
|
||||||
|
|
@ -350,7 +357,7 @@ type testEnv struct {
|
||||||
cc *grpc.ClientConn
|
cc *grpc.ClientConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e testEnv) Close() {
|
func (e *testEnv) Close() {
|
||||||
if e.cc != nil {
|
if e.cc != nil {
|
||||||
e.cc.Close()
|
e.cc.Close()
|
||||||
e.cc = nil
|
e.cc = nil
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue