diff --git a/go.mod b/go.mod index 82a01bc..b522a0b 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.24.1 require ( github.com/golang/protobuf v1.5.4 github.com/jhump/protoreflect v1.18.0 - google.golang.org/grpc v1.79.3 + google.golang.org/grpc v1.80.0 google.golang.org/protobuf v1.36.11 ) @@ -24,11 +24,11 @@ require ( github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect github.com/stretchr/testify v1.11.1 // indirect - golang.org/x/net v0.48.0 // indirect + golang.org/x/net v0.49.0 // indirect golang.org/x/oauth2 v0.34.0 // indirect golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.39.0 // indirect - golang.org/x/text v0.32.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/text v0.33.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect ) diff --git a/go.sum b/go.sum index 1933e03..f677cd5 100644 --- a/go.sum +++ b/go.sum @@ -56,24 +56,24 @@ go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2W go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= -golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= -google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 h1:vmC/ws+pLzWjj/gzApyoZuSVrDtF1aod4u/+bbj8hgM= +google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:p3MLuOwURrGBRoEyFHBT3GjUwaCQVKeNqqWxlcISGdw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= 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= diff --git a/grpcurl.go b/grpcurl.go index af206aa..777e3c1 100644 --- a/grpcurl.go +++ b/grpcurl.go @@ -26,6 +26,7 @@ import ( "github.com/jhump/protoreflect/desc/protoprint" "github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" xdsCredentials "google.golang.org/grpc/credentials/xds" @@ -674,26 +675,41 @@ func BlockingDial(ctx context.Context, network, address string, creds credential opts = append([]grpc.DialOption{grpc.WithContextDialer(dialer)}, opts...) } - // Even with grpc.FailOnNonTempDialError, this call will usually timeout in - // the face of TLS handshake errors. So we can't rely on grpc.WithBlock() to - // know when we're done. So we run it in a goroutine and then use result - // channel to either get the connection or fail-fast. - go func() { - // We put grpc.FailOnNonTempDialError *before* the explicitly provided - // options so that it could be overridden. - opts = append([]grpc.DialOption{grpc.FailOnNonTempDialError(true)}, opts...) - // But we don't want caller to be able to override these two, so we put - // them *after* the explicitly provided options. - opts = append(opts, grpc.WithBlock(), grpc.WithTransportCredentials(creds)) + // grpc.NewClient does not connect immediately, so we use conn.Connect() + // to trigger eager connection and then poll connectivity state to block + // until ready. The errSignalingCreds wrapper will capture TLS handshake + // errors and write them to the result channel for fail-fast behavior. - conn, err := grpc.DialContext(ctx, address, opts...) - var res interface{} - if err != nil { - res = err - } else { - res = conn + // Normalize address for NewClient which defaults to "dns" resolver. + // Bare host:port addresses need "passthrough:///" to preserve the old + // grpc.Dial behavior. + if !strings.Contains(address, "://") { + address = "passthrough:///" + address + } + + opts = append(opts, grpc.WithTransportCredentials(creds)) + + conn, err := grpc.NewClient(address, opts...) + if err != nil { + return nil, err + } + conn.Connect() + + go func() { + for { + s := conn.GetState() + if s == connectivity.Ready { + writeResult(conn) + return + } + if s == connectivity.Shutdown { + return + } + if !conn.WaitForStateChange(ctx, s) { + // Context expired + return + } } - writeResult(res) }() select { diff --git a/grpcurl_test.go b/grpcurl_test.go index f23bc0f..d4ef43f 100644 --- a/grpcurl_test.go +++ b/grpcurl_test.go @@ -74,10 +74,8 @@ func TestMain(m *testing.M) { defer svrReflect.Stop() // And a corresponding client - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - if ccReflect, err = grpc.DialContext(ctx, fmt.Sprintf("127.0.0.1:%d", portReflect), - grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()); err != nil { + if ccReflect, err = grpc.NewClient(fmt.Sprintf("passthrough:///127.0.0.1:%d", portReflect), + grpc.WithTransportCredentials(insecure.NewCredentials())); err != nil { panic(err) } defer ccReflect.Close() @@ -99,10 +97,8 @@ func TestMain(m *testing.M) { defer svrProtoset.Stop() // And a corresponding client - ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - if ccNoReflect, err = grpc.DialContext(ctx, fmt.Sprintf("127.0.0.1:%d", portProtoset), - grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()); err != nil { + if ccNoReflect, err = grpc.NewClient(fmt.Sprintf("passthrough:///127.0.0.1:%d", portProtoset), + grpc.WithTransportCredentials(insecure.NewCredentials())); err != nil { panic(err) } defer ccNoReflect.Close()