Compare commits

...

195 Commits

Author SHA1 Message Date
dependabot[bot] c54eac28fd
Bump github.com/jhump/protoreflect from 1.17.0 to 1.18.0 (#545)
Bumps [github.com/jhump/protoreflect](https://github.com/jhump/protoreflect) from 1.17.0 to 1.18.0.
- [Release notes](https://github.com/jhump/protoreflect/releases)
- [Commits](https://github.com/jhump/protoreflect/compare/v1.17.0...v1.18.0)

---
updated-dependencies:
- dependency-name: github.com/jhump/protoreflect
  dependency-version: 1.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-27 12:58:19 -05:00
Scott Blum 6caf0e77fa
update lead version to Go 1.25 (#543) 2026-01-22 16:01:15 -05:00
rifeplight 1ad1dc15dd
refactor: use strings.Builder to improve performance (#537)
Signed-off-by: rifeplight <rifeplight@outlook.com>
2025-10-29 08:07:46 -04:00
vastonus f575e91b2c
refactor: use slices.Contains to simplify code (#536)
Signed-off-by: vastonus <vastonus@outlook.com>
2025-10-16 07:57:01 -04:00
dependabot[bot] ed672b2bc9
Bump google.golang.org/protobuf from 1.36.8 to 1.36.10 (#535)
Bumps google.golang.org/protobuf from 1.36.8 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-06 15:17:34 -04:00
PietroPasotti eab6c910a6
add arm build for snap (#534) 2025-09-29 09:41:10 -04:00
dependabot[bot] 60e53f304f
Bump google.golang.org/protobuf from 1.36.7 to 1.36.8 (#527)
Bumps google.golang.org/protobuf from 1.36.7 to 1.36.8.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-25 19:09:35 -04:00
dependabot[bot] f093930c85
Bump google.golang.org/protobuf from 1.36.6 to 1.36.7 (#525)
Bumps google.golang.org/protobuf from 1.36.6 to 1.36.7.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 12:09:18 -04:00
Mikel Olasagasti Uranga 7ad93a42d9
test: Update TLS error checks for Go 1.25 compatibility (#522)
The error message for client certificate failures changed in Go 1.25.
Update tests to check for both the old ("bad certificate") and new
("handshake failure") error strings to support multiple Go versions.

Signed-off-by: Mikel Olasagasti Uranga <mikel@olasagasti.info>
2025-07-24 18:11:12 -04:00
dependabot[bot] 7155fb6211
Bump golang.org/x/oauth2 from 0.14.0 to 0.27.0 in the go_modules group (#520)
Bumps the go_modules group with 1 update: [golang.org/x/oauth2](https://github.com/golang/oauth2).


Updates `golang.org/x/oauth2` from 0.14.0 to 0.27.0
- [Commits](https://github.com/golang/oauth2/compare/v0.14.0...v0.27.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-version: 0.27.0
  dependency-type: indirect
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-18 14:47:35 -04:00
dependabot[bot] f28d506cea
Bump golang.org/x/net from 0.36.0 to 0.38.0 in the go_modules group (#517)
Bumps the go_modules group with 1 update: [golang.org/x/net](https://github.com/golang/net).


Updates `golang.org/x/net` from 0.36.0 to 0.38.0
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 20:05:23 -04:00
dependabot[bot] 58ccc6321e
Bump google.golang.org/protobuf from 1.36.5 to 1.36.6 (#515)
* Bump google.golang.org/protobuf from 1.36.5 to 1.36.6

Bumps google.golang.org/protobuf from 1.36.5 to 1.36.6.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix?

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Scott Blum <dragonsinth@gmail.com>
2025-04-09 11:11:29 -04:00
dependabot[bot] b519ffc959
Bump golang.org/x/net from 0.33.0 to 0.36.0 in the go_modules group (#511)
Bumps the go_modules group with 1 update: [golang.org/x/net](https://github.com/golang/net).


Updates `golang.org/x/net` from 0.33.0 to 0.36.0
- [Commits](https://github.com/golang/net/compare/v0.33.0...v0.36.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-09 11:01:11 -04:00
PietroPasotti 3a8fa31879
Snap: strict confinement, stable grade and docs (#514) 2025-03-18 23:01:48 -04:00
zhyuri 614b1687cf
Restore Unix socket support (#498)
https://github.com/fullstorydev/grpcurl/issues/496

Restore -unix to working properly by default with the default dialer.
2025-03-18 08:47:55 -04:00
Gustavo Passini 30f87c1323
Set GOWORK=off and GOPATHS=-trimpath when building (#513) 2025-03-17 16:42:39 -04:00
PietroPasotti 78655b4786
snap init (#512) 2025-03-13 10:13:48 -04:00
Joshua Humphries d00c28104b
avoid overflow in various timeouts (#505) 2025-02-19 11:36:35 -05:00
dependabot[bot] 9e3e083f29
Bump google.golang.org/protobuf from 1.36.4 to 1.36.5 (#509)
Bumps google.golang.org/protobuf from 1.36.4 to 1.36.5.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-10 18:03:32 -05:00
dependabot[bot] c32936d71e
Bump golang.org/x/net from 0.25.0 to 0.33.0 (#508)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.25.0 to 0.33.0.
- [Commits](https://github.com/golang/net/compare/v0.25.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-28 18:44:47 +00:00
dependabot[bot] f3c8ec2564
Bump google.golang.org/protobuf from 1.34.2 to 1.36.4 (#506)
* Bump google.golang.org/protobuf from 1.34.2 to 1.36.4

Bumps google.golang.org/protobuf from 1.34.2 to 1.36.4.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* ignore deprecations

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Scott Blum <dragonsinth@gmail.com>
2025-01-28 18:39:59 +00:00
Joshua Humphries 7e1a6c9068
Update to v1.17.0 of jhump/protoreflect (#502)
* update to v1.17.0 of jhump/protoreflect

* add lint:ignore annotations to fix CI
2025-01-13 10:33:18 -05:00
Edward Newman b9a11e9fea
update core Go version in go.mod to 1.21 (#495) 2024-11-28 11:13:36 -05:00
Or Shachar bc5cf811a0
chore: update to Golang 1.23 (#485)
* chore: update to Golang 1.23

* chore: configure cci to build with 1.23

Signed-off-by: Or Shachar <orchoock@gmail.com>

* Update config.yml

---------

Signed-off-by: Or Shachar <orchoock@gmail.com>
Co-authored-by: Scott Blum <dragonsinth@gmail.com>
2024-10-25 13:22:52 +00:00
ironhide0396 fb49f049e6
Update README.md (#353)
Updating README to add information about  the usage of headers/metadata in the rpc request.
2024-10-11 10:40:52 -04:00
Nathan Mische cdb43b08fa
Remove custom dialer in order to use the default grpc-go dialer, which supports proxies. (#480) 2024-10-03 11:43:25 -04:00
Scott Blum 56181ba330
makefile updates (#484) 2024-09-09 09:53:40 -04:00
Hector Oliveros 46c38b351a
feat: Add functionality to export proto files (#475)
* Add functionality to export proto files

Added a new function, `WriteProtoFiles` in `desc_source.go` which is used to generate .proto files. The process involves resolving symbols from the descriptor source and writing their definitions to a designated output directory. The corresponding flag `--proto-out` has been included in `grpcurl.go` to allow users to specify the directory path.

* Refactor file creation error handling

The code for file creation and error handling in desc_source.go has been refactored. Previously, the file closure operation was executed irrespective of whether the file was created successfully or not. Now, the file will only be closed if it was successfully created, improving error handling.

* Update 'proto-out' command to 'proto-out-dir'

The command for exporting proto files and setting the output directory has been updated from 'proto-out' to 'proto-out-dir'. This change has been made both in the README and the grpcurl go file. This makes the command name more descriptive, accurately reflecting its functionality.

* fix import sort

* Reorder file close operation in error handling

The file close operation has been moved within the error handling of the 'PrintProtoFile' function. Previously, it was being executed before this function, now it's executed immediately after. Moreover, an additional close operation has been added after the function success ensuring the file is properly closed in all scenarios.

* Update grpcurl commands in README

The grpcurl commands for exporting proto files and protoset files in the README are updated. The IP address has been changed to localhost and port number to '8787'. Also, the service name is adjusted to 'my.custom.server.Service'. Instructions for use of specific command options are added for enhanced clarity.

* Refactor error handling in file creation

The code responsible for error handling during file creation in the desc_source.go file has been streamlined. This modification simplifies the code by reducing unnecessary condition checks and redundant file closure action after an error has occurred.

* Refactor proto file writing into separate function

The file writing process for protobuf files has been extracted into a new function called writeProtoFile(). This refactoring simplifies the main function. The code is cleaner and more manageable this way, improving maintainability and readability.

* Refactor writeProtoFile function for better error handling

Streamlined the writeProtoFile function in desc_source.go file. Simplified path calculations and improved error messages for file-creation functions, making it easier to trace the exact point of failure and enhance the debugging process.
2024-07-12 12:47:32 -04:00
dependabot[bot] a05d48d6dd
Bump google.golang.org/protobuf from 1.34.1 to 1.34.2 (#470)
Bumps google.golang.org/protobuf from 1.34.1 to 1.34.2.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-17 14:39:52 -04:00
Philipp Glaser fc63514da1
add 32bit arm build targets (#461) 2024-06-17 11:00:36 -04:00
dependabot[bot] e14d9f769a
Bump google.golang.org/protobuf from 1.34.0 to 1.34.1 (#465)
Bumps google.golang.org/protobuf from 1.34.0 to 1.34.1.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 12:43:39 -04:00
Violin 239dde4a62
feat: introduce transparent client side health check (#463) 2024-05-08 10:47:02 -04:00
dependabot[bot] 80e833a557
Bump google.golang.org/protobuf (#460)
Bumps google.golang.org/protobuf from 1.33.1-0.20240408130810-98873a205002 to 1.34.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-07 08:37:54 -04:00
dependabot[bot] 6fccd7757e
Bump golang.org/x/net from 0.22.0 to 0.23.0 (#458)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.22.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 09:38:45 -04:00
Gustavo Passini 400fa5f2d3
Update Dockerfile to use Go v1.21 (#455) 2024-04-10 11:33:24 -04:00
goodfirm 0e13e85e65
chore: fix some typos in comments (#454)
Signed-off-by: goodfirm <fanyishang@yeah.net>
2024-04-10 09:51:09 -04:00
Joshua Humphries 07361b21ea
Use latest protoreflect to fix some bugs (#453)
* update to latest jhump/protoreflect

* be lenient when possible if server cannot furnish all dependencies

* move linting back to go 1.21 instead of latest go 1.22

* make staticcheck happy
2024-04-09 17:50:10 -04:00
Gustavo Passini 8e76884d21
Brand name update (#452) 2024-04-09 16:19:28 -04:00
dependabot[bot] 805ce40c63
Bump github.com/golang/protobuf from 1.5.3 to 1.5.4 (#448)
Bumps [github.com/golang/protobuf](https://github.com/golang/protobuf) from 1.5.3 to 1.5.4.
- [Release notes](https://github.com/golang/protobuf/releases)
- [Commits](https://github.com/golang/protobuf/compare/v1.5.3...v1.5.4)

---
updated-dependencies:
- dependency-name: github.com/golang/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-11 08:31:42 -04:00
Albin Parou 93ea011b36
goreleaser: bump version & add nfmp support (#440) 2024-02-26 09:09:32 -05:00
Paul Chesnais 5592211a41
Enable xDS credentials (#424)
* Enable xDS credentials

This change should be relatively straightforward. It is a noop outside of the
context of xDS (as demonstrated by the fact that the tests all pass), but it
enables xDS-provided certificates (i.e. the ones that would be
provided/specified in GRPC_XDS_BOOTSTRAP). See proposal
[A29](https://github.com/grpc/proposal/blob/master/A29-xds-tls-security.md#go)
for additional detail.

* Only enable xds credentials if the target is an xDS target

* Update after merge
2024-02-12 13:49:16 -05:00
dependabot[bot] 184c8f70b5
Bump github.com/jhump/protoreflect from 1.15.5 to 1.15.6 (#446)
Bumps [github.com/jhump/protoreflect](https://github.com/jhump/protoreflect) from 1.15.5 to 1.15.6.
- [Release notes](https://github.com/jhump/protoreflect/releases)
- [Commits](https://github.com/jhump/protoreflect/compare/v1.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: github.com/jhump/protoreflect
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-07 12:30:33 -05:00
Kristopher Wuollett 149a93e0ec
Use localhost for default unix domain socket authority (#445) 2024-01-30 11:38:15 -05:00
dependabot[bot] 252b57fd45
Bump github.com/jhump/protoreflect from 1.15.4 to 1.15.5 (#443)
Bumps [github.com/jhump/protoreflect](https://github.com/jhump/protoreflect) from 1.15.4 to 1.15.5.
- [Release notes](https://github.com/jhump/protoreflect/releases)
- [Commits](https://github.com/jhump/protoreflect/compare/v1.15.4...v1.15.5)

---
updated-dependencies:
- dependency-name: github.com/jhump/protoreflect
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-29 13:38:20 -05:00
KaibutsuX 24b80dfed8
Added initial support for -t flag to show timings (#428)
* Added initial support for -t flag to show timings

Shows very basic timing data for the Dial stage (TLS setup and
BlockingDial) and InvokeRPC method as well as the total time.

* Made timing data part of the very verbose functionality

* cleanup

* fix

---------

Co-authored-by: Scott Blum <dragonsinth@gmail.com>
2024-01-23 19:04:51 -05:00
Michael Diamond 334e3f56de
Expand the documentation of -max-time to clarify this sets the RPC timeout (#435)
Notably, this adds the words "timeout" and "deadline" to this help text which is helpful for discovering this flag.
2024-01-09 11:39:10 -05:00
dependabot[bot] f4157743ed
Bump github.com/jhump/protoreflect from 1.15.3 to 1.15.4 (#436)
Bumps [github.com/jhump/protoreflect](https://github.com/jhump/protoreflect) from 1.15.3 to 1.15.4.
- [Release notes](https://github.com/jhump/protoreflect/releases)
- [Commits](https://github.com/jhump/protoreflect/compare/v1.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: github.com/jhump/protoreflect
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-08 16:59:50 -05:00
dependabot[bot] 79fb35f680
Bump google.golang.org/protobuf from 1.31.0 to 1.32.0 (#437)
* Bump google.golang.org/protobuf from 1.31.0 to 1.32.0

Bumps google.golang.org/protobuf from 1.31.0 to 1.32.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* go mod tidy?

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Scott Blum <dragonsinth@gmail.com>
2023-12-26 09:20:30 -05:00
Scott Blum 7ccaf0a21f indent: rip out old go 1.9 support 2023-11-22 08:54:31 -05:00
ns-yuhanl 6093b09afa
Bump golang.google.org/grpc to v1.57.1 (#427)
* Bump golang.google.org/grpc to v1.57.1

	See https://github.com/advisories/GHSA-m425-mq94-257g

* Revert "Bump golang.google.org/grpc to v1.57.1"

This reverts commit 385868b36b.

* Bump golang.google.org/grpc to v1.57.1
2023-10-26 12:39:52 -04:00
Gustavo Passini 70c215f7e2
Update Tarball URL used by Homebrew (#421) 2023-10-24 15:33:15 -04:00
Gustavo Passini 28c0ee28f0
Disable CGO for improved compatibility across distros (#420)
* Disable CGO for improved compatibility across distros

* Enabled CGO in tests for the race detector
2023-10-24 11:53:47 -04:00
dependabot[bot] bc2944de97
Bump golang.org/x/net from 0.9.0 to 0.17.0 (#419)
* Bump golang.org/x/net from 0.9.0 to 0.17.0

Bumps [golang.org/x/net](https://github.com/golang/net) from 0.9.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.9.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* manual go.sum

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Scott Blum <dragonsinth@gmail.com>
2023-10-12 14:45:27 +00:00
Michael Primeaux 7a845ca5e9
SIGSEGV: panic: runtime error: invalid memory address or nil pointer dereference in protoreflect (#416)
* SIGSEGV: panic: runtime error: invalid memory address

- Executed the updatedeps make target
- protoreflect upgraded to v1.15.3, which addresses this panic.

* Reverted. Only updated protoreflect.
2023-10-03 16:42:07 -04:00
Jonathan Beaulieu c17f0782f7
Added alts credential option (#341)
* Added alts credential option

Added flag that sets up ALTS credentials to connect to gRPC services using Application Layer Transport Security.

Reference: https://grpc.io/docs/languages/go/alts/
Fixes: #333

* Added ALTS client options as cmd arguments.

* Update target service accounts docs

Co-authored-by: Joshua Humphries <2035234+jhump@users.noreply.github.com>

* Applied feedback

* Small fix: removed indirection on usetls

---------

Co-authored-by: Joshua Humphries <2035234+jhump@users.noreply.github.com>
2023-09-25 13:57:49 -04:00
Scott Blum 42f63028d4
Update go.mod, goreleaser for v1.8.8 (#413)
* Update go.mod, goreleaser for v1.8.8

* move ci check to go 1.20

* debug

* ci diff
2023-09-22 16:21:54 -04:00
Valters Jansons 743e60a4c9
Run tests on Go 1.21 (#408)
* Run tests on Go 1.21

For the most part, there are no breaking changes.

However, the expired certificate is now showing "expired certificate"
although previously it showed a simpler "bad certificate" which was
hard-coded into the TLS settings test scenario.

* Simplify condition for certificate error

Instead of two `expired certificate` and `bad certificate` comparisons, we can just check for `certificate` in error output. This satisfies us when checking there is something wrong with the certificate.

Co-authored-by: Scott Blum <dragonsinth@gmail.com>

---------

Co-authored-by: Scott Blum <dragonsinth@gmail.com>
2023-08-31 12:14:34 -04:00
Valters Jansons b7a5d3bba8
Update `protoreflect` v1.15.2 and `grpc` v1.57.0 (#406)
The proposal already exists by Dependabot, but the version bump pulls
in `grpc.reflection.v1.ServerReflection` resulting in minor test change
being required.

Transient dependency `github.com/bufbuild/protocompile` is now added,
and requires at least Go 1.18. This PR includes the version removal.
2023-08-31 12:14:03 -04:00
Valters Jansons 2a29c1e64b
Use `grpc.reflection.v1.ServerReflection` (#407)
The proper `v1` gRPC reflection has been around for a bit.
The "client auto" mechanism still supports falling back to `v1alpha`.
Not many support the `v1`, but we should default to it when possible.
2023-08-31 12:13:33 -04:00
dependabot[bot] 9a59bed1d2
Bump google.golang.org/protobuf from 1.30.0 to 1.31.0 (#401)
Bumps google.golang.org/protobuf from 1.30.0 to 1.31.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-26 12:05:13 -04:00
dependabot[bot] ae7dadff19
Bump google.golang.org/grpc from 1.55.0 to 1.56.1 (#400)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.55.0 to 1.56.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.55.0...v1.56.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-26 11:38:28 -04:00
Sophy Cao 3961a33e7f
Fix issues with error details (#379)
Co-authored-by: Erik Engberg <ejohansson@spotify.com>
2023-06-22 12:58:51 -04:00
Joshua Humphries d5b8e4d4ce
fix nil-dereference panic (#395) 2023-05-12 12:50:32 -04:00
dependabot[bot] 0efcfa65f2
Bump google.golang.org/grpc from 1.54.0 to 1.55.0 (#390)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.54.0 to 1.55.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.54.0...v1.55.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-09 20:08:41 -04:00
Joshua Humphries fae58803d9
Add "checkgenerate" make target to CI (#385) 2023-04-18 12:09:16 -04:00
dependabot[bot] 1fda47eb90
Bump google.golang.org/grpc from 1.53.0 to 1.54.0 (#383)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.53.0 to 1.54.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.53.0...v1.54.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-27 10:01:15 -04:00
dependabot[bot] 031cd3d1e7
Bump google.golang.org/protobuf from 1.29.1 to 1.30.0 (#378)
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.29.1 to 1.30.0.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.29.1...v1.30.0)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-20 09:58:32 -04:00
dependabot[bot] a5037bdf4a
Bump google.golang.org/protobuf from 1.29.0 to 1.29.1 (#376)
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.29.0 to 1.29.1.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.29.0...v1.29.1)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-14 23:11:32 +00:00
dependabot[bot] 4775fb574b
Bump google.golang.org/protobuf from 1.28.1 to 1.29.0 (#375)
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.28.1 to 1.29.0.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.28.1...v1.29.0)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-13 09:12:47 -04:00
dependabot[bot] b96cb4ddd0
Bump github.com/golang/protobuf from 1.5.2 to 1.5.3 (#374)
Bumps [github.com/golang/protobuf](https://github.com/golang/protobuf) from 1.5.2 to 1.5.3.
- [Release notes](https://github.com/golang/protobuf/releases)
- [Commits](https://github.com/golang/protobuf/compare/v1.5.2...v1.5.3)

---
updated-dependencies:
- dependency-name: github.com/golang/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-13 09:11:31 -04:00
dependabot[bot] 006918123d
Bump google.golang.org/grpc from 1.52.3 to 1.53.0 (#370)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.52.3 to 1.53.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.52.3...v1.53.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-13 17:08:49 -05:00
codesee-maps[bot] 66a2405833
Install the CodeSee workflow. Learn more at https://docs.codesee.io (#368)
Co-authored-by: codesee-maps[bot] <86324825+codesee-maps[bot]@users.noreply.github.com>
2023-02-07 15:24:26 -05:00
dependabot[bot] dfd889a44b
Bump google.golang.org/grpc from 1.51.0 to 1.52.3 (#365)
* Bump google.golang.org/grpc from 1.51.0 to 1.52.3

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.51.0 to 1.52.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.51.0...v1.52.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* update test assertion for latest version of grpc-go (#366)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Joshua Humphries <2035234+jhump@users.noreply.github.com>
2023-01-30 20:35:03 -05:00
dependabot[bot] a3a5bcd8ab
Bump github.com/jhump/protoreflect from 1.14.0 to 1.14.1 (#361)
Bumps [github.com/jhump/protoreflect](https://github.com/jhump/protoreflect) from 1.14.0 to 1.14.1.
- [Release notes](https://github.com/jhump/protoreflect/releases)
- [Commits](https://github.com/jhump/protoreflect/compare/v1.14.0...v1.14.1)

---
updated-dependencies:
- dependency-name: github.com/jhump/protoreflect
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-16 09:15:25 -05:00
dependabot[bot] 61a9c00d87
Bump google.golang.org/grpc from 1.50.1 to 1.51.0 (#348)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.50.1 to 1.51.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.50.1...v1.51.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-22 11:01:49 -05:00
cui fliter 85f1cbf7ad
fix funcname in comment (#346)
Signed-off-by: cui fliter <imcusg@gmail.com>

Signed-off-by: cui fliter <imcusg@gmail.com>
2022-11-09 07:59:56 -05:00
dependabot[bot] dd2f60135c
Bump github.com/jhump/protoreflect from 1.13.0 to 1.14.0 (#343)
* Bump github.com/jhump/protoreflect from 1.13.0 to 1.14.0

Bumps [github.com/jhump/protoreflect](https://github.com/jhump/protoreflect) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/jhump/protoreflect/releases)
- [Commits](https://github.com/jhump/protoreflect/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: github.com/jhump/protoreflect
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix ci

* what can we even build on?

* stop testing old things

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Scott Blum <dragonsinth@gmail.com>
2022-11-01 10:26:55 -04:00
dependabot[bot] 81c624c41f
Bump google.golang.org/grpc from 1.50.0 to 1.50.1 (#338)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.50.0 to 1.50.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.50.0...v1.50.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-17 12:49:30 -04:00
dependabot[bot] 3826617999
Bump google.golang.org/grpc from 1.49.0 to 1.50.0 (#336)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.49.0 to 1.50.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.49.0...v1.50.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-10 12:31:11 -04:00
dependabot[bot] 8d60b618ac
Bump github.com/jhump/protoreflect from 1.12.0 to 1.13.0 (#335)
Bumps [github.com/jhump/protoreflect](https://github.com/jhump/protoreflect) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/jhump/protoreflect/releases)
- [Commits](https://github.com/jhump/protoreflect/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/jhump/protoreflect
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 11:26:42 -04:00
dependabot[bot] 47b842a805
Bump google.golang.org/grpc from 1.48.0 to 1.49.0 (#330)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.48.0 to 1.49.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.48.0...v1.49.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-29 09:36:26 -04:00
Scott Blum 898bdad041
fixup release process (#328) 2022-08-10 14:25:13 -04:00
dependabot[bot] 25c896aa59
Bump google.golang.org/protobuf from 1.28.0 to 1.28.1 (#327)
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.28.0 to 1.28.1.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.28.0...v1.28.1)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-08 12:26:59 -04:00
Scott Blum 0d0992e6a2
Add go 1.18 support; set Dockerfile to go 1.18 (#325) 2022-07-25 11:54:27 -04:00
Ben Redmond-Benham 8093376ece
build alpine base image (#311) 2022-07-20 14:58:00 -04:00
cui fliter fec466efa6
fix some typos (#314)
Signed-off-by: cuishuang <imcusg@gmail.com>
2022-07-20 14:57:53 -04:00
dependabot[bot] 7f919e6459
Bump google.golang.org/grpc from 1.47.0 to 1.48.0 (#324)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.47.0 to 1.48.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.47.0...v1.48.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-18 13:22:36 -04:00
ghatwala 0df5c93e8d
Adding power(ppc64le) arch support (#296)
Needing power(ppc64le) arch based "grpcurl" binaries here https://docs.openshift.com/container-platform/4.9/operators/admin/olm-restricted-networks.html
1. Adding power support for releases and docker images(multi-arch).

signed-off-by : Amit Ghatwal <ghatwala@us.ibm.com>
2022-06-27 11:10:08 -04:00
Lyubomir Gardev 353e0953cb
Enable support for Unix sockets for Windows by enabling -unix flag for Windows builds. (#317) 2022-06-20 09:37:33 -04:00
dependabot[bot] c9ac3da95f
Bump google.golang.org/grpc from 1.46.2 to 1.47.0 (#315)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.46.2 to 1.47.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.46.2...v1.47.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-06 08:08:43 -04:00
dependabot[bot] ea24a77215
Bump github.com/jhump/protoreflect from 1.10.3 to 1.12.0 (#294)
Bumps [github.com/jhump/protoreflect](https://github.com/jhump/protoreflect) from 1.10.3 to 1.12.0.
- [Release notes](https://github.com/jhump/protoreflect/releases)
- [Commits](https://github.com/jhump/protoreflect/compare/v1.10.3...v1.12.0)

---
updated-dependencies:
- dependency-name: github.com/jhump/protoreflect
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-02 16:18:22 -04:00
dependabot[bot] c3fbeaff64
Bump google.golang.org/grpc from 1.44.0 to 1.46.2 (#310)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.44.0 to 1.46.2.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.44.0...v1.46.2)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Scott Blum <dragonsinth@gmail.com>
2022-06-02 16:10:40 -04:00
dependabot[bot] 7860209a53
Bump google.golang.org/protobuf from 1.27.1 to 1.28.0 (#298)
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.27.1 to 1.28.0.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.27.1...v1.28.0)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-02 15:56:21 -04:00
Joshua Humphries b953ea196a
use newer goreleaser (#293) 2022-02-15 20:31:31 -05:00
Joshua Humphries 683a7fb09c
Restore support for linux/s390x for the next release. (#292)
This reverts commit 8ee6c9423b.
2022-02-15 19:03:59 -05:00
dependabot[bot] 8bb6eeb0d0
Bump google.golang.org/protobuf from 1.26.0 to 1.27.1 (#288)
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.26.0 to 1.27.1.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.26.0...v1.27.1)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-09 14:27:46 -05:00
Joshua Humphries 805e231182
bump protoreflect again, to 1.10.3 (#290) 2022-02-08 16:49:23 -05:00
Joshua Humphries aa5998a119
no longer supporting go 1.14 (#289) 2022-02-07 12:03:53 -05:00
Jeff Widman b34b13bab3
Bump grpc lib and fix deprecations (#286)
* Bump google.golang.org/grpc from 1.37.0 to 1.44.0

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.37.0 to 1.44.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.37.0...v1.44.0)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-05 10:28:05 -05:00
Jeff Widman b890db745f
Bump protobuf lib and fix deprecation warnings (#285)
* Bump github.com/golang/protobuf from 1.4.2 to 1.5.2

Bumps [github.com/golang/protobuf](https://github.com/golang/protobuf) from 1.4.2 to 1.5.2.
- [Release notes](https://github.com/golang/protobuf/releases)
- [Commits](https://github.com/golang/protobuf/compare/v1.4.2...v1.5.2)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---

* Also pulls along update of golang.google.org/protobuf to v1.26
* Fix deprecation warnings about `ptypes` package
2022-02-04 17:19:52 -05:00
Vladyslav Shalamai 57a2cd1a09
bump github.com/jhump/protoreflect from v1.10.1 to v1.10.2 (#284) 2022-02-04 10:15:18 -05:00
wwuck ae7473c7a7
add dependabot configuration (#267) 2022-01-12 09:57:41 -05:00
Joshua Humphries b3f576068c
update golang.org/x/text to v0.3.7 (#275) 2022-01-10 15:18:54 -05:00
Joshua Humphries 8ee6c9423b
can't build s390x docker images; skip for now (#265) 2021-10-07 16:37:41 -04:00
Joshua Humphries 4b65b3ee55
fix release script: 'docker buildx create' step needs to be re-runnable without error (#264) 2021-10-07 16:10:47 -04:00
Jeffrey Duce 9ac7e3a34d
set value of creds instead of creating new variable (#263) 2021-10-07 15:49:59 -04:00
Joshua Humphries 76bbedeed0
invoking client-streaming RPC can panic (#262) 2021-10-07 14:55:12 -04:00
Joshua Humphries b9d2d8cfa8
cleanup deps; pin versions of tools using 'go install' instead of in go.mod; tidy up go.sum (#260)
Addresses two depend-a-bot security warnings for libraries previously references in go.sum.
2021-10-06 20:58:00 -04:00
Bjarne Sievers 30b8cd1531
Add info about docker pitfalls to README (#258) 2021-10-06 15:25:18 -04:00
Joshua Humphries a8f79c8751
use latest protoreflect (#259) 2021-10-06 09:39:01 -04:00
Joshua Humphries bf9b13d6c5
update install from source directions to work with Go 1.16 2021-09-23 10:37:13 -04:00
Joshua Humphries c3bde04cc1
use circleci instead of travis (#256) 2021-09-23 09:20:48 -04:00
Namrata Bhave 2e9ba5024e
Build multiarch docker images for release: amd, s390x, arm (#251) 2021-09-23 07:55:01 -04:00
Igor 127194b205
Support SSLKEYLOGFILE environment variable for key logging (#245) 2021-09-20 11:53:44 -04:00
Namrata Bhave cd242fe1ed
build release binaries for linux/s390x (#248) 2021-08-18 08:53:40 -04:00
Sunil Thorat 59a32e5eb0
Upgrade to using golang 1.15 to build (#240) 2021-07-14 10:24:01 -04:00
Joshua Humphries 7e1cd16164
use newer protoreflect, v1.9.0 (#242) 2021-07-13 11:53:16 -04:00
Joshua Humphries de25c89822
allow callers of BlockingDial to override grpc.FailOnNonTempDialError dial option (#241) 2021-07-12 15:27:33 -04:00
Joshua Humphries f1d396c31e
freshen README, point to 3rd party packages for install (#229) 2021-05-04 12:03:54 -04:00
Josh Humphries 1986364acd make go.sum authoritative 2021-04-30 17:15:18 -04:00
Erik Johansson bdf97bc934
Bump grpc-go to 1.37.0 (#230)
Pulls in support for XDS v3
2021-04-29 10:48:41 -04:00
Z. Liu 2f55ac63a4
release binaries for arm64 (#220) 2021-04-02 08:06:16 -04:00
Joshua Humphries 8d7770a962
use latest version of protoreflect (#212) 2021-02-22 17:32:40 -05:00
Leonhard Markert 1f34448998
Help text for "-H" option: remove duplicated word (#207) 2021-02-01 10:31:49 -05:00
Josh Humphries 06a970022e darwin/386... 🤦 2021-01-05 12:42:15 -05:00
Josh Humphries db90ec1160 go 1.15 no longer supports darwin/386; ignore any IDE config folder 2021-01-05 12:39:34 -05:00
Mikhail Katychev 9da71fbe53
when -format-error option is used, format service reflection errors, too (#188) 2020-09-30 13:24:46 -04:00
Guilherme Salazar 9846afccbc
feat: add support for user-agent header (#182) 2020-09-04 11:31:28 -04:00
Joshua Humphries ba5f667e13
fix latest CI breakages by forking code from grpc-go's interop/testing
Also moves testing package to internal/testing
2020-08-31 14:44:59 -04:00
Joshua Humphries 54ffdcacda
fix typo 2020-08-24 13:58:09 -04:00
Joshua Humphries ceef817807
make use of -plaintext flag more clear in examples 2020-08-24 13:56:57 -04:00
Sergei Vorobev e544b9e66f
Print size of the message in very verbose mode (#181)
Adds -vv flag for "very verbose".
Includes estimated message size when enabled. The size is an estimate because it is the canonical size for the proto message, but not necessarily its actual on-the-wire size.
2020-08-14 11:02:42 -04:00
Joshua Humphries 41a6ac0479
no more support for go 1.11 without modules (#180) 2020-07-31 12:26:31 -04:00
Joshua Humphries f37ec641c5
make release work... (#179) 2020-07-30 22:10:03 -04:00
Adam Babik 8e51c5e2d3
cmd/grpcurl: add -allow-unknown-fields option (#147) 2020-07-23 10:54:06 -04:00
Joshua Humphries 8376c2f7bb
update `go get` line in README
Go tool needs to also gets dependencies for sub-packages
2020-07-20 12:46:55 -04:00
Joshua Humphries 0162fa9726
go.mod and Makefile tweaks; move import of xds package to cmd/grpcurl (#170) 2020-07-14 12:20:32 -04:00
Erik Johansson b8c67b7a4e
Bump grpc-go to 1.30.0 (#168)
* Bump grpc-go to 1.30.0
* Import new xds package instead of experimental

Co-authored-by: Erik Johansson <ejohansson@spotify.com>
2020-07-13 13:16:48 -04:00
Joshua Humphries ff114930fd
update travis badge and link 2020-07-09 20:53:23 -04:00
Joshua Humphries 5ad5edb29c
pin goreleaser in go.mod; fix config file so latest version accepts it; allow using go modules in 'make release' (#161) 2020-06-01 18:56:18 -04:00
Joshua Humphries 44547153b3
unpin Go point release and alpine version in Dockerfile (#162) 2020-05-15 11:40:32 -04:00
Joshua Humphries 2108c8f0b3
use go 1.13 to build docker image (#160) 2020-05-15 08:21:08 -04:00
Mikhail Katychev 36008aa111
Add -format-error option (#155) 2020-05-14 20:40:13 -04:00
Joshua Humphries 50833f1b21
upgrade deps; remove go 1.14 from go.mod (#158) 2020-05-14 20:00:04 -04:00
Joshua Humphries 939766fb42
Go tip does not like using string(i) where i untyped int (#159) 2020-05-14 19:38:08 -04:00
Joshua Humphries b58182a88d
get travis CI green again (#157)
* fix test to work w/ newer grpc repo; run 'make ci' to have it update go.mod and go.sum, trying to repro ci failure locally
* vet w/ go 1.13; use normal go proxy instead of direct in CI
2020-05-12 13:06:14 -04:00
Serge Bazanski 8e2cf9b3c2
fix crash when emitting empty messages in text format (#153) 2020-04-23 16:20:45 -04:00
Alexander Chiu 36f9e53dfd
Allow extra descriptor sources in reflection mode (#146)
Allows descriptors or proto files to be supplied that are used as an additional descriptor source when server reflection is used. This is needed to resolve message types embedded in an Any message, that the server has no knowledge of, as well as extensions that the server has no knowledge of.
2020-04-20 15:42:11 -04:00
Joshua Humphries bfbbed1d42
vet with modules (#150)
fixes CI failures caused by newer version of dependency introducing new deprecations that cause staticcheck violations
2020-04-16 15:27:01 -04:00
Johan Brandhorst 153d36db8c
Remove golang/protobuf/jsonpbtest dep (#145)
With 1.4.0 the golang/protobuf json testing protofiles
are being moved to [an internal package][1]. While this is
technically a breaking change, I doubt the maintainers
are going to reintroduce it, so I've copied the necessary
parts to our own testing protofile.

This allows users of this package and, more importantly,
grpcui to try 1.4.0 without this package having to migrate yet.
When it comes to migrating to 1.4.0, this should also make the
transition easier.

[1]: https://github.com/golang/protobuf/tree/v1.4.0-rc.4/internal/testprotos
2020-04-03 12:45:05 -04:00
Joshua Humphries 2af40876fc
drop Go 1.9 and 1.10 from CI; add 1.13 and 1.14 (#144) 2020-04-01 09:02:40 -04:00
Joshua Humphries 0218a7db67
add script and readme for creating new releases (#140) 2020-03-18 08:51:18 -04:00
Richard Belleville 96cfd48e32
Activate xDS support in grpcurl (#137) 2020-03-13 16:29:41 -04:00
Richard Belleville d30f3a01b7
Bump grpc to 1.28 (#136) 2020-03-12 18:55:09 -04:00
Joshua Humphries 0d669e78d0
use wrapped TransportCredentials instead of handling handshake in Dialer (#130)
* use wrapped TransportCredentials instead of handling handshake in Dialer, so that grpc library will use correct :scheme
* support -authority for TLS conns; now effectively supercedes -servername flag
2020-01-27 12:15:47 -05:00
Blake Williams 9572bd4525 Register gzip decoder (#124) 2019-11-26 08:55:50 -05:00
Joshua Humphries ccc9007156
add -protoset-out option (#120) 2019-09-30 09:50:17 -04:00
J M 9248ea0963 Add Expand Headers Feature (#117) 2019-09-26 17:26:38 -04:00
Joshua Humphries 4054d1d115
fix go.mod for Go 1.13 (#110) 2019-08-09 14:17:53 -04:00
Joshua Humphries 5631bba117
add official Dockerfile (#104) 2019-07-03 15:57:24 -04:00
Joshua Humphries 80425d1b17
use protoreflect 1.4.4 (#109) 2019-07-03 15:56:51 -04:00
Joshua Humphries 7e4045565f
update all deps; use new ResolveFilenames method (#103) 2019-05-24 10:26:38 -04:00
Joshua Humphries e5b4fc6cc0
add API to expose AnyResolver implementations backed by a DescriptorSource (#102) 2019-05-22 21:38:46 -04:00
Joshua Humphries 09c3d1d69e
use just-released v1.3.0 protoreflect (#101) 2019-05-22 16:20:41 -04:00
Joshua Humphries 5d6316f470
Adds support for showing error details (#98)
To better support printing of google.protobuf.Any messages (error details), this
also makes a few other changes:
1. Allows printing of unresolvable Any messages using an "@value" field in JSON output
   that has the base64-encoded embedded message data.
2. Improves support for "-format text" to show expanded Any messages if possible.
   (Due to limitations in underlying proto package, this will usually *not* be
   that helpful. But this should greatly improve with v2 of the go protobuf API.)
3. Addresses a TODO in existing AnyResolver code to lazily fetch descriptors
   as needed instead of having to download all files eagerly.
2019-04-09 09:34:39 -04:00
Joshua Humphries f0723c6273
fix flaky test (#93) 2019-03-25 10:34:32 -04:00
Joshua Humphries fe97274a1b
staticcheck no longer runs on Go 1.10 (#92)
* staticcheck no longer runs on Go 1.10, so run it on Go 1.11 in CI
* be explicit about default make target in .travis.yml
2019-03-22 14:35:01 -04:00
CodeLingo Team 1bbf8dae71 Fix function comments based on best practices from Effective Go (#87)
Signed-off-by: CodeLingo Bot <bot@codelingo.io>
2019-03-13 10:58:13 -04:00
Joshua Humphries 0fcd3253f6
latest protoreflect fixes some bugs in JSON marshaling/unmarshaling (#86) 2019-03-07 13:35:49 -05:00
Joshua Humphries 4c9c82cec3
use custom flagset (#85) 2019-02-28 10:58:53 -05:00
Joshua Humphries 5082a1dc68
add -max-msg-sz flag (#84) 2019-02-28 08:35:50 -05:00
Joshua Humphries d641a66208
fix flaky test where code can be 'cancelled' unexpectedly, instead of some error code provided by the server (#83) 2019-02-27 20:28:43 -05:00
Joshua Humphries ce84976d3c
fix typo in readme
fixes #79
2019-02-27 18:16:55 -05:00
Joshua Humphries b292d5aef8
add Go 1.12 to travis config (#82) 2019-02-27 17:31:54 -05:00
Joshua Humphries 5516a45602
update proto and grpc deps (#81)
Fixes the build by updating grpc (and deps) and using new, non-deprecated function
2019-02-27 17:30:44 -05:00
Andrew McCallum 4a329f3b13 add Homebrew installation method to README (#77) 2019-01-23 10:50:39 -05:00
Joshua Humphries 1c6532c060
fix minor issues caught by staticcheck (#76) 2019-01-03 10:09:24 -05:00
Joshua Humphries 0f9e76c978
ignore deprecated check for now (#75) 2018-12-17 10:47:17 -05:00
Joshua Humphries 9fa2fce63b
make method invocation resilient to errors trying to load all files from server (#74) 2018-12-13 11:44:43 -05:00
Joshua Humphries 70e9bba1b8
fix typo in Makefile (#72) 2018-12-12 10:10:23 -05:00
Joshua Humphries d86529bb4f
don't use TLS 1.3, which isn't quite right yet in Go tip (#66) 2018-11-15 20:27:01 -05:00
Joshua Humphries 0dea37ee70
on error reading request, bidi stream would hang (#63) 2018-11-01 14:32:56 -04:00
Joshua Humphries dfa06f4410
several Go libraries, including gRPC and golang.org/x/net/http2, no longer support Go 1.8 and older (#64) 2018-11-01 14:24:33 -04:00
Joshua Humphries 22ce2f04fd
account type in OpenAccount RPC was ignored (#61) 2018-11-01 13:52:58 -04:00
Joshua Humphries 1e8e50f4f8
make MakeTemplate more robust (#60) 2018-10-22 22:59:11 -04:00
Joshua Humphries 7cabe7a9d0
move more stuff from cmd to package (#59) 2018-10-19 14:47:25 -04:00
Joshua Humphries 9a4bbacdd6
some pre-factoring and small fixes (#58)
* organize into multiple files
* make listing methods show fully-qualified names
* address small feedback from recent change (trim then check if empty)
2018-10-18 23:51:38 -04:00
Joshua Humphries 69ea782936
reconcile with recent change to "describe" output 2018-10-18 13:54:51 -04:00
Joshua Humphries 58cd93280e
use proto syntax for describe (#57) 2018-10-18 13:50:44 -04:00
Joshua Humphries a337c1afcf
improve flag doc with go 1.10+ (#56) 2018-10-18 13:16:33 -04:00
Joshua Humphries e00ef3eb7c
add option to support text format (#54)
* augments grpcurl package API in order to handle multiple formats
* deprecates old signature for InvokeRpc
* add command-line flag to use protobuf text format instead of JSON
* use AnyResolver when marshaling to/from JSON
2018-10-16 21:26:16 -04:00
Joshua Humphries 397a8c18ca
update README (#55)
* Revises the sections that talk about descriptor sources, making them more consistent.
* Adds a link to the Gophercon 2018 talk on grpcurl.
* Improve  installation section, mentioning versioned Go.
2018-10-16 12:56:40 -04:00
Joshua Humphries 554e69be2c
use correct package name for golint (#53) 2018-10-12 09:06:35 -04:00
Ryo Nakao 2dd771c49e fix typo in InvokeRpc() comment (#50) 2018-09-26 22:02:34 -04:00
Leigh McCulloch 79a550b858 add goreleaser (#49)
* add goreleaser

Add a goreleaser configuration file that builds binaries for Linux,
MacOS and Windows, for 32bit (x86/386) and 64bit (x64/amd64). The
binaries will be archived into a tar.gz/zip file along with the LICENSE
file.

The `dist/` directory will be written to by goreleaser with the binaries
during the build process, so it's also been added to .gitignore.

To build all the binaries and release them to GitHub:
1. Tag the release. e.g. `git tag -a v1.0.0 -m 'First release'`
2. Generate a GitHub Personal Access Token. See https://github.com/settings/tokens.
3. Push the release to GitHub. e.g. `git push origin v1.0.0`
4. Make the release, which will publish binaries to the GitHub "Releases" page. e.g.  `GITHUB_TOKEN=xxxxxxx... make release`

* add -version flag

Run `grpcurl -version` to see the release version. Use `make install` to build a binary that shows the version based on current git hash (which will show a version number if HEAD is a release tag and otherwise uses `git describe` to summarize the version).
2018-09-25 09:34:02 -04:00
79 changed files with 8748 additions and 3743 deletions

40
.circleci/config.yml Normal file
View File

@ -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

7
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
# Check for updates once a week
schedule:
interval: "weekly"

View File

@ -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 }}

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
dist/
.idea/
VERSION
.tmp/
*.snap

63
.goreleaser.yml Normal file
View File

@ -0,0 +1,63 @@
builds:
- binary: grpcurl
main: ./cmd/grpcurl
goos:
- linux
- darwin
- windows
goarch:
- amd64
- 386
- arm
- arm64
- s390x
- ppc64le
goarm:
- 5
- 6
- 7
ignore:
- goos: darwin
goarch: 386
- goos: windows
goarch: arm64
- goos: darwin
goarch: arm
- goos: windows
goarch: arm
- goos: darwin
goarch: s390x
- goos: windows
goarch: s390x
- goos: darwin
goarch: ppc64le
- goos: windows
goarch: ppc64le
ldflags:
- -s -w -X main.version=v{{.Version}}
archives:
- format: tar.gz
name_template: >-
{{ .Binary }}_{{ .Version }}_
{{- if eq .Os "darwin" }}osx{{ else }}{{ .Os }}{{ end }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}x86_32
{{- else }}{{ .Arch }}{{ end }}
{{- with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}
format_overrides:
- goos: windows
format: zip
files:
- LICENSE
nfpms:
- vendor: Fullstory
homepage: https://github.com/fullstorydev/grpcurl/
maintainer: Engineering at Fullstory <fixme@fixme>
description: 'Like cURL, but for gRPC: Command-line tool for interacting with gRPC servers'
license: MIT
id: nfpms
formats:
- deb
- rpm

View File

@ -1,18 +0,0 @@
language: go
sudo: false
matrix:
include:
- go: "1.7"
- go: "1.8"
- go: "1.9"
- go: "1.10"
env: VET=1
- go: "1.11"
env: GO111MODULE=off
- go: "1.11"
env: GO111MODULE=on
- go: tip
script:
- if [[ "$VET" = 1 ]]; then make; else make deps test; fi

36
Dockerfile Normal file
View File

@ -0,0 +1,36 @@
FROM golang:1.25-alpine AS builder
LABEL maintainer="Fullstory Engineering"
# create non-privileged group and user
RUN addgroup -S grpcurl && adduser -S grpcurl -G grpcurl
WORKDIR /tmp/fullstorydev/grpcurl
# copy just the files/sources we need to build grpcurl
COPY VERSION *.go go.* /tmp/fullstorydev/grpcurl/
COPY cmd /tmp/fullstorydev/grpcurl/cmd
# and build a completely static binary (so we can use
# scratch as basis for the final image)
ENV CGO_ENABLED=0
ENV GO111MODULE=on
RUN go build -o /grpcurl \
-ldflags "-w -extldflags \"-static\" -X \"main.version=$(cat VERSION)\"" \
./cmd/grpcurl
FROM alpine:3 AS alpine
WORKDIR /
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /grpcurl /bin/grpcurl
USER grpcurl
ENTRYPOINT ["/bin/grpcurl"]
# New FROM so we have a nice'n'tiny image
FROM scratch
WORKDIR /
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /grpcurl /bin/grpcurl
USER grpcurl
ENTRYPOINT ["/bin/grpcurl"]

View File

@ -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.

View File

@ -1,22 +1,60 @@
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 unused 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:
go install ./... go install -ldflags '-X "main.version=dev build $(dev_build_version)"' ./...
.PHONY: release
release:
@go install github.com/goreleaser/goreleaser@v1.21.0
goreleaser release --clean
.PHONY: docker
docker:
@echo $(dev_build_version) > VERSION
docker build -t fullstorydev/grpcurl:$(dev_build_version) .
@rm VERSION
.PHONY: generate
generate: .tmp/protoc/bin/protoc
@go install google.golang.org/protobuf/cmd/protoc-gen-go@a709e31e5d12
@go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1.0
@go install github.com/jhump/protoreflect/desc/sourceinfo/cmd/protoc-gen-gosrcinfo@v1.14.1
go generate ./...
go mod tidy
.PHONY: checkgenerate
checkgenerate: generate
git status --porcelain -- '**/*.go'
@if [ -n "$$(git status --porcelain -- '**/*.go')" ]; then \
git diff -- '**/*.go'; \
exit 1; \
fi
.PHONY: checkgofmt .PHONY: checkgofmt
checkgofmt: checkgofmt:
@ -31,36 +69,35 @@ vet:
.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: unused
unused:
@go get honnef.co/go/tools/cmd/unused
unused ./...
.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 github.com/golang/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: errchack .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

138
README.md
View File

@ -1,6 +1,7 @@
# gRPCurl # gRPCurl
[![Build Status](https://travis-ci.org/fullstorydev/grpcurl.svg?branch=master)](https://travis-ci.org/fullstorydev/grpcurl/branches) [![Build Status](https://circleci.com/gh/fullstorydev/grpcurl/tree/master.svg?style=svg)](https://circleci.com/gh/fullstorydev/grpcurl/tree/master)
[![Go Report Card](https://goreportcard.com/badge/github.com/fullstorydev/grpcurl)](https://goreportcard.com/report/github.com/fullstorydev/grpcurl) [![Go Report Card](https://goreportcard.com/badge/github.com/fullstorydev/grpcurl)](https://goreportcard.com/report/github.com/fullstorydev/grpcurl)
[![Snap Release Status](https://snapcraft.io/grpcurl/badge.svg)](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,52 +15,98 @@ 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 [service 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 encoded file by reading proto source files, or by loading in compiled "protoset" files (files that contain
[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
is using that very same schema. So, if the server you interact with does not support is using that very same schema. So, if the server you interact with does not support
reflection, you will either need the proto source files that define the service or need reflection, you will either need the proto source files that define the service or need
protoset files that `grpcurl` can use. protoset files that `grpcurl` can use.
[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)
This repo also provides a library package, `github.com/fullstorydev/grpcurl`, that has This repo also provides a library package, `github.com/fullstorydev/grpcurl`, that has
functions for simplifying the construction of other command-line tools that dynamically functions for simplifying the construction of other command-line tools that dynamically
invoke gRPC endpoints. This code is a great example of how to use the various packages of invoke gRPC endpoints. This code is a great example of how to use the various packages of
the [protoreflect](https://godoc.org/github.com/jhump/protoreflect) library, and shows the [protoreflect](https://godoc.org/github.com/jhump/protoreflect) library, and shows
off what they can do. off what they can do.
See also the [`grpcurl` talk at GopherCon 2018](https://www.youtube.com/watch?v=dDr-8kbMnaw).
## Features ## Features
`grpcurl` supports all kinds of RPC methods, including streaming methods. You can even `grpcurl` supports all kinds of RPC methods, including streaming methods. You can even
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
files (containing compiled descriptors, produced by `protoc`) to `grpcurl`. files (containing compiled descriptors, produced by `protoc`) to `grpcurl`.
## Installation ## Installation
You can use the `go` tool to install `grpcurl`:
### Binaries
Download the binary from the [releases](https://github.com/fullstorydev/grpcurl/releases) page.
### Homebrew (macOS)
On macOS, `grpcurl` is available via Homebrew:
```shell ```shell
go get github.com/fullstorydev/grpcurl brew install grpcurl
go install github.com/fullstorydev/grpcurl/cmd/grpcurl ```
### Docker
For platforms that support Docker, you can download an image that lets you run `grpcurl`:
```shell
# Download image
docker pull fullstorydev/grpcurl:latest
# Run the tool
docker run fullstorydev/grpcurl api.grpc.me:443 list
```
Note that there are some pitfalls when using docker:
- If you need to interact with a server listening on the host's loopback network, you must specify the host as `host.docker.internal` instead of `localhost` (for Mac or Windows) _OR_ have the container use the host network with `-network="host"` (Linux only).
- If you need to provide proto source files or descriptor sets, you must mount the folder containing the files as a volume (`-v $(pwd):/protos`) and adjust the import paths to container paths accordingly.
- If you want to provide the request message via stdin, using the `-d @` option, you need to use the `-i` flag on the docker command.
### Other Packages
There are numerous other ways to install `grpcurl`, thanks to support from third parties that
have created recipes/packages for it. These include other ways to install `grpcurl` on a variety
of environments, including Windows and myriad Linux distributions.
You can see more details and the full list of other packages for `grpcurl` at _repology.org_:
https://repology.org/project/grpcurl/information
### Snap
You can install `grpcurl` using the snap package:
`snap install grpcurl`
### From Source
If you already have the [Go SDK](https://golang.org/doc/install) installed, you can use the `go`
tool to install `grpcurl`:
```shell
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
``` ```
This installs the command into the `bin` sub-folder of wherever your `$GOPATH` 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 could have out-dated versions of `grpcurl`'s dependencies. You can update the
dependencies by running `make updatedeps`. Or, if you are using Go 1.11 or 1.12, you
can add `GO111MODULE=on` as a prefix to the commands above, which will also build using
the right versions of dependencies (vs. whatever you may already have in your `GOPATH`).
## Usage ## Usage
The usage doc for the tool explains the numerous options: The usage doc for the tool explains the numerous options:
@ -72,10 +119,13 @@ In the sections below, you will find numerous examples demonstrating how to use
### Invoking RPCs ### Invoking RPCs
Invoking an RPC on a trusted server (e.g. TLS without self-signed key or custom CA) Invoking an RPC on a trusted server (e.g. TLS without self-signed key or custom CA)
that requires no client certs and supports service reflection is the simplest thing to that requires no client certs and supports server reflection is the simplest thing to
do with `grpcurl`. This minimal invocation sends an empty request body: 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
@ -92,7 +142,7 @@ If you want to include `grpcurl` in a command pipeline, such as when using `jq`
create a request body, you can use `-d @`, which tells `grpcurl` to read the actual create a request body, you can use `-d @`, which tells `grpcurl` to read the actual
request body from stdin: request body from stdin:
```shell ```shell
grpcurl -d @ grpc.server.com:443 my.custom.server.Service/Method <<<EOM grpcurl -d @ grpc.server.com:443 my.custom.server.Service/Method <<EOM
{ {
"id": 1234, "id": 1234,
"tags": [ "tags": [
@ -102,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
@ -116,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:
@ -125,8 +189,10 @@ grpcurl localhost:8787 list my.custom.server.Service
### Describing Elements ### Describing Elements
The "describe" verb will print the type of any symbol that the server knows about The "describe" verb will print the type of any symbol that the server knows about
or that is found in a given protoset file and also print the full descriptor for the or that is found in a given protoset file. It also prints a description of that
symbol, in JSON. symbol, in the form of snippets of proto source. It won't necessarily be the
original source that defined the element, but it will be equivalent.
```shell ```shell
# Server supports reflection # Server supports reflection
grpcurl localhost:8787 describe my.custom.server.Service.MethodOne grpcurl localhost:8787 describe my.custom.server.Service.MethodOne
@ -138,7 +204,24 @@ grpcurl -protoset my-protos.bin describe my.custom.server.Service.MethodOne
grpcurl -import-path ../protos -proto my-stuff.proto describe my.custom.server.Service.MethodOne grpcurl -import-path ../protos -proto my-stuff.proto describe my.custom.server.Service.MethodOne
``` ```
## Proto Source Files ## Descriptor Sources
The `grpcurl` tool can operate on a variety of sources for descriptors. The descriptors
are required, in order for `grpcurl` to understand the RPC schema, translate inputs
into the protobuf binary format as well as translate responses from the binary format
into text. The sections below document the supported sources and what command-line flags
are needed to use them.
### Server Reflection
Without any additional command-line flags, `grpcurl` will try to use [server reflection](https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1/reflection.proto).
Examples for how to set up server reflection can be found [here](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md#known-implementations).
When using reflection, the server address (host:port or path to Unix socket) is required
even for "list" and "describe" operations, so that `grpcurl` can connect to the server
and ask it for its descriptors.
### Proto Source Files
To use `grpcurl` on servers that do not support reflection, you can use `.proto` source To use `grpcurl` on servers that do not support reflection, you can use `.proto` source
files. files.
@ -151,14 +234,18 @@ location of the standard protos included with `protoc` (which contain various "w
types" with a package definition of `google.protobuf`). These files are "known" by `grpcurl` types" with a package definition of `google.protobuf`). These files are "known" by `grpcurl`
as a snapshot of their descriptors is built into the `grpcurl` binary. as a snapshot of their descriptors is built into the `grpcurl` binary.
## Protoset Files When using proto sources, you can omit the server address (host:port or path to Unix socket)
when using the "list" and "describe" operations since they only need to consult the proto
source files.
### Protoset Files
You can also use compiled protoset files with `grpcurl`. If you are scripting `grpcurl` and You can also use compiled protoset files with `grpcurl`. If you are scripting `grpcurl` and
need to re-use the same proto sources for many invocations, you will see better performance need to re-use the same proto sources for many invocations, you will see better performance
by using protoset files (since it skips the parsing and compilation steps with each by using protoset files (since it skips the parsing and compilation steps with each
invocation). invocation).
Protoset files contain binary encoded `google.protobuf.FileDescriptorSet` protos. To create Protoset files contain binary encoded `google.protobuf.FileDescriptorSet` protos. To create
a protoset file, invoke `protoc` with the `*.proto` files that describe the service: a protoset file, invoke `protoc` with the `*.proto` files that define the service:
```shell ```shell
protoc --proto_path=. \ protoc --proto_path=. \
--descriptor_set_out=myservice.protoset \ --descriptor_set_out=myservice.protoset \
@ -169,3 +256,8 @@ protoc --proto_path=. \
The `--descriptor_set_out` argument is what tells `protoc` to produce a protoset, The `--descriptor_set_out` argument is what tells `protoc` to produce a protoset,
and the `--include_imports` argument is necessary for the protoset to contain and the `--include_imports` argument is necessary for the protoset to contain
everything that `grpcurl` needs to process and understand the schema. everything that `grpcurl` needs to process and understand the schema.
When using protosets, you can omit the server address (host:port or path to Unix socket)
when using the "list" and "describe" operations since they only need to consult the
protoset files.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
package main
import (
"bytes"
"flag"
"testing"
)
func TestFlagDocIndent(t *testing.T) {
// Tests the prettify() and indent() function. The indent() function
// differs by Go version, due to differences in "flags" package across
// versions. Run with multiple versions of Go to ensure that doc output
// is properly indented, regardless of Go version.
var fs flag.FlagSet
var buf bytes.Buffer
fs.SetOutput(&buf)
fs.String("foo", "", prettify(`
This is a flag doc string.
It has multiple lines.
More than two, actually.`))
fs.Int("bar", 100, prettify(`This is a simple flag doc string.`))
fs.Bool("baz", false, prettify(`
This is another long doc string.
It also has multiple lines. But not as long as the first one.`))
fs.PrintDefaults()
expected :=
` -bar int
This is a simple flag doc string. (default 100)
-baz
This is another long doc string.
It also has multiple lines. But not as long as the first one.
-foo string
This is a flag doc string.
It has multiple lines.
More than two, actually.
`
actual := buf.String()
if actual != expected {
t.Errorf("Flag output had wrong indentation.\nExpecting:\n%s\nGot:\n%s", expected, actual)
}
}

View File

@ -1,12 +1,11 @@
// +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
import "flag"
var ( var (
unix = flag.Bool("unix", false, unix = flags.Bool("unix", false, prettify(`
`Indicates that the server address is the path to a Unix domain socket.`) Indicates that the server address is the path to a Unix domain socket.`))
) )
func init() { func init() {

369
desc_source.go Normal file
View File

@ -0,0 +1,369 @@
package grpcurl
import (
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"sync"
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/desc/protoparse" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/desc/protoprint"
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/grpcreflect"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/descriptorpb"
)
// ErrReflectionNotSupported is returned by DescriptorSource operations that
// rely on interacting with the reflection service when the source does not
// actually expose the reflection service. When this occurs, an alternate source
// (like file descriptor sets) must be used.
var ErrReflectionNotSupported = errors.New("server does not support the reflection API")
// DescriptorSource is a source of protobuf descriptor information. It can be backed by a FileDescriptorSet
// proto (like a file generated by protoc) or a remote server that supports the reflection API.
type DescriptorSource interface {
// ListServices returns a list of fully-qualified service names. It will be all services in a set of
// descriptor files or the set of all services exposed by a gRPC server.
ListServices() ([]string, error)
// FindSymbol returns a descriptor for the given fully-qualified symbol name.
FindSymbol(fullyQualifiedName string) (desc.Descriptor, error)
// AllExtensionsForType returns all known extension fields that extend the given message type name.
AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error)
}
// DescriptorSourceFromProtoSets creates a DescriptorSource that is backed by the named files, whose contents
// are encoded FileDescriptorSet protos.
func DescriptorSourceFromProtoSets(fileNames ...string) (DescriptorSource, error) {
files := &descriptorpb.FileDescriptorSet{}
for _, fileName := range fileNames {
b, err := os.ReadFile(fileName)
if err != nil {
return nil, fmt.Errorf("could not load protoset file %q: %v", fileName, err)
}
var fs descriptorpb.FileDescriptorSet
err = proto.Unmarshal(b, &fs)
if err != nil {
return nil, fmt.Errorf("could not parse contents of protoset file %q: %v", fileName, err)
}
files.File = append(files.File, fs.File...)
}
return DescriptorSourceFromFileDescriptorSet(files)
}
// DescriptorSourceFromProtoFiles creates a DescriptorSource that is backed by the named files,
// whose contents are Protocol Buffer source files. The given importPaths are used to locate
// any imported files.
func DescriptorSourceFromProtoFiles(importPaths []string, fileNames ...string) (DescriptorSource, error) {
fileNames, err := protoparse.ResolveFilenames(importPaths, fileNames...)
if err != nil {
return nil, err
}
p := protoparse.Parser{
ImportPaths: importPaths,
InferImportPaths: len(importPaths) == 0,
IncludeSourceCodeInfo: true,
}
fds, err := p.ParseFiles(fileNames...)
if err != nil {
return nil, fmt.Errorf("could not parse given files: %v", err)
}
return DescriptorSourceFromFileDescriptors(fds...)
}
// DescriptorSourceFromFileDescriptorSet creates a DescriptorSource that is backed by the FileDescriptorSet.
func DescriptorSourceFromFileDescriptorSet(files *descriptorpb.FileDescriptorSet) (DescriptorSource, error) {
unresolved := map[string]*descriptorpb.FileDescriptorProto{}
for _, fd := range files.File {
unresolved[fd.GetName()] = fd
}
resolved := map[string]*desc.FileDescriptor{}
for _, fd := range files.File {
_, err := resolveFileDescriptor(unresolved, resolved, fd.GetName())
if err != nil {
return nil, err
}
}
return &fileSource{files: resolved}, nil
}
func resolveFileDescriptor(unresolved map[string]*descriptorpb.FileDescriptorProto, resolved map[string]*desc.FileDescriptor, filename string) (*desc.FileDescriptor, error) {
if r, ok := resolved[filename]; ok {
return r, nil
}
fd, ok := unresolved[filename]
if !ok {
return nil, fmt.Errorf("no descriptor found for %q", filename)
}
deps := make([]*desc.FileDescriptor, 0, len(fd.GetDependency()))
for _, dep := range fd.GetDependency() {
depFd, err := resolveFileDescriptor(unresolved, resolved, dep)
if err != nil {
return nil, err
}
deps = append(deps, depFd)
}
result, err := desc.CreateFileDescriptor(fd, deps...)
if err != nil {
return nil, err
}
resolved[filename] = result
return result, nil
}
// DescriptorSourceFromFileDescriptors creates a DescriptorSource that is backed by the given
// file descriptors
func DescriptorSourceFromFileDescriptors(files ...*desc.FileDescriptor) (DescriptorSource, error) {
fds := map[string]*desc.FileDescriptor{}
for _, fd := range files {
if err := addFile(fd, fds); err != nil {
return nil, err
}
}
return &fileSource{files: fds}, nil
}
func addFile(fd *desc.FileDescriptor, fds map[string]*desc.FileDescriptor) error {
name := fd.GetName()
if existing, ok := fds[name]; ok {
// already added this file
if existing != fd {
// doh! duplicate files provided
return fmt.Errorf("given files include multiple copies of %q", name)
}
return nil
}
fds[name] = fd
for _, dep := range fd.GetDependencies() {
if err := addFile(dep, fds); err != nil {
return err
}
}
return nil
}
type fileSource struct {
files map[string]*desc.FileDescriptor
er *dynamic.ExtensionRegistry
erInit sync.Once
}
func (fs *fileSource) ListServices() ([]string, error) {
set := map[string]bool{}
for _, fd := range fs.files {
for _, svc := range fd.GetServices() {
set[svc.GetFullyQualifiedName()] = true
}
}
sl := make([]string, 0, len(set))
for svc := range set {
sl = append(sl, svc)
}
return sl, nil
}
// GetAllFiles returns all of the underlying file descriptors. This is
// more thorough and more efficient than the fallback strategy used by
// the GetAllFiles package method, for enumerating all files from a
// descriptor source.
func (fs *fileSource) GetAllFiles() ([]*desc.FileDescriptor, error) {
files := make([]*desc.FileDescriptor, len(fs.files))
i := 0
for _, fd := range fs.files {
files[i] = fd
i++
}
return files, nil
}
func (fs *fileSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) {
for _, fd := range fs.files {
if dsc := fd.FindSymbol(fullyQualifiedName); dsc != nil {
return dsc, nil
}
}
return nil, notFound("Symbol", fullyQualifiedName)
}
func (fs *fileSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) {
fs.erInit.Do(func() {
fs.er = &dynamic.ExtensionRegistry{}
for _, fd := range fs.files {
fs.er.AddExtensionsFromFile(fd)
}
})
return fs.er.AllExtensionsForType(typeName), nil
}
// DescriptorSourceFromServer creates a DescriptorSource that uses the given gRPC reflection client
// to interrogate a server for descriptor information. If the server does not support the reflection
// API then the various DescriptorSource methods will return ErrReflectionNotSupported
func DescriptorSourceFromServer(_ context.Context, refClient *grpcreflect.Client) DescriptorSource {
return serverSource{client: refClient}
}
type serverSource struct {
client *grpcreflect.Client
}
func (ss serverSource) ListServices() ([]string, error) {
svcs, err := ss.client.ListServices()
return svcs, reflectionSupport(err)
}
func (ss serverSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) {
file, err := ss.client.FileContainingSymbol(fullyQualifiedName)
if err != nil {
return nil, reflectionSupport(err)
}
d := file.FindSymbol(fullyQualifiedName)
if d == nil {
return nil, notFound("Symbol", fullyQualifiedName)
}
return d, nil
}
func (ss serverSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) {
var exts []*desc.FieldDescriptor
nums, err := ss.client.AllExtensionNumbersForType(typeName)
if err != nil {
return nil, reflectionSupport(err)
}
for _, fieldNum := range nums {
ext, err := ss.client.ResolveExtension(typeName, fieldNum)
if err != nil {
return nil, reflectionSupport(err)
}
exts = append(exts, ext)
}
return exts, nil
}
func reflectionSupport(err error) error {
if err == nil {
return nil
}
if stat, ok := status.FromError(err); ok && stat.Code() == codes.Unimplemented {
return ErrReflectionNotSupported
}
return err
}
// WriteProtoset will use the given descriptor source to resolve all of the given
// symbols and write a proto file descriptor set with their definitions to the
// given output. The output will include descriptors for all files in which the
// symbols are defined as well as their transitive dependencies.
func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string) error {
filenames, fds, err := getFileDescriptors(symbols, descSource)
if err != nil {
return err
}
// now expand that to include transitive dependencies in topologically sorted
// order (such that file always appears after its dependencies)
expandedFiles := make(map[string]struct{}, len(fds))
allFilesSlice := make([]*descriptorpb.FileDescriptorProto, 0, len(fds))
for _, filename := range filenames {
allFilesSlice = addFilesToSet(allFilesSlice, expandedFiles, fds[filename])
}
// now we can serialize to file
b, err := proto.Marshal(&descriptorpb.FileDescriptorSet{File: allFilesSlice})
if err != nil {
return fmt.Errorf("failed to serialize file descriptor set: %v", err)
}
if _, err := out.Write(b); err != nil {
return fmt.Errorf("failed to write file descriptor set: %v", err)
}
return nil
}
func addFilesToSet(allFiles []*descriptorpb.FileDescriptorProto, expanded map[string]struct{}, fd *desc.FileDescriptor) []*descriptorpb.FileDescriptorProto {
if _, ok := expanded[fd.GetName()]; ok {
// already seen this one
return allFiles
}
expanded[fd.GetName()] = struct{}{}
// add all dependencies first
for _, dep := range fd.GetDependencies() {
allFiles = addFilesToSet(allFiles, expanded, dep)
}
return append(allFiles, fd.AsFileDescriptorProto())
}
// WriteProtoFiles will use the given descriptor source to resolve all the given
// symbols and write proto files with their definitions to the given output directory.
func WriteProtoFiles(outProtoDirPath string, descSource DescriptorSource, symbols ...string) error {
filenames, fds, err := getFileDescriptors(symbols, descSource)
if err != nil {
return err
}
// now expand that to include transitive dependencies in topologically sorted
// order (such that file always appears after its dependencies)
expandedFiles := make(map[string]struct{}, len(fds))
allFileDescriptors := make([]*desc.FileDescriptor, 0, len(fds))
for _, filename := range filenames {
allFileDescriptors = addFilesToFileDescriptorList(allFileDescriptors, expandedFiles, fds[filename])
}
pr := protoprint.Printer{}
// now we can serialize to files
for i := range allFileDescriptors {
if err := writeProtoFile(outProtoDirPath, allFileDescriptors[i], &pr); err != nil {
return err
}
}
return nil
}
func writeProtoFile(outProtoDirPath string, fd *desc.FileDescriptor, pr *protoprint.Printer) error {
outFile := filepath.Join(outProtoDirPath, fd.GetFullyQualifiedName())
outDir := filepath.Dir(outFile)
if err := os.MkdirAll(outDir, 0777); err != nil {
return fmt.Errorf("failed to create directory %q: %w", outDir, err)
}
f, err := os.Create(outFile)
if err != nil {
return fmt.Errorf("failed to create proto file %q: %w", outFile, err)
}
defer f.Close()
if err := pr.PrintProtoFile(fd, f); err != nil {
return fmt.Errorf("failed to write proto file %q: %w", outFile, err)
}
return nil
}
func getFileDescriptors(symbols []string, descSource DescriptorSource) ([]string, map[string]*desc.FileDescriptor, error) {
// compute set of file descriptors
filenames := make([]string, 0, len(symbols))
fds := make(map[string]*desc.FileDescriptor, len(symbols))
for _, sym := range symbols {
d, err := descSource.FindSymbol(sym)
if err != nil {
return nil, nil, fmt.Errorf("failed to find descriptor for %q: %v", sym, err)
}
fd := d.GetFile()
if _, ok := fds[fd.GetName()]; !ok {
fds[fd.GetName()] = fd
filenames = append(filenames, fd.GetName())
}
}
return filenames, fds, nil
}
func addFilesToFileDescriptorList(allFiles []*desc.FileDescriptor, expanded map[string]struct{}, fd *desc.FileDescriptor) []*desc.FileDescriptor {
if _, ok := expanded[fd.GetName()]; ok {
// already seen this one
return allFiles
}
expanded[fd.GetName()] = struct{}{}
// add all dependencies first
for _, dep := range fd.GetDependencies() {
allFiles = addFilesToFileDescriptorList(allFiles, expanded, dep)
}
return append(allFiles, fd)
}

62
desc_source_test.go Normal file
View File

@ -0,0 +1,62 @@
package grpcurl
import (
"bytes"
"os"
"testing"
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
"google.golang.org/protobuf/types/descriptorpb"
)
func TestWriteProtoset(t *testing.T) {
exampleProtoset, err := loadProtoset("./internal/testing/example.protoset")
if err != nil {
t.Fatalf("failed to load example.protoset: %v", err)
}
testProtoset, err := loadProtoset("./internal/testing/test.protoset")
if err != nil {
t.Fatalf("failed to load test.protoset: %v", err)
}
mergedProtoset := &descriptorpb.FileDescriptorSet{
File: append(exampleProtoset.File, testProtoset.File...),
}
descSrc, err := DescriptorSourceFromFileDescriptorSet(mergedProtoset)
if err != nil {
t.Fatalf("failed to create descriptor source: %v", err)
}
checkWriteProtoset(t, descSrc, exampleProtoset, "TestService")
checkWriteProtoset(t, descSrc, testProtoset, "testing.TestService")
checkWriteProtoset(t, descSrc, mergedProtoset, "TestService", "testing.TestService")
}
func loadProtoset(path string) (*descriptorpb.FileDescriptorSet, error) {
b, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var protoset descriptorpb.FileDescriptorSet
if err := proto.Unmarshal(b, &protoset); err != nil {
return nil, err
}
return &protoset, nil
}
func checkWriteProtoset(t *testing.T, descSrc DescriptorSource, protoset *descriptorpb.FileDescriptorSet, symbols ...string) {
var buf bytes.Buffer
if err := WriteProtoset(&buf, descSrc, symbols...); err != nil {
t.Fatalf("failed to write protoset: %v", err)
}
var result descriptorpb.FileDescriptorSet
if err := proto.Unmarshal(buf.Bytes(), &result); err != nil {
t.Fatalf("failed to unmarshal written protoset: %v", err)
}
if !proto.Equal(protoset, &result) {
t.Fatalf("written protoset not equal to input:\nExpecting: %s\nActual: %s", protoset, &result)
}
}

35
download_protoc.sh Executable file
View File

@ -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

554
format.go Normal file
View File

@ -0,0 +1,554 @@
package grpcurl
import (
"bufio"
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"reflect"
"strings"
"sync"
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// RequestParser processes input into messages.
type RequestParser interface {
// Next parses input data into the given request message. If called after
// input is exhausted, it returns io.EOF. If the caller re-uses the same
// instance in multiple calls to Next, it should call msg.Reset() in between
// each call.
Next(msg proto.Message) error
// NumRequests returns the number of messages that have been parsed and
// returned by a call to Next.
NumRequests() int
}
type jsonRequestParser struct {
dec *json.Decoder
unmarshaler jsonpb.Unmarshaler
requestCount int
}
// NewJSONRequestParser returns a RequestParser that reads data in JSON format
// from the given reader. The given resolver is used to assist with decoding of
// google.protobuf.Any messages.
//
// Input data that contains more than one message should just include all
// messages concatenated (though whitespace is necessary to separate some kinds
// of values in JSON).
//
// If the given reader has no data, the returned parser will return io.EOF on
// the very first call.
func NewJSONRequestParser(in io.Reader, resolver jsonpb.AnyResolver) RequestParser {
return &jsonRequestParser{
dec: json.NewDecoder(in),
unmarshaler: jsonpb.Unmarshaler{AnyResolver: resolver},
}
}
// NewJSONRequestParserWithUnmarshaler is like NewJSONRequestParser but
// accepts a protobuf jsonpb.Unmarshaler instead of jsonpb.AnyResolver.
func NewJSONRequestParserWithUnmarshaler(in io.Reader, unmarshaler jsonpb.Unmarshaler) RequestParser {
return &jsonRequestParser{
dec: json.NewDecoder(in),
unmarshaler: unmarshaler,
}
}
func (f *jsonRequestParser) Next(m proto.Message) error {
var msg json.RawMessage
if err := f.dec.Decode(&msg); err != nil {
return err
}
f.requestCount++
return f.unmarshaler.Unmarshal(bytes.NewReader(msg), m)
}
func (f *jsonRequestParser) NumRequests() int {
return f.requestCount
}
const (
textSeparatorChar = '\x1e'
)
type textRequestParser struct {
r *bufio.Reader
err error
requestCount int
}
// NewTextRequestParser returns a RequestParser that reads data in the protobuf
// text format from the given reader.
//
// Input data that contains more than one message should include an ASCII
// 'Record Separator' character (0x1E) between each message.
//
// Empty text is a valid text format and represents an empty message. So if the
// given reader has no data, the returned parser will yield an empty message
// for the first call to Next and then return io.EOF thereafter. This also means
// that if the input data ends with a record separator, then a final empty
// message will be parsed *after* the separator.
func NewTextRequestParser(in io.Reader) RequestParser {
return &textRequestParser{r: bufio.NewReader(in)}
}
func (f *textRequestParser) Next(m proto.Message) error {
if f.err != nil {
return f.err
}
var b []byte
b, f.err = f.r.ReadBytes(textSeparatorChar)
if f.err != nil && f.err != io.EOF {
return f.err
}
// remove delimiter
if len(b) > 0 && b[len(b)-1] == textSeparatorChar {
b = b[:len(b)-1]
}
f.requestCount++
return proto.UnmarshalText(string(b), m)
}
func (f *textRequestParser) NumRequests() int {
return f.requestCount
}
// Formatter translates messages into string representations.
type Formatter func(proto.Message) (string, error)
// NewJSONFormatter returns a formatter that returns JSON strings. The JSON will
// include empty/default values (instead of just omitted them) if emitDefaults
// is true. The given resolver is used to assist with encoding of
// google.protobuf.Any messages.
func NewJSONFormatter(emitDefaults bool, resolver jsonpb.AnyResolver) Formatter {
marshaler := jsonpb.Marshaler{
EmitDefaults: emitDefaults,
AnyResolver: resolver,
}
// Workaround for indentation issue in jsonpb with Any messages.
// Bug was originally fixed in https://github.com/golang/protobuf/pull/834
// but later re-introduced before the module was deprecated and frozen.
// If jsonpb is ever replaced with google.golang.org/protobuf/encoding/protojson
// this workaround will no longer be needed.
formatter := func(message proto.Message) (string, error) {
output, err := marshaler.MarshalToString(message)
if err != nil {
return "", err
}
var buf bytes.Buffer
if err := json.Indent(&buf, []byte(output), "", " "); err != nil {
return "", err
}
return buf.String(), nil
}
return formatter
}
// NewTextFormatter returns a formatter that returns strings in the protobuf
// text format. If includeSeparator is true then, when invoked to format
// multiple messages, all messages after the first one will be prefixed with the
// ASCII 'Record Separator' character (0x1E).
func NewTextFormatter(includeSeparator bool) Formatter {
tf := textFormatter{useSeparator: includeSeparator}
return tf.format
}
type textFormatter struct {
useSeparator bool
numFormatted int
}
var protoTextMarshaler = proto.TextMarshaler{ExpandAny: true}
func (tf *textFormatter) format(m proto.Message) (string, error) {
var buf bytes.Buffer
if tf.useSeparator && tf.numFormatted > 0 {
if err := buf.WriteByte(textSeparatorChar); err != nil {
return "", err
}
}
// If message implements MarshalText method (such as a *dynamic.Message),
// it won't get details about whether or not to format to text compactly
// or with indentation. So first see if the message also implements a
// MarshalTextIndent method and use that instead if available.
type indentMarshaler interface {
MarshalTextIndent() ([]byte, error)
}
if indenter, ok := m.(indentMarshaler); ok {
b, err := indenter.MarshalTextIndent()
if err != nil {
return "", err
}
if _, err := buf.Write(b); err != nil {
return "", err
}
} else if err := protoTextMarshaler.Marshal(&buf, m); err != nil {
return "", err
}
// no trailing newline needed
str := buf.String()
if len(str) > 0 && str[len(str)-1] == '\n' {
str = str[:len(str)-1]
}
tf.numFormatted++
return str, nil
}
// Format of request data. The allowed values are 'json' or 'text'.
type Format string
const (
// FormatJSON specifies input data in JSON format. Multiple request values
// may be concatenated (messages with a JSON representation other than
// object must be separated by whitespace, such as a newline)
FormatJSON = Format("json")
// FormatText specifies input data must be in the protobuf text format.
// Multiple request values must be separated by the "record separator"
// ASCII character: 0x1E. The stream should not end in a record separator.
// If it does, it will be interpreted as a final, blank message after the
// separator.
FormatText = Format("text")
)
// AnyResolverFromDescriptorSource returns an AnyResolver that will search for
// types using the given descriptor source.
func AnyResolverFromDescriptorSource(source DescriptorSource) jsonpb.AnyResolver {
return &anyResolver{source: source}
}
// AnyResolverFromDescriptorSourceWithFallback returns an AnyResolver that will
// search for types using the given descriptor source and then fallback to a
// special message if the type is not found. The fallback type will render to
// JSON with a "@type" property, just like an Any message, but also with a
// custom "@value" property that includes the binary encoded payload.
func AnyResolverFromDescriptorSourceWithFallback(source DescriptorSource) jsonpb.AnyResolver {
res := anyResolver{source: source}
return &anyResolverWithFallback{AnyResolver: &res}
}
type anyResolver struct {
source DescriptorSource
er dynamic.ExtensionRegistry
mu sync.RWMutex
mf *dynamic.MessageFactory
resolved map[string]func() proto.Message
}
func (r *anyResolver) Resolve(typeUrl string) (proto.Message, error) {
mname := typeUrl
if slash := strings.LastIndex(mname, "/"); slash >= 0 {
mname = mname[slash+1:]
}
r.mu.RLock()
factory := r.resolved[mname]
r.mu.RUnlock()
// already resolved?
if factory != nil {
return factory(), nil
}
r.mu.Lock()
defer r.mu.Unlock()
// double-check, in case we were racing with another goroutine
// that resolved this one
factory = r.resolved[mname]
if factory != nil {
return factory(), nil
}
// use descriptor source to resolve message type
d, err := r.source.FindSymbol(mname)
if err != nil {
return nil, err
}
md, ok := d.(*desc.MessageDescriptor)
if !ok {
return nil, fmt.Errorf("unknown message: %s", typeUrl)
}
// populate any extensions for this message, too (if there are any)
if exts, err := r.source.AllExtensionsForType(mname); err == nil {
if err := r.er.AddExtension(exts...); err != nil {
return nil, err
}
}
if r.mf == nil {
r.mf = dynamic.NewMessageFactoryWithExtensionRegistry(&r.er)
}
factory = func() proto.Message {
return r.mf.NewMessage(md)
}
if r.resolved == nil {
r.resolved = map[string]func() proto.Message{}
}
r.resolved[mname] = factory
return factory(), nil
}
// anyResolverWithFallback can provide a fallback value for unknown
// messages that will format itself to JSON using an "@value" field
// that has the base64-encoded data for the unknown message value.
type anyResolverWithFallback struct {
jsonpb.AnyResolver
}
func (r anyResolverWithFallback) Resolve(typeUrl string) (proto.Message, error) {
msg, err := r.AnyResolver.Resolve(typeUrl)
if err == nil {
return msg, err
}
// Try "default" resolution logic. This mirrors the default behavior
// of jsonpb, which checks to see if the given message name is registered
// in the proto package.
mname := typeUrl
if slash := strings.LastIndex(mname, "/"); slash >= 0 {
mname = mname[slash+1:]
}
//lint:ignore SA1019 new non-deprecated API requires other code changes; deferring...
mt := proto.MessageType(mname)
if mt != nil {
return reflect.New(mt.Elem()).Interface().(proto.Message), nil
}
// finally, fallback to a special placeholder that can marshal itself
// to JSON using a special "@value" property to show base64-encoded
// data for the embedded message
return &unknownAny{TypeUrl: typeUrl, Error: fmt.Sprintf("%s is not recognized; see @value for raw binary message data", mname)}, nil
}
type unknownAny struct {
TypeUrl string `json:"@type"`
Error string `json:"@error"`
Value string `json:"@value"`
}
func (a *unknownAny) MarshalJSONPB(jsm *jsonpb.Marshaler) ([]byte, error) {
if jsm.Indent != "" {
return json.MarshalIndent(a, "", jsm.Indent)
}
return json.Marshal(a)
}
func (a *unknownAny) Unmarshal(b []byte) error {
a.Value = base64.StdEncoding.EncodeToString(b)
return nil
}
func (a *unknownAny) Reset() {
a.Value = ""
}
func (a *unknownAny) String() string {
b, err := a.MarshalJSONPB(&jsonpb.Marshaler{})
if err != nil {
return fmt.Sprintf("ERROR: %v", err.Error())
}
return string(b)
}
func (a *unknownAny) ProtoMessage() {
}
var _ proto.Message = (*unknownAny)(nil)
// FormatOptions is a set of flags that are passed to a JSON or text formatter.
type FormatOptions struct {
// EmitJSONDefaultFields flag, when true, includes empty/default values in the output.
// FormatJSON only flag.
EmitJSONDefaultFields bool
// AllowUnknownFields is an option for the parser. When true,
// it accepts input which includes unknown fields. These unknown fields
// are skipped instead of returning an error.
// FormatJSON only flag.
AllowUnknownFields bool
// IncludeTextSeparator is true then, when invoked to format multiple messages,
// all messages after the first one will be prefixed with the
// ASCII 'Record Separator' character (0x1E).
// It might be useful when the output is piped to another grpcurl process.
// FormatText only flag.
IncludeTextSeparator bool
}
// RequestParserAndFormatter returns a request parser and formatter for the
// given format. The given descriptor source may be used for parsing message
// data (if needed by the format).
// It accepts a set of options. The field EmitJSONDefaultFields and IncludeTextSeparator
// are options for JSON and protobuf text formats, respectively. The AllowUnknownFields field
// is a JSON-only format flag.
// Requests will be parsed from the given in.
func RequestParserAndFormatter(format Format, descSource DescriptorSource, in io.Reader, opts FormatOptions) (RequestParser, Formatter, error) {
switch format {
case FormatJSON:
resolver := AnyResolverFromDescriptorSource(descSource)
unmarshaler := jsonpb.Unmarshaler{AnyResolver: resolver, AllowUnknownFields: opts.AllowUnknownFields}
return NewJSONRequestParserWithUnmarshaler(in, unmarshaler), NewJSONFormatter(opts.EmitJSONDefaultFields, anyResolverWithFallback{AnyResolver: resolver}), nil
case FormatText:
return NewTextRequestParser(in), NewTextFormatter(opts.IncludeTextSeparator), nil
default:
return nil, nil, fmt.Errorf("unknown format: %s", format)
}
}
// RequestParserAndFormatterFor returns a request parser and formatter for the
// given format. The given descriptor source may be used for parsing message
// data (if needed by the format). The flags emitJSONDefaultFields and
// includeTextSeparator are options for JSON and protobuf text formats,
// respectively. Requests will be parsed from the given in.
// This function is deprecated. Please use RequestParserAndFormatter instead.
// DEPRECATED
func RequestParserAndFormatterFor(format Format, descSource DescriptorSource, emitJSONDefaultFields, includeTextSeparator bool, in io.Reader) (RequestParser, Formatter, error) {
return RequestParserAndFormatter(format, descSource, in, FormatOptions{
EmitJSONDefaultFields: emitJSONDefaultFields,
IncludeTextSeparator: includeTextSeparator,
})
}
// DefaultEventHandler logs events to a writer. This is not thread-safe, but is
// safe for use with InvokeRPC as long as NumResponses and Status are not read
// until the call to InvokeRPC completes.
type DefaultEventHandler struct {
Out io.Writer
Formatter Formatter
// 0 = default
// 1 = verbose
// 2 = very verbose
VerbosityLevel int
// NumResponses is the number of responses that have been received.
NumResponses int
// Status is the status that was received at the end of an RPC. It is
// nil if the RPC is still in progress.
Status *status.Status
}
// NewDefaultEventHandler returns an InvocationEventHandler that logs events to
// the given output. If verbose is true, all events are logged. Otherwise, only
// response messages are logged.
//
// Deprecated: NewDefaultEventHandler exists for compatibility.
// It doesn't allow fine control over the `VerbosityLevel`
// and provides only 0 and 1 options (which corresponds to the `verbose` argument).
// Use DefaultEventHandler{} initializer directly.
func NewDefaultEventHandler(out io.Writer, descSource DescriptorSource, formatter Formatter, verbose bool) *DefaultEventHandler {
verbosityLevel := 0
if verbose {
verbosityLevel = 1
}
return &DefaultEventHandler{
Out: out,
Formatter: formatter,
VerbosityLevel: verbosityLevel,
}
}
var _ InvocationEventHandler = (*DefaultEventHandler)(nil)
func (h *DefaultEventHandler) OnResolveMethod(md *desc.MethodDescriptor) {
if h.VerbosityLevel > 0 {
txt, err := GetDescriptorText(md, nil)
if err == nil {
fmt.Fprintf(h.Out, "\nResolved method descriptor:\n%s\n", txt)
}
}
}
func (h *DefaultEventHandler) OnSendHeaders(md metadata.MD) {
if h.VerbosityLevel > 0 {
fmt.Fprintf(h.Out, "\nRequest metadata to send:\n%s\n", MetadataToString(md))
}
}
func (h *DefaultEventHandler) OnReceiveHeaders(md metadata.MD) {
if h.VerbosityLevel > 0 {
fmt.Fprintf(h.Out, "\nResponse headers received:\n%s\n", MetadataToString(md))
}
}
func (h *DefaultEventHandler) OnReceiveResponse(resp proto.Message) {
h.NumResponses++
if h.VerbosityLevel > 1 {
fmt.Fprintf(h.Out, "\nEstimated response size: %d bytes\n", proto.Size(resp))
}
if h.VerbosityLevel > 0 {
fmt.Fprint(h.Out, "\nResponse contents:\n")
}
if respStr, err := h.Formatter(resp); err != nil {
fmt.Fprintf(h.Out, "Failed to format response message %d: %v\n", h.NumResponses, err)
} else {
fmt.Fprintln(h.Out, respStr)
}
}
func (h *DefaultEventHandler) OnReceiveTrailers(stat *status.Status, md metadata.MD) {
h.Status = stat
if h.VerbosityLevel > 0 {
fmt.Fprintf(h.Out, "\nResponse trailers received:\n%s\n", MetadataToString(md))
}
}
// PrintStatus prints details about the given status to the given writer. The given
// formatter is used to print any detail messages that may be included in the status.
// If the given status has a code of OK, "OK" is printed and that is all. Otherwise,
// "ERROR:" is printed along with a line showing the code, one showing the message
// string, and each detail message if any are present. The detail messages will be
// printed as proto text format or JSON, depending on the given formatter.
func PrintStatus(w io.Writer, stat *status.Status, formatter Formatter) {
if stat.Code() == codes.OK {
fmt.Fprintln(w, "OK")
return
}
fmt.Fprintf(w, "ERROR:\n Code: %s\n Message: %s\n", stat.Code().String(), stat.Message())
statpb := stat.Proto()
if len(statpb.Details) > 0 {
fmt.Fprintf(w, " Details:\n")
for i, det := range statpb.Details {
prefix := fmt.Sprintf(" %d)", i+1)
fmt.Fprintf(w, "%s\t", prefix)
prefix = strings.Repeat(" ", len(prefix)) + "\t"
output, err := formatter(det)
if err != nil {
fmt.Fprintf(w, "Error parsing detail message: %v\n", err)
} else {
lines := strings.Split(output, "\n")
for i, line := range lines {
if i == 0 {
// first line is already indented
fmt.Fprintf(w, "%s\n", line)
} else {
fmt.Fprintf(w, "%s%s\n", prefix, line)
}
}
}
}
}
}

309
format_test.go Normal file
View File

@ -0,0 +1,309 @@
package grpcurl
import (
"bytes"
"fmt"
"io"
"strings"
"testing"
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/types/known/structpb"
)
func TestRequestParser(t *testing.T) {
source, err := DescriptorSourceFromProtoSets("internal/testing/example.protoset")
if err != nil {
t.Fatalf("failed to create descriptor source: %v", err)
}
msg, err := makeProto()
if err != nil {
t.Fatalf("failed to create message: %v", err)
}
testCases := []struct {
format Format
input string
expectedOutput []proto.Message
}{
{
format: FormatJSON,
input: "",
},
{
format: FormatJSON,
input: messageAsJSON,
expectedOutput: []proto.Message{msg},
},
{
format: FormatJSON,
input: messageAsJSON + messageAsJSON + messageAsJSON,
expectedOutput: []proto.Message{msg, msg, msg},
},
{
// unlike JSON, empty input yields one empty message (vs. zero messages)
format: FormatText,
input: "",
expectedOutput: []proto.Message{&structpb.Value{}},
},
{
format: FormatText,
input: messageAsText,
expectedOutput: []proto.Message{msg},
},
{
format: FormatText,
input: messageAsText + string(textSeparatorChar),
expectedOutput: []proto.Message{msg, &structpb.Value{}},
},
{
format: FormatText,
input: messageAsText + string(textSeparatorChar) + messageAsText + string(textSeparatorChar) + messageAsText,
expectedOutput: []proto.Message{msg, msg, msg},
},
}
for i, tc := range testCases {
name := fmt.Sprintf("#%d, %s, %d message(s)", i+1, tc.format, len(tc.expectedOutput))
rf, _, err := RequestParserAndFormatter(tc.format, source, strings.NewReader(tc.input), FormatOptions{})
if err != nil {
t.Errorf("Failed to create parser and formatter: %v", err)
continue
}
numReqs := 0
for {
var req structpb.Value
err := rf.Next(&req)
if err == io.EOF {
break
} else if err != nil {
t.Errorf("%s, msg %d: unexpected error: %v", name, numReqs, err)
}
if !proto.Equal(&req, tc.expectedOutput[numReqs]) {
t.Errorf("%s, msg %d: incorrect message;\nexpecting:\n%v\ngot:\n%v", name, numReqs, tc.expectedOutput[numReqs], &req)
}
numReqs++
}
if rf.NumRequests() != numReqs {
t.Errorf("%s: factory reported wrong number of requests: expecting %d, got %d", name, numReqs, rf.NumRequests())
}
}
}
// Handler prints response data (and headers/trailers in verbose mode).
// This verifies that we get the right output in both JSON and proto text modes.
func TestHandler(t *testing.T) {
source, err := DescriptorSourceFromProtoSets("internal/testing/example.protoset")
if err != nil {
t.Fatalf("failed to create descriptor source: %v", err)
}
d, err := source.FindSymbol("TestService.GetFiles")
if err != nil {
t.Fatalf("failed to find method 'TestService.GetFiles': %v", err)
}
md, ok := d.(*desc.MethodDescriptor)
if !ok {
t.Fatalf("wrong kind of descriptor found: %T", d)
}
reqHeaders := metadata.Pairs("foo", "123", "bar", "456")
respHeaders := metadata.Pairs("foo", "abc", "bar", "def", "baz", "xyz")
respTrailers := metadata.Pairs("a", "1", "b", "2", "c", "3")
rsp, err := makeProto()
if err != nil {
t.Fatalf("failed to create response message: %v", err)
}
for _, format := range []Format{FormatJSON, FormatText} {
for _, numMessages := range []int{1, 3} {
for verbosityLevel := 0; verbosityLevel <= 2; verbosityLevel++ {
name := fmt.Sprintf("%s, %d message(s)", format, numMessages)
if verbosityLevel > 0 {
name += fmt.Sprintf(", verbosityLevel=%d", verbosityLevel)
}
verbose := verbosityLevel > 0
_, formatter, err := RequestParserAndFormatter(format, source, nil, FormatOptions{IncludeTextSeparator: !verbose})
if err != nil {
t.Errorf("Failed to create parser and formatter: %v", err)
continue
}
var buf bytes.Buffer
h := &DefaultEventHandler{
Out: &buf,
Formatter: formatter,
VerbosityLevel: verbosityLevel,
}
h.OnResolveMethod(md)
h.OnSendHeaders(reqHeaders)
h.OnReceiveHeaders(respHeaders)
for i := 0; i < numMessages; i++ {
h.OnReceiveResponse(rsp)
}
h.OnReceiveTrailers(nil, respTrailers)
expectedOutput := ""
if verbose {
expectedOutput += verbosePrefix
}
for i := 0; i < numMessages; i++ {
if verbosityLevel > 1 {
expectedOutput += verboseResponseSize
}
if verbose {
expectedOutput += verboseResponseHeader
}
if format == "json" {
expectedOutput += messageAsJSON
} else {
if i > 0 && !verbose {
expectedOutput += string(textSeparatorChar)
}
expectedOutput += messageAsText
}
}
if verbose {
expectedOutput += verboseSuffix
}
out := buf.String()
if !compare(out, expectedOutput) {
t.Errorf("%s: Incorrect output. Expected:\n%s\nGot:\n%s", name, expectedOutput, out)
}
}
}
}
}
// compare checks that actual and expected are equal, returning true if so.
// A simple equality check (==) does not suffice because jsonpb formats
// structpb.Value strangely. So if that formatting gets fixed, we don't
// want this test in grpcurl to suddenly start failing. So we check each
// line and compare the lines after stripping whitespace (which removes
// the jsonpb format anomalies).
func compare(actual, expected string) bool {
actualLines := strings.Split(actual, "\n")
expectedLines := strings.Split(expected, "\n")
if len(actualLines) != len(expectedLines) {
return false
}
for i := 0; i < len(actualLines); i++ {
if strings.TrimSpace(actualLines[i]) != strings.TrimSpace(expectedLines[i]) {
return false
}
}
return true
}
func makeProto() (proto.Message, error) {
var rsp structpb.Value
err := jsonpb.UnmarshalString(`{
"foo": ["abc", "def", "ghi"],
"bar": { "a": 1, "b": 2 },
"baz": true,
"null": null
}`, &rsp)
if err != nil {
return nil, err
}
return &rsp, nil
}
var (
verbosePrefix = `
Resolved method descriptor:
rpc GetFiles ( .TestRequest ) returns ( .TestResponse );
Request metadata to send:
bar: 456
foo: 123
Response headers received:
bar: def
baz: xyz
foo: abc
`
verboseSuffix = `
Response trailers received:
a: 1
b: 2
c: 3
`
verboseResponseSize = `
Estimated response size: 100 bytes
`
verboseResponseHeader = `
Response contents:
`
messageAsJSON = `{
"bar": {
"a": 1,
"b": 2
},
"baz": true,
"foo": [
"abc",
"def",
"ghi"
],
"null": null
}
`
messageAsText = `struct_value: <
fields: <
key: "bar"
value: <
struct_value: <
fields: <
key: "a"
value: <
number_value: 1
>
>
fields: <
key: "b"
value: <
number_value: 2
>
>
>
>
>
fields: <
key: "baz"
value: <
bool_value: true
>
>
fields: <
key: "foo"
value: <
list_value: <
values: <
string_value: "abc"
>
values: <
string_value: "def"
>
values: <
string_value: "ghi"
>
>
>
>
fields: <
key: "null"
value: <
null_value: NULL_VALUE
>
>
>
`
)

32
go.mod
View File

@ -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.1.0 github.com/golang/protobuf v1.5.4
github.com/jhump/protoreflect v1.0.0 github.com/jhump/protoreflect v1.18.0
golang.org/x/net v0.0.0-20180530234432-1e491301e022 google.golang.org/grpc v1.66.2
google.golang.org/grpc v1.12.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
) )

69
go.sum
View File

@ -1,13 +1,56 @@
github.com/golang/protobuf v1.1.0 h1:0iH4Ffd/meGoXqF2lSAhZHt8X+cPgkfn/cb6Cce5Vpc= cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg=
github.com/jhump/protoreflect v1.0.0 h1:l94KtQ6gRI3ouKVcXNdofCQJWoHATzcI6tDizOgUaf0= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
github.com/jhump/protoreflect v1.0.0/go.mod h1:kG/zRVeS2M91gYaCvvUbPkMjjtFQS4qqjcPFzFkh2zE= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
golang.org/x/net v0.0.0-20180530234432-1e491301e022 h1:MVYFTUmVD3/+ERcvRRI+P/C2+WOUimXh+Pd8LVsklZ4= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
google.golang.org/genproto v0.0.0-20170818100345-ee236bd376b0 h1:jgaHBfsPDMBDKsth1hPtI1HcOyecWndWOFSGW21VgaM= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
google.golang.org/genproto v0.0.0-20170818100345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw=
google.golang.org/grpc v1.12.0 h1:Mm8atZtkT+P6R43n/dqNDWkPPu5BwRVu/1rJnJCeZH8= github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc=
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA=
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jhump/protoreflect v1.18.0 h1:TOz0MSR/0JOZ5kECB/0ufGnC2jdsgZ123Rd/k4Z5/2w=
github.com/jhump/protoreflect v1.18.0/go.mod h1:ezWcltJIVF4zYdIFM+D/sHV4Oh5LNU08ORzCGfwvTz8=
github.com/jhump/protoreflect/v2 v2.0.0-beta.1 h1:Dw1rslK/VotaUGYsv53XVWITr+5RCPXfvvlGrM/+B6w=
github.com/jhump/protoreflect/v2 v2.0.0-beta.1/go.mod h1:D9LBEowZyv8/iSu97FU2zmXG3JxVTmNw21mu63niFzU=
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14=
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU=
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
package grpcurl_test package grpcurl_test
import ( import (
"context"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net" "net"
@ -10,21 +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
"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/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 (
@ -44,20 +45,24 @@ type descSourceCase struct {
includeRefl bool includeRefl bool
} }
// NB: These tests intentionally use the deprecated InvokeRpc since that
// calls the other (non-deprecated InvokeRPC). That allows the tests to
// easily exercise both functions.
func TestMain(m *testing.M) { 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 {
@ -72,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)
@ -97,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()
@ -112,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)
@ -135,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)
} }
@ -172,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)
@ -192,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{
"EmptyCall", "testing.TestService.EmptyCall",
"FullDuplexCall", "testing.TestService.FullDuplexCall",
"HalfDuplexCall", "testing.TestService.HalfDuplexCall",
"StreamingInputCall", "testing.TestService.StreamingInputCall",
"StreamingOutputCall", "testing.TestService.StreamingOutputCall",
"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)
@ -210,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{"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{"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)
@ -235,6 +240,149 @@ func doTestListMethods(t *testing.T, source DescriptorSource, includeReflection
} }
} }
func TestGetAllFiles(t *testing.T) {
expectedFiles := []string{"test.proto"}
expectedFilesWithReflection := []string{
"grpc/reflection/v1/reflection.proto", "grpc/reflection/v1alpha/reflection.proto", "test.proto",
}
for _, ds := range descSources {
t.Run(ds.name, func(t *testing.T) {
files, err := GetAllFiles(ds.source)
if err != nil {
t.Fatalf("failed to get all files: %v", err)
}
names := fileNames(files)
match := false
var expected []string
if ds.includeRefl {
expected = expectedFilesWithReflection
} else {
expected = expectedFiles
}
match = reflect.DeepEqual(expected, names)
if !match {
t.Errorf("GetAllFiles returned wrong results: wanted %v, got %v", expected, names)
}
})
}
// try cases with more complicated set of files
otherSourceProtoset, err := DescriptorSourceFromProtoSets("internal/testing/test.protoset", "internal/testing/example.protoset")
if err != nil {
t.Fatal(err.Error())
}
otherSourceProtoFiles, err := DescriptorSourceFromProtoFiles([]string{"internal/testing"}, "test.proto", "example.proto")
if err != nil {
t.Fatal(err.Error())
}
otherDescSources := []descSourceCase{
{"protoset[b]", otherSourceProtoset, false},
{"proto[b]", otherSourceProtoFiles, false},
}
expectedFiles = []string{
"example.proto",
"example2.proto",
"google/protobuf/any.proto",
"google/protobuf/descriptor.proto",
"google/protobuf/empty.proto",
"google/protobuf/timestamp.proto",
"test.proto",
}
for _, ds := range otherDescSources {
t.Run(ds.name, func(t *testing.T) {
files, err := GetAllFiles(ds.source)
if err != nil {
t.Fatalf("failed to get all files: %v", err)
}
names := fileNames(files)
if !reflect.DeepEqual(expectedFiles, names) {
t.Errorf("GetAllFiles returned wrong results: wanted %v, got %v", expectedFiles, names)
}
})
}
}
func TestExpandHeaders(t *testing.T) {
inHeaders := []string{"key1: ${value}", "key2: bar", "key3: ${woo", "key4: woo}", "key5: ${TEST}",
"key6: ${TEST_VAR}", "${TEST}: ${TEST_VAR}", "key8: ${EMPTY}"}
os.Setenv("value", "value")
os.Setenv("TEST", "value5")
os.Setenv("TEST_VAR", "value6")
os.Setenv("EMPTY", "")
expectedHeaders := map[string]bool{"key1: value": true, "key2: bar": true, "key3: ${woo": true, "key4: woo}": true,
"key5: value5": true, "key6: value6": true, "value5: value6": true, "key8: ": true}
outHeaders, err := ExpandHeaders(inHeaders)
if err != nil {
t.Errorf("The ExpandHeaders function generated an unexpected error %s", err)
}
for _, expandedHeader := range outHeaders {
if _, ok := expectedHeaders[expandedHeader]; !ok {
t.Errorf("The ExpandHeaders function has returned an unexpected header. Received unexpected header %s", expandedHeader)
}
}
badHeaders := []string{"key: ${DNE}"}
_, err = ExpandHeaders(badHeaders)
if err == nil {
t.Errorf("The ExpandHeaders function should return an error for missing environment variables %q", badHeaders)
}
}
func fileNames(files []*desc.FileDescriptor) []string {
names := make([]string, len(files))
for i, f := range files {
names[i] = f.GetName()
}
return names
}
const expectKnownType = `{
"dur": "0s",
"ts": "1970-01-01T00:00:00Z",
"dbl": 0,
"flt": 0,
"i64": "0",
"u64": "0",
"i32": 0,
"u32": 0,
"bool": false,
"str": "",
"bytes": null,
"st": {"google.protobuf.Struct": "supports arbitrary JSON objects"},
"an": {"@type": "type.googleapis.com/google.protobuf.Empty", "value": {}},
"lv": [{"google.protobuf.ListValue": "is an array of arbitrary JSON values"}],
"val": {"google.protobuf.Value": "supports arbitrary JSON"}
}`
func TestMakeTemplateKnownTypes(t *testing.T) {
descriptor, err := desc.LoadMessageDescriptorForMessage((*jsonpbtest.KnownTypes)(nil))
if err != nil {
t.Fatalf("failed to load descriptor: %v", err)
}
message := MakeTemplate(descriptor)
jsm := jsonpb.Marshaler{EmitDefaults: true}
out, err := jsm.MarshalToString(message)
if err != nil {
t.Fatalf("failed to marshal to JSON: %v", err)
}
// make sure template JSON matches expected
var actual, expected interface{}
if err := json.Unmarshal([]byte(out), &actual); err != nil {
t.Fatalf("failed to parse actual JSON: %v", err)
}
if err := json.Unmarshal([]byte(expectKnownType), &expected); err != nil {
t.Fatalf("failed to parse expected JSON: %v", err)
}
if !reflect.DeepEqual(actual, expected) {
t.Errorf("template message is not as expected; want:\n%s\ngot:\n%s", expectKnownType, out)
}
}
func TestDescribe(t *testing.T) { func TestDescribe(t *testing.T) {
for _, ds := range descSources { for _, ds := range descSources {
t.Run(ds.name, func(t *testing.T) { t.Run(ds.name, func(t *testing.T) {
@ -244,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)
@ -255,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)
@ -278,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"
> >
` `
@ -334,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])
} }
@ -347,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) {
@ -366,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
@ -383,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) {
@ -409,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},
}, },
} }
@ -422,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))
@ -445,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) {
@ -475,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)
@ -490,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) {
@ -517,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)
@ -531,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
@ -547,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))
@ -562,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 {

View File

@ -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"
) )

View File

@ -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

View File

@ -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";

View File

@ -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",
}

View File

@ -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,

View File

@ -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.
@ -38,11 +37,12 @@ func (a *accounts) openAccount(customer string, accountType Account_Type, initia
a.AccountNumbersByCustomer[customer] = accountNums a.AccountNumbersByCustomer[customer] = accountNums
var acct account var acct account
acct.AccountNumber = num acct.AccountNumber = num
acct.Type = accountType
acct.BalanceCents = initialBalanceCents acct.BalanceCents = initialBalanceCents
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",
}) })
@ -129,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,
@ -138,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 {

View File

@ -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

View File

@ -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";

View File

@ -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",
}

View File

@ -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)
} }
@ -120,7 +119,7 @@ func unaryLogger(ctx context.Context, req interface{}, info *grpc.UnaryServerInf
} else { } else {
code = codes.Unknown code = codes.Unknown
} }
grpclog.Infof("completed <%d>: %v (%d) %v\n", i, code, code, time.Now().Sub(start)) grpclog.Infof("completed <%d>: %v (%d) %v\n", i, code, code, time.Since(start))
return rsp, err return rsp, err
} }
@ -135,7 +134,7 @@ func streamLogger(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServer
} else { } else {
code = codes.Unknown code = codes.Unknown
} }
grpclog.Infof("completed <%d>: %v(%d) %v\n", i, code, code, time.Now().Sub(start)) grpclog.Infof("completed <%d>: %v(%d) %v\n", i, code, code, time.Since(start))
return err return err
} }

View File

@ -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

View File

@ -3,9 +3,11 @@ 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 "example2.proto";
message TestRequest { message TestRequest {
repeated string file_names = 1; repeated string file_names = 1;
repeated Extension extensions = 2;
} }
message TestResponse { message TestResponse {

View File

@ -0,0 +1,8 @@
syntax = "proto3";
import "google/protobuf/any.proto";
message Extension {
uint64 id = 1;
google.protobuf.Any data = 2;
}

View File

@ -0,0 +1,344 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0-devel
// protoc v3.14.0
// source: test_objects.proto
package jsonpb
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
anypb "google.golang.org/protobuf/types/known/anypb"
durationpb "google.golang.org/protobuf/types/known/durationpb"
structpb "google.golang.org/protobuf/types/known/structpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type KnownTypes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
An *anypb.Any `protobuf:"bytes,14,opt,name=an" json:"an,omitempty"`
Dur *durationpb.Duration `protobuf:"bytes,1,opt,name=dur" json:"dur,omitempty"`
St *structpb.Struct `protobuf:"bytes,12,opt,name=st" json:"st,omitempty"`
Ts *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=ts" json:"ts,omitempty"`
Lv *structpb.ListValue `protobuf:"bytes,15,opt,name=lv" json:"lv,omitempty"`
Val *structpb.Value `protobuf:"bytes,16,opt,name=val" json:"val,omitempty"`
Dbl *wrapperspb.DoubleValue `protobuf:"bytes,3,opt,name=dbl" json:"dbl,omitempty"`
Flt *wrapperspb.FloatValue `protobuf:"bytes,4,opt,name=flt" json:"flt,omitempty"`
I64 *wrapperspb.Int64Value `protobuf:"bytes,5,opt,name=i64" json:"i64,omitempty"`
U64 *wrapperspb.UInt64Value `protobuf:"bytes,6,opt,name=u64" json:"u64,omitempty"`
I32 *wrapperspb.Int32Value `protobuf:"bytes,7,opt,name=i32" json:"i32,omitempty"`
U32 *wrapperspb.UInt32Value `protobuf:"bytes,8,opt,name=u32" json:"u32,omitempty"`
Bool *wrapperspb.BoolValue `protobuf:"bytes,9,opt,name=bool" json:"bool,omitempty"`
Str *wrapperspb.StringValue `protobuf:"bytes,10,opt,name=str" json:"str,omitempty"`
Bytes *wrapperspb.BytesValue `protobuf:"bytes,11,opt,name=bytes" json:"bytes,omitempty"`
}
func (x *KnownTypes) Reset() {
*x = KnownTypes{}
if protoimpl.UnsafeEnabled {
mi := &file_test_objects_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *KnownTypes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*KnownTypes) ProtoMessage() {}
func (x *KnownTypes) ProtoReflect() protoreflect.Message {
mi := &file_test_objects_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use KnownTypes.ProtoReflect.Descriptor instead.
func (*KnownTypes) Descriptor() ([]byte, []int) {
return file_test_objects_proto_rawDescGZIP(), []int{0}
}
func (x *KnownTypes) GetAn() *anypb.Any {
if x != nil {
return x.An
}
return nil
}
func (x *KnownTypes) GetDur() *durationpb.Duration {
if x != nil {
return x.Dur
}
return nil
}
func (x *KnownTypes) GetSt() *structpb.Struct {
if x != nil {
return x.St
}
return nil
}
func (x *KnownTypes) GetTs() *timestamppb.Timestamp {
if x != nil {
return x.Ts
}
return nil
}
func (x *KnownTypes) GetLv() *structpb.ListValue {
if x != nil {
return x.Lv
}
return nil
}
func (x *KnownTypes) GetVal() *structpb.Value {
if x != nil {
return x.Val
}
return nil
}
func (x *KnownTypes) GetDbl() *wrapperspb.DoubleValue {
if x != nil {
return x.Dbl
}
return nil
}
func (x *KnownTypes) GetFlt() *wrapperspb.FloatValue {
if x != nil {
return x.Flt
}
return nil
}
func (x *KnownTypes) GetI64() *wrapperspb.Int64Value {
if x != nil {
return x.I64
}
return nil
}
func (x *KnownTypes) GetU64() *wrapperspb.UInt64Value {
if x != nil {
return x.U64
}
return nil
}
func (x *KnownTypes) GetI32() *wrapperspb.Int32Value {
if x != nil {
return x.I32
}
return nil
}
func (x *KnownTypes) GetU32() *wrapperspb.UInt32Value {
if x != nil {
return x.U32
}
return nil
}
func (x *KnownTypes) GetBool() *wrapperspb.BoolValue {
if x != nil {
return x.Bool
}
return nil
}
func (x *KnownTypes) GetStr() *wrapperspb.StringValue {
if x != nil {
return x.Str
}
return nil
}
func (x *KnownTypes) GetBytes() *wrapperspb.BytesValue {
if x != nil {
return x.Bytes
}
return nil
}
var File_test_objects_proto protoreflect.FileDescriptor
var file_test_objects_proto_rawDesc = []byte{
0x0a, 0x12, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x62, 0x1a, 0x19, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e,
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xba, 0x05, 0x0a, 0x0a, 0x4b, 0x6e, 0x6f, 0x77, 0x6e,
0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x02, 0x61, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x02, 0x61, 0x6e, 0x12, 0x2b, 0x0a, 0x03, 0x64,
0x75, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x03, 0x64, 0x75, 0x72, 0x12, 0x27, 0x0a, 0x02, 0x73, 0x74, 0x18, 0x0c,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x02, 0x73,
0x74, 0x12, 0x2a, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x02, 0x74, 0x73, 0x12, 0x2a, 0x0a,
0x02, 0x6c, 0x76, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x02, 0x6c, 0x76, 0x12, 0x28, 0x0a, 0x03, 0x76, 0x61, 0x6c,
0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03,
0x76, 0x61, 0x6c, 0x12, 0x2e, 0x0a, 0x03, 0x64, 0x62, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03,
0x64, 0x62, 0x6c, 0x12, 0x2d, 0x0a, 0x03, 0x66, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x66,
0x6c, 0x74, 0x12, 0x2d, 0x0a, 0x03, 0x69, 0x36, 0x34, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x69, 0x36,
0x34, 0x12, 0x2e, 0x0a, 0x03, 0x75, 0x36, 0x34, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x75, 0x36,
0x34, 0x12, 0x2d, 0x0a, 0x03, 0x69, 0x33, 0x32, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x69, 0x33, 0x32,
0x12, 0x2e, 0x0a, 0x03, 0x75, 0x33, 0x32, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x75, 0x33, 0x32,
0x12, 0x2e, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x6f, 0x6c,
0x12, 0x2e, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x73, 0x74, 0x72,
0x12, 0x31, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x62, 0x79,
0x74, 0x65, 0x73, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x3b, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x62,
}
var (
file_test_objects_proto_rawDescOnce sync.Once
file_test_objects_proto_rawDescData = file_test_objects_proto_rawDesc
)
func file_test_objects_proto_rawDescGZIP() []byte {
file_test_objects_proto_rawDescOnce.Do(func() {
file_test_objects_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_objects_proto_rawDescData)
})
return file_test_objects_proto_rawDescData
}
var file_test_objects_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_test_objects_proto_goTypes = []interface{}{
(*KnownTypes)(nil), // 0: jsonpb.KnownTypes
(*anypb.Any)(nil), // 1: google.protobuf.Any
(*durationpb.Duration)(nil), // 2: google.protobuf.Duration
(*structpb.Struct)(nil), // 3: google.protobuf.Struct
(*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp
(*structpb.ListValue)(nil), // 5: google.protobuf.ListValue
(*structpb.Value)(nil), // 6: google.protobuf.Value
(*wrapperspb.DoubleValue)(nil), // 7: google.protobuf.DoubleValue
(*wrapperspb.FloatValue)(nil), // 8: google.protobuf.FloatValue
(*wrapperspb.Int64Value)(nil), // 9: google.protobuf.Int64Value
(*wrapperspb.UInt64Value)(nil), // 10: google.protobuf.UInt64Value
(*wrapperspb.Int32Value)(nil), // 11: google.protobuf.Int32Value
(*wrapperspb.UInt32Value)(nil), // 12: google.protobuf.UInt32Value
(*wrapperspb.BoolValue)(nil), // 13: google.protobuf.BoolValue
(*wrapperspb.StringValue)(nil), // 14: google.protobuf.StringValue
(*wrapperspb.BytesValue)(nil), // 15: google.protobuf.BytesValue
}
var file_test_objects_proto_depIdxs = []int32{
1, // 0: jsonpb.KnownTypes.an:type_name -> google.protobuf.Any
2, // 1: jsonpb.KnownTypes.dur:type_name -> google.protobuf.Duration
3, // 2: jsonpb.KnownTypes.st:type_name -> google.protobuf.Struct
4, // 3: jsonpb.KnownTypes.ts:type_name -> google.protobuf.Timestamp
5, // 4: jsonpb.KnownTypes.lv:type_name -> google.protobuf.ListValue
6, // 5: jsonpb.KnownTypes.val:type_name -> google.protobuf.Value
7, // 6: jsonpb.KnownTypes.dbl:type_name -> google.protobuf.DoubleValue
8, // 7: jsonpb.KnownTypes.flt:type_name -> google.protobuf.FloatValue
9, // 8: jsonpb.KnownTypes.i64:type_name -> google.protobuf.Int64Value
10, // 9: jsonpb.KnownTypes.u64:type_name -> google.protobuf.UInt64Value
11, // 10: jsonpb.KnownTypes.i32:type_name -> google.protobuf.Int32Value
12, // 11: jsonpb.KnownTypes.u32:type_name -> google.protobuf.UInt32Value
13, // 12: jsonpb.KnownTypes.bool:type_name -> google.protobuf.BoolValue
14, // 13: jsonpb.KnownTypes.str:type_name -> google.protobuf.StringValue
15, // 14: jsonpb.KnownTypes.bytes:type_name -> google.protobuf.BytesValue
15, // [15:15] is the sub-list for method output_type
15, // [15:15] is the sub-list for method input_type
15, // [15:15] is the sub-list for extension type_name
15, // [15:15] is the sub-list for extension extendee
0, // [0:15] is the sub-list for field type_name
}
func init() { file_test_objects_proto_init() }
func file_test_objects_proto_init() {
if File_test_objects_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_test_objects_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*KnownTypes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_test_objects_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_test_objects_proto_goTypes,
DependencyIndexes: file_test_objects_proto_depIdxs,
MessageInfos: file_test_objects_proto_msgTypes,
}.Build()
File_test_objects_proto = out.File
file_test_objects_proto_rawDesc = nil
file_test_objects_proto_goTypes = nil
file_test_objects_proto_depIdxs = nil
}

View File

@ -0,0 +1,30 @@
syntax = "proto2";
import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
package jsonpb;
option go_package=".;jsonpb";
message KnownTypes {
optional google.protobuf.Any an = 14;
optional google.protobuf.Duration dur = 1;
optional google.protobuf.Struct st = 12;
optional google.protobuf.Timestamp ts = 2;
optional google.protobuf.ListValue lv = 15;
optional google.protobuf.Value val = 16;
optional google.protobuf.DoubleValue dbl = 3;
optional google.protobuf.FloatValue flt = 4;
optional google.protobuf.Int64Value i64 = 5;
optional google.protobuf.UInt64Value u64 = 6;
optional google.protobuf.Int32Value i32 = 7;
optional google.protobuf.UInt32Value u32 = 8;
optional google.protobuf.BoolValue bool = 9;
optional google.protobuf.StringValue str = 10;
optional google.protobuf.BytesValue bytes = 11;
}

1030
internal/testing/test.pb.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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",
}

View File

@ -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{}

409
invoke.go Normal file
View File

@ -0,0 +1,409 @@
package grpcurl
import (
"bytes"
"context"
"fmt"
"io"
"strings"
"sync"
"sync/atomic"
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/dynamic/grpcdynamic"
"github.com/jhump/protoreflect/grpcreflect"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// InvocationEventHandler is a bag of callbacks for handling events that occur in the course
// of invoking an RPC. The handler also provides request data that is sent. The callbacks are
// generally called in the order they are listed below.
type InvocationEventHandler interface {
// OnResolveMethod is called with a descriptor of the method that is being invoked.
OnResolveMethod(*desc.MethodDescriptor)
// OnSendHeaders is called with the request metadata that is being sent.
OnSendHeaders(metadata.MD)
// OnReceiveHeaders is called when response headers have been received.
OnReceiveHeaders(metadata.MD)
// OnReceiveResponse is called for each response message received.
OnReceiveResponse(proto.Message)
// OnReceiveTrailers is called when response trailers and final RPC status have been received.
OnReceiveTrailers(*status.Status, metadata.MD)
}
// RequestMessageSupplier is a function that is called to retrieve request
// messages for a GRPC operation. This type is deprecated and will be removed in
// a future release.
//
// Deprecated: This is only used with the deprecated InvokeRpc. Instead, use
// RequestSupplier with InvokeRPC.
type RequestMessageSupplier func() ([]byte, error)
// InvokeRpc uses the given gRPC connection to invoke the given method. This function is deprecated
// and will be removed in a future release. It just delegates to the similarly named InvokeRPC
// method, whose signature is only slightly different.
//
// Deprecated: use InvokeRPC instead.
func InvokeRpc(ctx context.Context, source DescriptorSource, cc *grpc.ClientConn, methodName string,
headers []string, handler InvocationEventHandler, requestData RequestMessageSupplier) error {
return InvokeRPC(ctx, source, cc, methodName, headers, handler, func(m proto.Message) error {
// New function is almost identical, but the request supplier function works differently.
// So we adapt the logic here to maintain compatibility.
data, err := requestData()
if err != nil {
return err
}
return jsonpb.Unmarshal(bytes.NewReader(data), m)
})
}
// RequestSupplier is a function that is called to populate messages for a gRPC operation. The
// function should populate the given message or return a non-nil error. If the supplier has no
// more messages, it should return io.EOF. When it returns io.EOF, it should not in any way
// modify the given message argument.
type RequestSupplier func(proto.Message) error
// InvokeRPC uses the given gRPC channel to invoke the given method. The given descriptor source
// is used to determine the type of method and the type of request and response message. The given
// headers are sent as request metadata. Methods on the given event handler are called as the
// invocation proceeds.
//
// The given requestData function supplies the actual data to send. It should return io.EOF when
// there is no more request data. If the method being invoked is a unary or server-streaming RPC
// (e.g. exactly one request message) and there is no request data (e.g. the first invocation of
// the function returns io.EOF), then an empty request message is sent.
//
// If the requestData function and the given event handler coordinate or share any state, they should
// be thread-safe. This is because the requestData function may be called from a different goroutine
// than the one invoking event callbacks. (This only happens for bi-directional streaming RPCs, where
// one goroutine sends request messages and another consumes the response messages).
func InvokeRPC(ctx context.Context, source DescriptorSource, ch grpcdynamic.Channel, methodName string,
headers []string, handler InvocationEventHandler, requestData RequestSupplier) error {
md := MetadataFromHeaders(headers)
svc, mth := parseSymbol(methodName)
if svc == "" || mth == "" {
return fmt.Errorf("given method name %q is not in expected format: 'service/method' or 'service.method'", methodName)
}
dsc, err := source.FindSymbol(svc)
if err != nil {
// return a gRPC status error if hasStatus is true
errStatus, hasStatus := status.FromError(err)
switch {
case hasStatus && isNotFoundError(err):
return status.Errorf(errStatus.Code(), "target server does not expose service %q: %s", svc, errStatus.Message())
case hasStatus:
return status.Errorf(errStatus.Code(), "failed to query for service descriptor %q: %s", svc, errStatus.Message())
case isNotFoundError(err):
return fmt.Errorf("target server does not expose service %q", svc)
}
return fmt.Errorf("failed to query for service descriptor %q: %v", svc, err)
}
sd, ok := dsc.(*desc.ServiceDescriptor)
if !ok {
return fmt.Errorf("target server does not expose service %q", svc)
}
mtd := sd.FindMethodByName(mth)
if mtd == nil {
return fmt.Errorf("service %q does not include a method named %q", svc, mth)
}
handler.OnResolveMethod(mtd)
// we also download any applicable extensions so we can provide full support for parsing user-provided data
var ext dynamic.ExtensionRegistry
alreadyFetched := map[string]bool{}
if err = fetchAllExtensions(source, &ext, mtd.GetInputType(), alreadyFetched); err != nil {
return fmt.Errorf("error resolving server extensions for message %s: %v", mtd.GetInputType().GetFullyQualifiedName(), err)
}
if err = fetchAllExtensions(source, &ext, mtd.GetOutputType(), alreadyFetched); err != nil {
return fmt.Errorf("error resolving server extensions for message %s: %v", mtd.GetOutputType().GetFullyQualifiedName(), err)
}
msgFactory := dynamic.NewMessageFactoryWithExtensionRegistry(&ext)
req := msgFactory.NewMessage(mtd.GetInputType())
handler.OnSendHeaders(md)
ctx = metadata.NewOutgoingContext(ctx, md)
stub := grpcdynamic.NewStubWithMessageFactory(ch, msgFactory)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if mtd.IsClientStreaming() && mtd.IsServerStreaming() {
return invokeBidi(ctx, stub, mtd, handler, requestData, req)
} else if mtd.IsClientStreaming() {
return invokeClientStream(ctx, stub, mtd, handler, requestData, req)
} else if mtd.IsServerStreaming() {
return invokeServerStream(ctx, stub, mtd, handler, requestData, req)
} else {
return invokeUnary(ctx, stub, mtd, handler, requestData, req)
}
}
func invokeUnary(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescriptor, handler InvocationEventHandler,
requestData RequestSupplier, req proto.Message) error {
err := requestData(req)
if err != nil && err != io.EOF {
return fmt.Errorf("error getting request data: %v", err)
}
if err != io.EOF {
// verify there is no second message, which is a usage error
err := requestData(req)
if err == nil {
return fmt.Errorf("method %q is a unary RPC, but request data contained more than 1 message", md.GetFullyQualifiedName())
} else if err != io.EOF {
return fmt.Errorf("error getting request data: %v", err)
}
}
// Now we can actually invoke the RPC!
var respHeaders metadata.MD
var respTrailers metadata.MD
resp, err := stub.InvokeRpc(ctx, md, req, grpc.Trailer(&respTrailers), grpc.Header(&respHeaders))
stat, ok := status.FromError(err)
if !ok {
// Error codes sent from the server will get printed differently below.
// So just bail for other kinds of errors here.
return fmt.Errorf("grpc call for %q failed: %v", md.GetFullyQualifiedName(), err)
}
handler.OnReceiveHeaders(respHeaders)
if stat.Code() == codes.OK {
handler.OnReceiveResponse(resp)
}
handler.OnReceiveTrailers(stat, respTrailers)
return nil
}
func invokeClientStream(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescriptor, handler InvocationEventHandler,
requestData RequestSupplier, req proto.Message) error {
// invoke the RPC!
str, err := stub.InvokeRpcClientStream(ctx, md)
// Upload each request message in the stream
var resp proto.Message
for err == nil {
err = requestData(req)
if err == io.EOF {
resp, err = str.CloseAndReceive()
break
}
if err != nil {
return fmt.Errorf("error getting request data: %v", err)
}
err = str.SendMsg(req)
if err == io.EOF {
// We get EOF on send if the server says "go away"
// We have to use CloseAndReceive to get the actual code
resp, err = str.CloseAndReceive()
break
}
req.Reset()
}
// finally, process response data
stat, ok := status.FromError(err)
if !ok {
// Error codes sent from the server will get printed differently below.
// So just bail for other kinds of errors here.
return fmt.Errorf("grpc call for %q failed: %v", md.GetFullyQualifiedName(), err)
}
if str != nil {
if respHeaders, err := str.Header(); err == nil {
handler.OnReceiveHeaders(respHeaders)
}
}
if stat.Code() == codes.OK {
handler.OnReceiveResponse(resp)
}
if str != nil {
handler.OnReceiveTrailers(stat, str.Trailer())
}
return nil
}
func invokeServerStream(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescriptor, handler InvocationEventHandler,
requestData RequestSupplier, req proto.Message) error {
err := requestData(req)
if err != nil && err != io.EOF {
return fmt.Errorf("error getting request data: %v", err)
}
if err != io.EOF {
// verify there is no second message, which is a usage error
err := requestData(req)
if err == nil {
return fmt.Errorf("method %q is a server-streaming RPC, but request data contained more than 1 message", md.GetFullyQualifiedName())
} else if err != io.EOF {
return fmt.Errorf("error getting request data: %v", err)
}
}
// Now we can actually invoke the RPC!
str, err := stub.InvokeRpcServerStream(ctx, md, req)
if str != nil {
if respHeaders, err := str.Header(); err == nil {
handler.OnReceiveHeaders(respHeaders)
}
}
// Download each response message
for err == nil {
var resp proto.Message
resp, err = str.RecvMsg()
if err != nil {
if err == io.EOF {
err = nil
}
break
}
handler.OnReceiveResponse(resp)
}
stat, ok := status.FromError(err)
if !ok {
// Error codes sent from the server will get printed differently below.
// So just bail for other kinds of errors here.
return fmt.Errorf("grpc call for %q failed: %v", md.GetFullyQualifiedName(), err)
}
if str != nil {
handler.OnReceiveTrailers(stat, str.Trailer())
}
return nil
}
func invokeBidi(ctx context.Context, stub grpcdynamic.Stub, md *desc.MethodDescriptor, handler InvocationEventHandler,
requestData RequestSupplier, req proto.Message) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// invoke the RPC!
str, err := stub.InvokeRpcBidiStream(ctx, md)
var wg sync.WaitGroup
var sendErr atomic.Value
defer wg.Wait()
if err == nil {
wg.Add(1)
go func() {
defer wg.Done()
// Concurrently upload each request message in the stream
var err error
for err == nil {
err = requestData(req)
if err == io.EOF {
err = str.CloseSend()
break
}
if err != nil {
err = fmt.Errorf("error getting request data: %v", err)
cancel()
break
}
err = str.SendMsg(req)
req.Reset()
}
if err != nil {
sendErr.Store(err)
}
}()
}
if str != nil {
if respHeaders, err := str.Header(); err == nil {
handler.OnReceiveHeaders(respHeaders)
}
}
// Download each response message
for err == nil {
var resp proto.Message
resp, err = str.RecvMsg()
if err != nil {
if err == io.EOF {
err = nil
}
break
}
handler.OnReceiveResponse(resp)
}
if se, ok := sendErr.Load().(error); ok && se != io.EOF {
err = se
}
stat, ok := status.FromError(err)
if !ok {
// Error codes sent from the server will get printed differently below.
// So just bail for other kinds of errors here.
return fmt.Errorf("grpc call for %q failed: %v", md.GetFullyQualifiedName(), err)
}
if str != nil {
handler.OnReceiveTrailers(stat, str.Trailer())
}
return nil
}
type notFoundError string
func notFound(kind, name string) error {
return notFoundError(fmt.Sprintf("%s not found: %s", kind, name))
}
func (e notFoundError) Error() string {
return string(e)
}
func isNotFoundError(err error) bool {
if grpcreflect.IsElementNotFoundError(err) {
return true
}
_, ok := err.(notFoundError)
return ok
}
func parseSymbol(svcAndMethod string) (string, string) {
pos := strings.LastIndex(svcAndMethod, "/")
if pos < 0 {
pos = strings.LastIndex(svcAndMethod, ".")
if pos < 0 {
return "", ""
}
}
return svcAndMethod[:pos], svcAndMethod[pos+1:]
}

View File

@ -7,14 +7,17 @@ cd "$(dirname $0)"
# Run this script to generate files used by tests. # Run this script to generate files used by tests.
echo "Creating protosets..." echo "Creating protosets..."
protoc ../../../google.golang.org/grpc/interop/grpc_testing/test.proto \ protoc testing/test.proto \
-I../../../ --include_imports \ --include_imports \
--descriptor_set_out=testing/test.protoset --descriptor_set_out=testing/test.protoset
protoc testing/example.proto \ 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

79
releasing/README.md Normal file
View File

@ -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!

View File

@ -0,0 +1,11 @@
## Changes
### Command-line tool
* _In this list, describe the changes to the command-line tool._
* _Use one bullet per change. Include both bug-fixes and improvements. Omit this section if there are no changes that impact the command-line tool._
### Go package "github.com/fullstorydev/grpcurl"
* _In this list, describe the changes to exported API in the main package in this repo: "github.com/fullstorydev/grpcurl". These will often be closely related to changes to the command-line tool, though not always: changes that only impact the cmd/grpcurl directory of this repo do not impact exported API._
* _Use one bullet per change. Include both bug-fixes and improvements. Omit this section if there are no changes that impact the exported API._

62
releasing/do-release.sh Executable file
View File

@ -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

23
snap/README.md Normal file
View File

@ -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!

47
snap/snapcraft.yaml Normal file
View File

@ -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.

View File

@ -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,35 +102,53 @@ 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)
} }
// client connection (usually) succeeds since client is not waiting for TLS handshake // client connection (usually) succeeds since client is not waiting for TLS handshake
e, err := createTestServerAndClient(serverCreds, nil) // (we try several times, but if we never get a connection and the error message is
if err != nil { // a known/expected possibility, we'll just bail)
if strings.Contains(err.Error(), "deadline exceeded") { var e testEnv
// It is possible that connection never becomes healthy: failCount := 0
for {
e, err = createTestServerAndClient(serverCreds, nil)
if err == nil {
// success!
defer e.Close()
break
}
if strings.Contains(err.Error(), "deadline exceeded") ||
strings.Contains(err.Error(), "use of closed network connection") {
// It is possible that the connection never becomes healthy:
// 1) grpc connects successfully // 1) grpc connects successfully
// 2) grpc client tries to send HTTP/2 preface and settings frame // 2) grpc client tries to send HTTP/2 preface and settings frame
// 3) server, expecting handshake, closes the connection // 3) server, expecting handshake, closes the connection
// 4) in the client, the write fails, so the connection never // 4) in the client, the write fails, so the connection never
// becomes ready // becomes ready
// More often than not, the connection becomes ready (presumably // The client will attempt to reconnect on transient errors, so
// the write to the socket succeeds before the server closes the // may eventually bump into the connect time limit. This used to
// connection). But when it does not, it is possible to observe // result in a "deadline exceeded" error, but more recent versions
// timeouts when setting up the connection. // of the grpc library report any underlying I/O error instead, so
return // we also check for "use of closed network connection".
failCount++
if failCount > 5 {
return // bail...
}
// we'll try again
} else {
// some other error occurred, so we'll consider that a test failure
t.Fatalf("failed to setup server and client: %v", err)
} }
t.Fatalf("failed to setup server and client: %v", err)
} }
defer e.Close()
// 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")
} }
@ -146,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)
@ -162,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)
@ -182,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)
@ -222,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.FailFast(false)) _, 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)
} }
@ -305,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
@ -332,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