Compare commits

..

No commits in common. "master" and "v1.8.9" have entirely different histories.

25 changed files with 274 additions and 508 deletions

View File

@ -9,22 +9,22 @@ shared_configs:
# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference # Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference
version: 2.1 version: 2.1
jobs: jobs:
build-1-23: build-1-18:
working_directory: ~/repo working_directory: ~/repo
docker: docker:
- image: cimg/go:1.23 - image: cimg/go:1.18
steps: *simple_job_steps steps: *simple_job_steps
build-1-24: build-1-19:
working_directory: ~/repo working_directory: ~/repo
docker: docker:
- image: cimg/go:1.24 - image: cimg/go:1.19
steps: *simple_job_steps steps: *simple_job_steps
build-1-25: build-1-20:
working_directory: ~/repo working_directory: ~/repo
docker: docker:
- image: cimg/go:1.25 - image: cimg/go:1.20
steps: steps:
- checkout - checkout
- run: - run:
@ -32,9 +32,16 @@ jobs:
command: | command: |
make ci make ci
build-1-21:
working_directory: ~/repo
docker:
- image: cimg/go:1.21
steps: *simple_job_steps
workflows: workflows:
pr-build-test: pr-build-test:
jobs: jobs:
- build-1-23 - build-1-18
- build-1-24 - build-1-19
- build-1-25 - build-1-20
- build-1-21

1
.gitignore vendored
View File

@ -2,4 +2,3 @@ dist/
.idea/ .idea/
VERSION VERSION
.tmp/ .tmp/
*.snap

View File

@ -8,23 +8,14 @@ builds:
goarch: goarch:
- amd64 - amd64
- 386 - 386
- arm
- arm64 - arm64
- s390x - s390x
- ppc64le - ppc64le
goarm:
- 5
- 6
- 7
ignore: ignore:
- goos: darwin - goos: darwin
goarch: 386 goarch: 386
- goos: windows - goos: windows
goarch: arm64 goarch: arm64
- goos: darwin
goarch: arm
- goos: windows
goarch: arm
- goos: darwin - goos: darwin
goarch: s390x goarch: s390x
- goos: windows - goos: windows
@ -38,26 +29,12 @@ builds:
archives: archives:
- format: tar.gz - 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: format_overrides:
- goos: windows - goos: windows
format: zip format: zip
replacements:
amd64: x86_64
386: x86_32
darwin: osx
files: files:
- LICENSE - 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,5 +1,5 @@
FROM golang:1.25-alpine AS builder FROM golang:1.18-alpine as builder
LABEL maintainer="Fullstory Engineering" MAINTAINER FullStory Engineering
# create non-privileged group and user # create non-privileged group and user
RUN addgroup -S grpcurl && adduser -S grpcurl -G grpcurl RUN addgroup -S grpcurl && adduser -S grpcurl -G grpcurl
@ -16,7 +16,7 @@ RUN go build -o /grpcurl \
-ldflags "-w -extldflags \"-static\" -X \"main.version=$(cat VERSION)\"" \ -ldflags "-w -extldflags \"-static\" -X \"main.version=$(cat VERSION)\"" \
./cmd/grpcurl ./cmd/grpcurl
FROM alpine:3 AS alpine FROM alpine:3 as alpine
WORKDIR / WORKDIR /
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 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 /etc/passwd /etc/passwd

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,11 +1,11 @@
dev_build_version=$(shell git describe --tags --always --dirty) dev_build_version=$(shell git describe --tags --always --dirty)
export PATH := $(shell pwd)/.tmp/protoc/bin:$(PATH) export PATH := $(shell pwd)/.tmp/protoc/bin:$(PATH)
export PROTOC_VERSION := 22.0 export PROTOC_VERSION := 22.0
# Disable CGO for improved compatibility across distros # Disable CGO for improved compatibility across distros
export CGO_ENABLED=0 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
@ -18,12 +18,10 @@ 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:
@ -31,8 +29,8 @@ install:
.PHONY: release .PHONY: release
release: release:
@go install github.com/goreleaser/goreleaser@v1.21.0 @go install github.com/goreleaser/goreleaser@v1.10.0
goreleaser release --clean goreleaser release --rm-dist
.PHONY: docker .PHONY: docker
docker: docker:
@ -44,15 +42,13 @@ docker:
generate: .tmp/protoc/bin/protoc generate: .tmp/protoc/bin/protoc
@go install google.golang.org/protobuf/cmd/protoc-gen-go@a709e31e5d12 @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 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 generate ./...
go mod tidy
.PHONY: checkgenerate .PHONY: checkgenerate
checkgenerate: generate checkgenerate: generate
git status --porcelain -- '**/*.go' git status --porcelain
@if [ -n "$$(git status --porcelain -- '**/*.go')" ]; then \ @if [ -n "$$(git status --porcelain)" ]; then \
git diff -- '**/*.go'; \ git diff; \
exit 1; \ exit 1; \
fi fi
@ -69,8 +65,8 @@ vet:
.PHONY: staticcheck .PHONY: staticcheck
staticcheck: staticcheck:
@go install honnef.co/go/tools/cmd/staticcheck@2025.1.1 @go install honnef.co/go/tools/cmd/staticcheck@v0.4.3
staticcheck -checks "inherit,-SA1019" ./... staticcheck ./...
.PHONY: ineffassign .PHONY: ineffassign
ineffassign: ineffassign:
@ -79,7 +75,7 @@ ineffassign:
.PHONY: predeclared .PHONY: predeclared
predeclared: predeclared:
@go install github.com/nishanths/predeclared@51e8c974458a0f93dc03fe356f91ae1a6d791e6f @go install github.com/nishanths/predeclared@5f2f810c9ae6
predeclared ./... predeclared ./...
# Intentionally omitted from CI, but target here for ad-hoc reports. # Intentionally omitted from CI, but target here for ad-hoc reports.
@ -95,7 +91,8 @@ errcheck:
errcheck ./... errcheck ./...
.PHONY: test .PHONY: test
test: deps test:
# The race detector requires CGO: https://github.com/golang/go/issues/6508
CGO_ENABLED=1 go test -race ./... CGO_ENABLED=1 go test -race ./...
.tmp/protoc/bin/protoc: ./Makefile ./download_protoc.sh .tmp/protoc/bin/protoc: ./Makefile ./download_protoc.sh

View File

@ -1,7 +1,6 @@
# gRPCurl # gRPCurl
[![Build Status](https://circleci.com/gh/fullstorydev/grpcurl/tree/master.svg?style=svg)](https://circleci.com/gh/fullstorydev/grpcurl/tree/master) [![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.
@ -80,12 +79,6 @@ 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_: You can see more details and the full list of other packages for `grpcurl` at _repology.org_:
https://repology.org/project/grpcurl/information https://repology.org/project/grpcurl/information
### Snap
You can install `grpcurl` using the snap package:
`snap install grpcurl`
### From Source ### From Source
If you already have the [Go SDK](https://golang.org/doc/install) installed, you can use the `go` If you already have the [Go SDK](https://golang.org/doc/install) installed, you can use the `go`
tool to install `grpcurl`: tool to install `grpcurl`:
@ -152,13 +145,6 @@ 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
@ -173,13 +159,6 @@ 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:

10
cmd/grpcurl/go1_10.go Normal file
View File

@ -0,0 +1,10 @@
//go:build go1.10
// +build go1.10
package main
func indent() string {
// In Go 1.10 and up, the flag package automatically
// adds the right indentation.
return ""
}

10
cmd/grpcurl/go1_9.go Normal file
View File

@ -0,0 +1,10 @@
//go:build !go1.10
// +build !go1.10
package main
func indent() string {
// In Go 1.9 and older, we need to add indentation
// after newlines in the flag doc strings.
return " \t"
}

View File

@ -8,14 +8,13 @@ import (
"flag" "flag"
"fmt" "fmt"
"io" "io"
"math"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 required to use APIs in other grpcurl package "github.com/jhump/protoreflect/desc"
"github.com/jhump/protoreflect/grpcreflect" "github.com/jhump/protoreflect/grpcreflect"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
@ -34,9 +33,9 @@ import (
"github.com/fullstorydev/grpcurl" "github.com/fullstorydev/grpcurl"
) )
// To avoid confusion between program error codes and the gRPC response // To avoid confusion between program error codes and the gRPC resonse
// status codes 'Cancelled' and 'Unknown', 1 and 2 respectively, // status codes 'Cancelled' and 'Unknown', 1 and 2 respectively,
// the response status codes emitted use an offset of 64 // the response status codes emitted use an offest of 64
const statusCodeOffset = 64 const statusCodeOffset = 64
const noVersion = "dev build <no version set>" const noVersion = "dev build <no version set>"
@ -98,8 +97,7 @@ var (
value of the ":authority" pseudo-header in the HTTP/2 protocol. When TLS value of the ":authority" pseudo-header in the HTTP/2 protocol. When TLS
is used, this will also be used as the server name when verifying the is used, this will also be used as the server name when verifying the
server's certificate. It defaults to the address that is provided in the server's certificate. It defaults to the address that is provided in the
positional arguments, or 'localhost' in the case of a unix domain positional arguments.`))
socket.`))
userAgent = flags.String("user-agent", "", prettify(` userAgent = flags.String("user-agent", "", prettify(`
If set, the specified value will be added to the User-Agent header set If set, the specified value will be added to the User-Agent header set
by the grpc-go library. by the grpc-go library.
@ -135,11 +133,9 @@ var (
is received for this same period then the connection is closed and the is received for this same period then the connection is closed and the
operation fails.`)) operation fails.`))
maxTime = flags.Float64("max-time", 0, prettify(` maxTime = flags.Float64("max-time", 0, prettify(`
The maximum total time the operation can take, in seconds. This sets a The maximum total time the operation can take, in seconds. This is
timeout on the gRPC context, allowing both client and server to give up useful for preventing batch jobs that use grpcurl from hanging due to
after the deadline has past. This is useful for preventing batch jobs slow or bad network links or due to incorrect stream method usage.`))
that use grpcurl from hanging due to slow or bad network links or due
to incorrect stream method usage.`))
maxMsgSz = flags.Int("max-msg-sz", 0, prettify(` maxMsgSz = flags.Int("max-msg-sz", 0, prettify(`
The maximum encoded size of a response message, in bytes, that grpcurl The maximum encoded size of a response message, in bytes, that grpcurl
will accept. If not specified, defaults to 4,194,304 (4 megabytes).`)) will accept. If not specified, defaults to 4,194,304 (4 megabytes).`))
@ -152,20 +148,12 @@ var (
file if this option is given. When invoking an RPC and this option is file if this option is given. When invoking an RPC and this option is
given, the method being invoked and its transitive dependencies will be given, the method being invoked and its transitive dependencies will be
included in the output file.`)) included in the output file.`))
protoOut = flags.String("proto-out-dir", "", prettify(`
The name of a directory where the generated .proto files will be written.
With the list and describe verbs, the listed or described elements and
their transitive dependencies will be written as .proto files in the
specified directory if this option is given. When invoking an RPC and
this option is given, the method being invoked and its transitive
dependencies will be included in the generated .proto files in the
output directory.`))
msgTemplate = flags.Bool("msg-template", false, prettify(` msgTemplate = flags.Bool("msg-template", false, prettify(`
When describing messages, show a template of input data.`)) When describing messages, show a template of input data.`))
verbose = flags.Bool("v", false, prettify(` verbose = flags.Bool("v", false, prettify(`
Enable verbose output.`)) Enable verbose output.`))
veryVerbose = flags.Bool("vv", false, prettify(` veryVerbose = flags.Bool("vv", false, prettify(`
Enable very verbose output (includes timing data).`)) Enable very verbose output.`))
serverName = flags.String("servername", "", prettify(` serverName = flags.String("servername", "", prettify(`
Override server name when validating TLS certificate. This flag is Override server name when validating TLS certificate. This flag is
ignored if -plaintext or -insecure is used. ignored if -plaintext or -insecure is used.
@ -286,32 +274,6 @@ func (cs compositeSource) AllExtensionsForType(typeName string) ([]*desc.FieldDe
return exts, nil return exts, nil
} }
type timingData struct {
Title string
Start time.Time
Value time.Duration
Parent *timingData
Sub []*timingData
}
func (d *timingData) Child(title string) *timingData {
if d == nil {
return nil
}
child := &timingData{Title: title, Start: time.Now()}
d.Sub = append(d.Sub, child)
return child
}
func (d *timingData) Done() {
if d == nil {
return
}
if d.Value == 0 {
d.Value = time.Since(d.Start)
}
}
func main() { func main() {
flags.Usage = usage flags.Usage = usage
flags.Parse(os.Args[1:]) flags.Parse(os.Args[1:])
@ -397,16 +359,8 @@ func main() {
if *verbose { if *verbose {
verbosityLevel = 1 verbosityLevel = 1
} }
var rootTiming *timingData
if *veryVerbose { if *veryVerbose {
verbosityLevel = 2 verbosityLevel = 2
rootTiming = &timingData{Title: "Timing Data", Start: time.Now()}
defer func() {
rootTiming.Done()
dumpTiming(rootTiming, 0)
}()
} }
var symbol string var symbol string
@ -458,24 +412,22 @@ func main() {
ctx := context.Background() ctx := context.Background()
if *maxTime > 0 { if *maxTime > 0 {
timeout := floatSecondsToDuration(*maxTime) timeout := time.Duration(*maxTime * float64(time.Second))
var cancel context.CancelFunc var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, timeout) ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel() defer cancel()
} }
dial := func() *grpc.ClientConn { dial := func() *grpc.ClientConn {
dialTiming := rootTiming.Child("Dial")
defer dialTiming.Done()
dialTime := 10 * time.Second dialTime := 10 * time.Second
if *connectTimeout > 0 { if *connectTimeout > 0 {
dialTime = floatSecondsToDuration(*connectTimeout) dialTime = time.Duration(*connectTimeout * float64(time.Second))
} }
ctx, cancel := context.WithTimeout(ctx, dialTime) ctx, cancel := context.WithTimeout(ctx, dialTime)
defer cancel() defer cancel()
var opts []grpc.DialOption var opts []grpc.DialOption
if *keepaliveTime > 0 { if *keepaliveTime > 0 {
timeout := floatSecondsToDuration(*keepaliveTime) timeout := time.Duration(*keepaliveTime * float64(time.Second))
opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{ opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: timeout, Time: timeout,
Timeout: timeout, Timeout: timeout,
@ -484,13 +436,6 @@ func main() {
if *maxMsgSz > 0 { if *maxMsgSz > 0 {
opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*maxMsgSz))) opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*maxMsgSz)))
} }
if isUnixSocket != nil && isUnixSocket() && !strings.HasPrefix(target, "unix://") {
// prepend unix:// to the address if it's not already there
// this is to maintain backwards compatibility because the custom dialer is replaced by
// the default dialer in grpc-go.
// https://github.com/fullstorydev/grpcurl/pull/480
target = "unix://" + target
}
var creds credentials.TransportCredentials var creds credentials.TransportCredentials
if *plaintext { if *plaintext {
if *authority != "" { if *authority != "" {
@ -506,9 +451,6 @@ func main() {
} }
creds = alts.NewClientCreds(clientOptions) creds = alts.NewClientCreds(clientOptions)
} else if usetls { } else if usetls {
tlsTiming := dialTiming.Child("TLS Setup")
defer tlsTiming.Done()
tlsConf, err := grpcurl.ClientTLSConfig(*insecure, *cacert, *cert, *key) tlsConf, err := grpcurl.ClientTLSConfig(*insecure, *cacert, *cert, *key)
if err != nil { if err != nil {
fail(err, "Failed to create TLS config") fail(err, "Failed to create TLS config")
@ -541,7 +483,6 @@ func main() {
if overrideName != "" { if overrideName != "" {
opts = append(opts, grpc.WithAuthority(overrideName)) opts = append(opts, grpc.WithAuthority(overrideName))
} }
tlsTiming.Done()
} else { } else {
panic("Should have defaulted to use TLS.") panic("Should have defaulted to use TLS.")
} }
@ -555,9 +496,11 @@ func main() {
} }
opts = append(opts, grpc.WithUserAgent(grpcurlUA)) opts = append(opts, grpc.WithUserAgent(grpcurlUA))
blockingDialTiming := dialTiming.Child("BlockingDial") network := "tcp"
defer blockingDialTiming.Done() if isUnixSocket != nil && isUnixSocket() {
cc, err := grpcurl.BlockingDial(ctx, "", target, creds, opts...) network = "unix"
}
cc, err := grpcurl.BlockingDial(ctx, network, target, creds, opts...)
if err != nil { if err != nil {
fail(err, "Failed to dial target host %q", target) fail(err, "Failed to dial target host %q", target)
} }
@ -609,7 +552,6 @@ func main() {
refCtx := metadata.NewOutgoingContext(ctx, md) refCtx := metadata.NewOutgoingContext(ctx, md)
cc = dial() cc = dial()
refClient = grpcreflect.NewClientAuto(refCtx, cc) refClient = grpcreflect.NewClientAuto(refCtx, cc)
refClient.AllowMissingFileDescriptors()
reflSource := grpcurl.DescriptorSourceFromServer(ctx, refClient) reflSource := grpcurl.DescriptorSourceFromServer(ctx, refClient)
if fileSource != nil { if fileSource != nil {
descSource = compositeSource{reflSource, fileSource} descSource = compositeSource{reflSource, fileSource}
@ -654,9 +596,6 @@ func main() {
if err := writeProtoset(descSource, svcs...); err != nil { if err := writeProtoset(descSource, svcs...); err != nil {
fail(err, "Failed to write protoset to %s", *protosetOut) fail(err, "Failed to write protoset to %s", *protosetOut)
} }
if err := writeProtos(descSource, svcs...); err != nil {
fail(err, "Failed to write protos to %s", *protoOut)
}
} else { } else {
methods, err := grpcurl.ListMethods(descSource, symbol) methods, err := grpcurl.ListMethods(descSource, symbol)
if err != nil { if err != nil {
@ -672,9 +611,6 @@ func main() {
if err := writeProtoset(descSource, symbol); err != nil { if err := writeProtoset(descSource, symbol); err != nil {
fail(err, "Failed to write protoset to %s", *protosetOut) fail(err, "Failed to write protoset to %s", *protosetOut)
} }
if err := writeProtos(descSource, symbol); err != nil {
fail(err, "Failed to write protos to %s", *protoOut)
}
} }
} else if describe { } else if describe {
@ -779,9 +715,6 @@ func main() {
if err := writeProtoset(descSource, symbols...); err != nil { if err := writeProtoset(descSource, symbols...); err != nil {
fail(err, "Failed to write protoset to %s", *protosetOut) fail(err, "Failed to write protoset to %s", *protosetOut)
} }
if err := writeProtos(descSource, symbol); err != nil {
fail(err, "Failed to write protos to %s", *protoOut)
}
} else { } else {
// Invoke an RPC // Invoke an RPC
@ -814,9 +747,7 @@ func main() {
VerbosityLevel: verbosityLevel, VerbosityLevel: verbosityLevel,
} }
invokeTiming := rootTiming.Child("InvokeRPC")
err = grpcurl.InvokeRPC(ctx, descSource, cc, symbol, append(addlHeaders, rpcHeaders...), h, rf.Next) err = grpcurl.InvokeRPC(ctx, descSource, cc, symbol, append(addlHeaders, rpcHeaders...), h, rf.Next)
invokeTiming.Done()
if err != nil { if err != nil {
if errStatus, ok := status.FromError(err); ok && *formatError { if errStatus, ok := status.FromError(err); ok && *formatError {
h.Status = errStatus h.Status = errStatus
@ -847,17 +778,6 @@ func main() {
} }
} }
func dumpTiming(td *timingData, lvl int) {
var ind strings.Builder
for x := 0; x < lvl; x++ {
ind.WriteString(" ")
}
fmt.Printf("%s%s: %s\n", ind.String(), td.Title, td.Value)
for _, sd := range td.Sub {
dumpTiming(sd, lvl+1)
}
}
func usage() { func usage() {
fmt.Fprintf(os.Stderr, `Usage: fmt.Fprintf(os.Stderr, `Usage:
%s [flags] [address] [list|describe] [symbol] %s [flags] [address] [list|describe] [symbol]
@ -905,7 +825,7 @@ func prettify(docString string) string {
j++ j++
} }
return strings.Join(parts[:j], "\n") return strings.Join(parts[:j], "\n"+indent())
} }
func warn(msg string, args ...interface{}) { func warn(msg string, args ...interface{}) {
@ -941,13 +861,6 @@ func writeProtoset(descSource grpcurl.DescriptorSource, symbols ...string) error
return grpcurl.WriteProtoset(f, descSource, symbols...) return grpcurl.WriteProtoset(f, descSource, symbols...)
} }
func writeProtos(descSource grpcurl.DescriptorSource, symbols ...string) error {
if *protoOut == "" {
return nil
}
return grpcurl.WriteProtoFiles(*protoOut, descSource, symbols...)
}
type optionalBoolFlag struct { type optionalBoolFlag struct {
set, val bool set, val bool
} }
@ -972,12 +885,3 @@ func (f *optionalBoolFlag) Set(s string) error {
func (f *optionalBoolFlag) IsBoolFlag() bool { func (f *optionalBoolFlag) IsBoolFlag() bool {
return true return true
} }
func floatSecondsToDuration(seconds float64) time.Duration {
durationFloat := seconds * float64(time.Second)
if durationFloat > math.MaxInt64 {
// Avoid overflow
return math.MaxInt64
}
return time.Duration(durationFloat)
}

View File

@ -5,15 +5,13 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"os" "io/ioutil"
"path/filepath"
"sync" "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/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above "github.com/jhump/protoreflect/desc"
"github.com/jhump/protoreflect/desc/protoparse" //lint:ignore SA1019 same as above "github.com/jhump/protoreflect/desc/protoparse"
"github.com/jhump/protoreflect/desc/protoprint" "github.com/jhump/protoreflect/dynamic"
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
"github.com/jhump/protoreflect/grpcreflect" "github.com/jhump/protoreflect/grpcreflect"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@ -43,7 +41,7 @@ type DescriptorSource interface {
func DescriptorSourceFromProtoSets(fileNames ...string) (DescriptorSource, error) { func DescriptorSourceFromProtoSets(fileNames ...string) (DescriptorSource, error) {
files := &descriptorpb.FileDescriptorSet{} files := &descriptorpb.FileDescriptorSet{}
for _, fileName := range fileNames { for _, fileName := range fileNames {
b, err := os.ReadFile(fileName) b, err := ioutil.ReadFile(fileName)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not load protoset file %q: %v", fileName, err) return nil, fmt.Errorf("could not load protoset file %q: %v", fileName, err)
} }
@ -260,9 +258,19 @@ func reflectionSupport(err error) error {
// given output. The output will include descriptors for all files in which the // given output. The output will include descriptors for all files in which the
// symbols are defined as well as their transitive dependencies. // symbols are defined as well as their transitive dependencies.
func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string) error { func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string) error {
filenames, fds, err := getFileDescriptors(symbols, descSource) // compute set of file descriptors
if err != nil { filenames := make([]string, 0, len(symbols))
return err fds := make(map[string]*desc.FileDescriptor, len(symbols))
for _, sym := range symbols {
d, err := descSource.FindSymbol(sym)
if err != nil {
return fmt.Errorf("failed to find descriptor for %q: %v", sym, err)
}
fd := d.GetFile()
if _, ok := fds[fd.GetName()]; !ok {
fds[fd.GetName()] = fd
filenames = append(filenames, fd.GetName())
}
} }
// now expand that to include transitive dependencies in topologically sorted // now expand that to include transitive dependencies in topologically sorted
// order (such that file always appears after its dependencies) // order (such that file always appears after its dependencies)
@ -294,76 +302,3 @@ func addFilesToSet(allFiles []*descriptorpb.FileDescriptorProto, expanded map[st
} }
return append(allFiles, fd.AsFileDescriptorProto()) return append(allFiles, fd.AsFileDescriptorProto())
} }
// WriteProtoFiles will use the given descriptor source to resolve all the given
// symbols and write proto files with their definitions to the given output directory.
func WriteProtoFiles(outProtoDirPath string, descSource DescriptorSource, symbols ...string) error {
filenames, fds, err := getFileDescriptors(symbols, descSource)
if err != nil {
return err
}
// now expand that to include transitive dependencies in topologically sorted
// order (such that file always appears after its dependencies)
expandedFiles := make(map[string]struct{}, len(fds))
allFileDescriptors := make([]*desc.FileDescriptor, 0, len(fds))
for _, filename := range filenames {
allFileDescriptors = addFilesToFileDescriptorList(allFileDescriptors, expandedFiles, fds[filename])
}
pr := protoprint.Printer{}
// now we can serialize to files
for i := range allFileDescriptors {
if err := writeProtoFile(outProtoDirPath, allFileDescriptors[i], &pr); err != nil {
return err
}
}
return nil
}
func writeProtoFile(outProtoDirPath string, fd *desc.FileDescriptor, pr *protoprint.Printer) error {
outFile := filepath.Join(outProtoDirPath, fd.GetFullyQualifiedName())
outDir := filepath.Dir(outFile)
if err := os.MkdirAll(outDir, 0777); err != nil {
return fmt.Errorf("failed to create directory %q: %w", outDir, err)
}
f, err := os.Create(outFile)
if err != nil {
return fmt.Errorf("failed to create proto file %q: %w", outFile, err)
}
defer f.Close()
if err := pr.PrintProtoFile(fd, f); err != nil {
return fmt.Errorf("failed to write proto file %q: %w", outFile, err)
}
return nil
}
func getFileDescriptors(symbols []string, descSource DescriptorSource) ([]string, map[string]*desc.FileDescriptor, error) {
// compute set of file descriptors
filenames := make([]string, 0, len(symbols))
fds := make(map[string]*desc.FileDescriptor, len(symbols))
for _, sym := range symbols {
d, err := descSource.FindSymbol(sym)
if err != nil {
return nil, nil, fmt.Errorf("failed to find descriptor for %q: %v", sym, err)
}
fd := d.GetFile()
if _, ok := fds[fd.GetName()]; !ok {
fds[fd.GetName()] = fd
filenames = append(filenames, fd.GetName())
}
}
return filenames, fds, nil
}
func addFilesToFileDescriptorList(allFiles []*desc.FileDescriptor, expanded map[string]struct{}, fd *desc.FileDescriptor) []*desc.FileDescriptor {
if _, ok := expanded[fd.GetName()]; ok {
// already seen this one
return allFiles
}
expanded[fd.GetName()] = struct{}{}
// add all dependencies first
for _, dep := range fd.GetDependencies() {
allFiles = addFilesToFileDescriptorList(allFiles, expanded, dep)
}
return append(allFiles, fd)
}

View File

@ -2,7 +2,7 @@ package grpcurl
import ( import (
"bytes" "bytes"
"os" "io/ioutil"
"testing" "testing"
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API "github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
@ -34,7 +34,7 @@ func TestWriteProtoset(t *testing.T) {
} }
func loadProtoset(path string) (*descriptorpb.FileDescriptorSet, error) { func loadProtoset(path string) (*descriptorpb.FileDescriptorSet, error) {
b, err := os.ReadFile(path) b, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -11,10 +11,10 @@ import (
"strings" "strings"
"sync" "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/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above "github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above "github.com/jhump/protoreflect/desc"
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above "github.com/jhump/protoreflect/dynamic"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"

View File

@ -7,9 +7,9 @@ import (
"strings" "strings"
"testing" "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/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above "github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above "github.com/jhump/protoreflect/desc"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
) )

45
go.mod
View File

@ -1,32 +1,31 @@
module github.com/fullstorydev/grpcurl module github.com/fullstorydev/grpcurl
go 1.24.0 go 1.18
toolchain go1.24.1
require ( require (
github.com/golang/protobuf v1.5.4 github.com/golang/protobuf v1.5.3
github.com/jhump/protoreflect v1.18.0 github.com/jhump/protoreflect v1.15.3
google.golang.org/grpc v1.66.2 google.golang.org/grpc v1.57.0
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.31.0
) )
require ( require (
cel.dev/expr v0.15.0 // indirect cloud.google.com/go/compute v1.19.1 // indirect
cloud.google.com/go/compute/metadata v0.3.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/bufbuild/protocompile v0.6.0 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 // indirect github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect
github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f // indirect
github.com/jhump/protoreflect/v2 v2.0.0-beta.1 // indirect github.com/envoyproxy/protoc-gen-validate v0.10.1 // indirect
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect golang.org/x/net v0.17.0 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/net v0.38.0 // indirect golang.org/x/sync v0.3.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect golang.org/x/sys v0.13.0 // indirect
golang.org/x/sync v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect
golang.org/x/sys v0.31.0 // indirect google.golang.org/appengine v1.6.7 // indirect
golang.org/x/text v0.23.0 // indirect google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
) )

150
go.sum
View File

@ -1,56 +1,106 @@
cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY=
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk=
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:7T++XKzy4xg7PKy+bM+Sa9/oe1OC88yz2hXQUISoXfA=
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q=
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/jhump/protoreflect v1.18.0 h1:TOz0MSR/0JOZ5kECB/0ufGnC2jdsgZ123Rd/k4Z5/2w= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/jhump/protoreflect v1.18.0/go.mod h1:ezWcltJIVF4zYdIFM+D/sHV4Oh5LNU08ORzCGfwvTz8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/jhump/protoreflect/v2 v2.0.0-beta.1 h1:Dw1rslK/VotaUGYsv53XVWITr+5RCPXfvvlGrM/+B6w= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/jhump/protoreflect/v2 v2.0.0-beta.1/go.mod h1:D9LBEowZyv8/iSu97FU2zmXG3JxVTmNw21mu63niFzU= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls=
github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M=
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -14,22 +14,20 @@ import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"os" "os"
"regexp" "regexp"
"slices"
"sort" "sort"
"strings" "strings"
"github.com/golang/protobuf/proto" //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 we have to import this because it appears in exported API
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above "github.com/jhump/protoreflect/desc"
"github.com/jhump/protoreflect/desc/protoprint" "github.com/jhump/protoreflect/desc/protoprint"
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above "github.com/jhump/protoreflect/dynamic"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
xdsCredentials "google.golang.org/grpc/credentials/xds"
_ "google.golang.org/grpc/health" // import grpc/health to enable transparent client side checking
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
protov2 "google.golang.org/protobuf/proto" protov2 "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/descriptorpb" "google.golang.org/protobuf/types/descriptorpb"
@ -451,9 +449,11 @@ func makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescriptor) pr
dm := dynamic.NewMessage(md) dm := dynamic.NewMessage(md)
// if the message is a recursive structure, we don't want to blow the stack // if the message is a recursive structure, we don't want to blow the stack
if slices.Contains(path, md) { for _, seen := range path {
// already visited this type; avoid infinite recursion if seen == md {
return dm // already visited this type; avoid infinite recursion
return dm
}
} }
path = append(path, dm.GetMessageDescriptor()) path = append(path, dm.GetMessageDescriptor())
@ -544,7 +544,7 @@ func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, client
} else if cacertFile != "" { } else if cacertFile != "" {
// Create a certificate pool from the certificate authority // Create a certificate pool from the certificate authority
certPool := x509.NewCertPool() certPool := x509.NewCertPool()
ca, err := os.ReadFile(cacertFile) ca, err := ioutil.ReadFile(cacertFile)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not read ca certificate: %v", err) return nil, fmt.Errorf("could not read ca certificate: %v", err)
} }
@ -581,7 +581,7 @@ func ServerTransportCredentials(cacertFile, serverCertFile, serverKeyFile string
if cacertFile != "" { if cacertFile != "" {
// Create a certificate pool from the certificate authority // Create a certificate pool from the certificate authority
certPool := x509.NewCertPool() certPool := x509.NewCertPool()
ca, err := os.ReadFile(cacertFile) ca, err := ioutil.ReadFile(cacertFile)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not read ca certificate: %v", err) return nil, fmt.Errorf("could not read ca certificate: %v", err)
} }
@ -608,24 +608,7 @@ func ServerTransportCredentials(cacertFile, serverCertFile, serverKeyFile string
// BlockingDial is a helper method to dial the given address, using optional TLS credentials, // BlockingDial is a helper method to dial the given address, using optional TLS credentials,
// and blocking until the returned connection is ready. If the given credentials are nil, the // and blocking until the returned connection is ready. If the given credentials are nil, the
// connection will be insecure (plain-text). // connection will be insecure (plain-text).
// The network parameter should be left empty in most cases when your address is a RFC 3986
// compliant URI. The resolver from grpc-go will resolve the correct network type.
func BlockingDial(ctx context.Context, network, address string, creds credentials.TransportCredentials, opts ...grpc.DialOption) (*grpc.ClientConn, error) { func BlockingDial(ctx context.Context, network, address string, creds credentials.TransportCredentials, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
if creds == nil {
creds = insecure.NewCredentials()
}
var err error
if strings.HasPrefix(address, "xds:///") {
// The xds:/// prefix is used to signal to the gRPC client to use an xDS server to resolve the
// target. The relevant credentials will be automatically pulled from the GRPC_XDS_BOOTSTRAP or
// GRPC_XDS_BOOTSTRAP_CONFIG env vars.
creds, err = xdsCredentials.NewClientCredentials(xdsCredentials.ClientOptions{FallbackCreds: creds})
if err != nil {
return nil, err
}
}
// grpc.Dial doesn't provide any information on permanent connection errors (like // grpc.Dial doesn't provide any information on permanent connection errors (like
// TLS handshake failures). So in order to provide good error messages, we need a // TLS handshake failures). So in order to provide good error messages, we need a
// custom dialer that can provide that info. That means we manage the TLS handshake. // custom dialer that can provide that info. That means we manage the TLS handshake.
@ -641,37 +624,25 @@ func BlockingDial(ctx context.Context, network, address string, creds credential
// custom credentials and dialer will notify on error via the // custom credentials and dialer will notify on error via the
// writeResult function // writeResult function
creds = &errSignalingCreds{ if creds != nil {
TransportCredentials: creds, creds = &errSignalingCreds{
writeResult: writeResult, TransportCredentials: creds,
writeResult: writeResult,
}
} }
dialer := func(ctx context.Context, address string) (net.Conn, error) {
switch network { // NB: We *could* handle the TLS handshake ourselves, in the custom
case "": // dialer (instead of customizing both the dialer and the credentials).
// no-op, use address as-is // But that requires using insecure.NewCredentials() dial transport
case "tcp": // option (so that the gRPC library doesn't *also* try to do a
if strings.HasPrefix(address, "unix://") { // handshake). And that would mean that the library would send the
return nil, fmt.Errorf("tcp network type cannot use unix address %s", address) // wrong ":scheme" metaheader to servers: it would send "http" instead
// of "https" because it is unaware that TLS is actually in use.
conn, err := (&net.Dialer{}).DialContext(ctx, network, address)
if err != nil {
writeResult(err)
} }
case "unix": return conn, err
if !strings.HasPrefix(address, "unix://") {
// prepend unix:// to the address if it's not already there
// this is to maintain backwards compatibility because the custom dialer is replaced by
// the default dialer in grpc-go.
// https://github.com/fullstorydev/grpcurl/pull/480
address = "unix://" + address
}
default:
// custom dialer for other networks
dialer := func(ctx context.Context, address string) (net.Conn, error) {
conn, err := (&net.Dialer{}).DialContext(ctx, network, address)
if err != nil {
// capture the error so we can provide a better message
writeResult(err)
}
return conn, err
}
opts = append([]grpc.DialOption{grpc.WithContextDialer(dialer)}, opts...)
} }
// Even with grpc.FailOnNonTempDialError, this call will usually timeout in // Even with grpc.FailOnNonTempDialError, this call will usually timeout in
@ -684,8 +655,13 @@ func BlockingDial(ctx context.Context, network, address string, creds credential
opts = append([]grpc.DialOption{grpc.FailOnNonTempDialError(true)}, opts...) opts = append([]grpc.DialOption{grpc.FailOnNonTempDialError(true)}, opts...)
// But we don't want caller to be able to override these two, so we put // But we don't want caller to be able to override these two, so we put
// them *after* the explicitly provided options. // them *after* the explicitly provided options.
opts = append(opts, grpc.WithBlock(), grpc.WithTransportCredentials(creds)) opts = append(opts, grpc.WithBlock(), grpc.WithContextDialer(dialer))
if creds == nil {
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
} else {
opts = append(opts, grpc.WithTransportCredentials(creds))
}
conn, err := grpc.DialContext(ctx, address, opts...) conn, err := grpc.DialContext(ctx, address, opts...)
var res interface{} var res interface{}
if err != nil { if err != nil {

View File

@ -12,9 +12,9 @@ import (
"testing" "testing"
"time" "time"
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API "github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above "github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above "github.com/jhump/protoreflect/desc"
"github.com/jhump/protoreflect/grpcreflect" "github.com/jhump/protoreflect/grpcreflect"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"

View File

@ -7,6 +7,7 @@ import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"os" "os"
"os/signal" "os/signal"
@ -129,7 +130,7 @@ type svr struct {
} }
func (s *svr) load() error { func (s *svr) load() error {
accts, err := os.ReadFile(s.datafile) accts, err := ioutil.ReadFile(s.datafile)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err return err
} }
@ -161,7 +162,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 := os.WriteFile(s.datafile, b, 0666); err != nil { } else if err := ioutil.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)
} }
} }

View File

@ -9,10 +9,10 @@ import (
"sync" "sync"
"sync/atomic" "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/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above "github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above "github.com/jhump/protoreflect/desc"
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above "github.com/jhump/protoreflect/dynamic"
"github.com/jhump/protoreflect/dynamic/grpcdynamic" "github.com/jhump/protoreflect/dynamic/grpcdynamic"
"github.com/jhump/protoreflect/grpcreflect" "github.com/jhump/protoreflect/grpcreflect"
"google.golang.org/grpc" "google.golang.org/grpc"

View File

@ -63,7 +63,7 @@ The last step is to update the Homebrew recipe to use the latest version. First,
```sh ```sh
# download the source archive from GitHub # download the source archive from GitHub
URL=https://github.com/fullstorydev/grpcurl/archive/refs/tags/v2.3.4.tar.gz URL=https://github.com/fullstorydev/grpcurl/archive/v2.3.4.tar.gz
curl -L -o tmp.tgz $URL curl -L -o tmp.tgz $URL
# and compute the SHA # and compute the SHA
SHA="$(sha256sum < tmp.tgz | awk '{ print $1 }')" SHA="$(sha256sum < tmp.tgz | awk '{ print $1 }')"

View File

@ -55,7 +55,7 @@ rm VERSION
# Homebrew release # Homebrew release
URL="https://github.com/fullstorydev/grpcurl/archive/refs/tags/${VERSION}.tar.gz" URL="https://github.com/fullstorydev/grpcurl/archive/${VERSION}.tar.gz"
curl -L -o tmp.tgz "$URL" curl -L -o tmp.tgz "$URL"
SHA="$(sha256sum < tmp.tgz | awk '{ print $1 }')" SHA="$(sha256sum < tmp.tgz | awk '{ print $1 }')"
rm tmp.tgz rm tmp.tgz

View File

@ -1,23 +0,0 @@
# 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!

View File

@ -1,47 +0,0 @@
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

View File

@ -253,12 +253,8 @@ func TestBrokenTLS_ClientNotTrusted(t *testing.T) {
e.Close() e.Close()
t.Fatal("expecting TLS failure setting up server and client") t.Fatal("expecting TLS failure setting up server and client")
} }
// Check for either the old error (Go <=1.24) or the new one (Go 1.25+) if !strings.Contains(err.Error(), "bad certificate") {
// Go 1.24: "bad certificate" t.Fatalf("expecting TLS certificate error, got: %v", err)
// 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)
} }
} }
@ -297,12 +293,8 @@ func TestBrokenTLS_RequireClientCertButNonePresented(t *testing.T) {
e.Close() e.Close()
t.Fatal("expecting TLS failure setting up server and client") t.Fatal("expecting TLS failure setting up server and client")
} }
// Check for either the old error (Go <=1.24) or the new one (Go 1.25+) if !strings.Contains(err.Error(), "bad certificate") {
// Go 1.24: "bad certificate" t.Fatalf("expecting TLS certificate error, got: %v", err)
// 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)
} }
} }