"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
internalbackoff "google.golang.org/grpc/internal/backoff"
- "google.golang.org/grpc/internal/grpcsync"
- "google.golang.org/grpc/internal/grpctest"
- "google.golang.org/grpc/internal/transport"
- "google.golang.org/grpc/keepalive"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/manual"
"google.golang.org/grpc/serviceconfig"
return scpr
}
-func (s) TestDialWithTimeout(t *testing.T) {
- lis, err := net.Listen("tcp", "localhost:0")
- if err != nil {
- t.Fatalf("Error while listening. Err: %v", err)
- }
- defer lis.Close()
- lisAddr := resolver.Address{Addr: lis.Addr().String()}
- lisDone := make(chan struct{})
- dialDone := make(chan struct{})
- // 1st listener accepts the connection and then does nothing
- go func() {
- defer close(lisDone)
- conn, err := lis.Accept()
- if err != nil {
- t.Errorf("Error while accepting. Err: %v", err)
- return
- }
- framer := http2.NewFramer(conn, conn)
- if err := framer.WriteSettings(http2.Setting{}); err != nil {
- t.Errorf("Error while writing settings. Err: %v", err)
- return
- }
- <-dialDone // Close conn only after dial returns.
- }()
-
- r := manual.NewBuilderWithScheme("whatever")
- r.InitialState(resolver.State{Addresses: []resolver.Address{lisAddr}})
- client, err := Dial(r.Scheme()+":///test.server", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r), WithTimeout(5*time.Second))
- close(dialDone)
- if err != nil {
- t.Fatalf("Dial failed. Err: %v", err)
- }
- defer client.Close()
- timeout := time.After(1 * time.Second)
- select {
- case <-timeout:
- t.Fatal("timed out waiting for server to finish")
- case <-lisDone:
- }
-}
-
func (s) TestNewClientWithMultipleBackendsNotSendingServerPreface(t *testing.T) {
lis1, err := net.Listen("tcp", "localhost:0")
if err != nil {
}
}
-func (s) TestDialWaitsForServerSettings(t *testing.T) {
- lis, err := net.Listen("tcp", "localhost:0")
- if err != nil {
- t.Fatalf("Error while listening. Err: %v", err)
- }
- defer lis.Close()
- done := make(chan struct{})
- sent := make(chan struct{})
- dialDone := make(chan struct{})
- go func() { // Launch the server.
- defer func() {
- close(done)
- }()
- conn, err := lis.Accept()
- if err != nil {
- t.Errorf("Error while accepting. Err: %v", err)
- return
- }
- defer conn.Close()
- // Sleep for a little bit to make sure that Dial on client
- // side blocks until settings are received.
- time.Sleep(100 * time.Millisecond)
- framer := http2.NewFramer(conn, conn)
- close(sent)
- if err := framer.WriteSettings(http2.Setting{}); err != nil {
- t.Errorf("Error while writing settings. Err: %v", err)
- return
- }
- <-dialDone // Close conn only after dial returns.
- }()
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
- client, err := DialContext(ctx, lis.Addr().String(), WithTransportCredentials(insecure.NewCredentials()), WithBlock())
- close(dialDone)
- if err != nil {
- t.Fatalf("Error while dialing. Err: %v", err)
- }
- defer client.Close()
- select {
- case <-sent:
- default:
- t.Fatalf("Dial returned before server settings were sent")
- }
- <-done
-}
-
-func (s) TestDialWaitsForServerSettingsAndFails(t *testing.T) {
- lis, err := net.Listen("tcp", "localhost:0")
- if err != nil {
- t.Fatalf("Error while listening. Err: %v", err)
- }
- done := make(chan struct{})
- numConns := 0
- go func() { // Launch the server.
- defer func() {
- close(done)
- }()
- for {
- conn, err := lis.Accept()
- if err != nil {
- break
- }
- numConns++
- defer conn.Close()
- }
- }()
- ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
- defer cancel()
- client, err := DialContext(ctx,
- lis.Addr().String(),
- WithTransportCredentials(insecure.NewCredentials()),
- WithReturnConnectionError(),
- WithConnectParams(ConnectParams{
- Backoff: backoff.Config{},
- MinConnectTimeout: 250 * time.Millisecond,
- }))
- lis.Close()
- if err == nil {
- client.Close()
- t.Fatalf("Unexpected success (err=nil) while dialing")
- }
- expectedMsg := "server preface"
- if !strings.Contains(err.Error(), context.DeadlineExceeded.Error()) || !strings.Contains(err.Error(), expectedMsg) {
- t.Fatalf("DialContext(_) = %v; want a message that includes both %q and %q", err, context.DeadlineExceeded.Error(), expectedMsg)
- }
- <-done
- if numConns < 2 {
- t.Fatalf("dial attempts: %v; want > 1", numConns)
- }
-}
-
// 1. Client connects to a server that doesn't send preface.
// 2. After minConnectTimeout(500 ms here), client disconnects and retries.
// 3. The new server sends its preface.
<-done
}
-func (s) TestWithTimeout(t *testing.T) {
- conn, err := Dial("passthrough:///Non-Existent.Server:80",
- WithTimeout(time.Millisecond),
- WithBlock(),
- WithTransportCredentials(insecure.NewCredentials()))
- if err == nil {
- conn.Close()
- }
- if err != context.DeadlineExceeded {
- t.Fatalf("Dial(_, _) = %v, %v, want %v", conn, err, context.DeadlineExceeded)
- }
-}
-
-func (s) TestWithTransportCredentialsTLS(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
- defer cancel()
- creds, err := credentials.NewClientTLSFromFile(testdata.Path("x509/server_ca_cert.pem"), "x.test.example.com")
- if err != nil {
- t.Fatalf("Failed to create credentials %v", err)
- }
- conn, err := DialContext(ctx, "passthrough:///Non-Existent.Server:80", WithTransportCredentials(creds), WithBlock())
- if err == nil {
- conn.Close()
- }
- if err != context.DeadlineExceeded {
- t.Fatalf("Dial(_, _) = %v, %v, want %v", conn, err, context.DeadlineExceeded)
- }
-}
-
// When creating a transport configured with n addresses, only calculate the
// backoff once per "round" of attempts instead of once per address (n times
// per "round" of attempts) for old pickfirst and once per address for new pickfirst.
}
}
-func (s) TestDialContextCancel(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- cancel()
- if _, err := DialContext(ctx, "Non-Existent.Server:80", WithBlock(), WithTransportCredentials(insecure.NewCredentials())); err != context.Canceled {
- t.Fatalf("DialContext(%v, _) = _, %v, want _, %v", ctx, err, context.Canceled)
- }
-}
-
-type failFastError struct{}
-
-func (failFastError) Error() string { return "failfast" }
-func (failFastError) Temporary() bool { return false }
-
-func (s) TestDialContextFailFast(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
- failErr := failFastError{}
- dialer := func(string, time.Duration) (net.Conn, error) {
- return nil, failErr
- }
-
- _, err := DialContext(ctx, "Non-Existent.Server:80", WithBlock(), WithTransportCredentials(insecure.NewCredentials()), WithDialer(dialer), FailOnNonTempDialError(true))
- if terr, ok := err.(transport.ConnectionError); !ok || terr.Origin() != failErr {
- t.Fatalf("DialContext() = _, %v, want _, %v", err, failErr)
- }
-}
-
// securePerRPCCredentials always requires transport security.
type securePerRPCCredentials struct {
credentials.PerRPCCredentials
time.Sleep(time.Second) // Sleep to make sure the service config is handled by ClientConn.
}
-func (s) TestClientUpdatesParamsAfterGoAway(t *testing.T) {
- grpctest.ExpectError("Client received GoAway with error code ENHANCE_YOUR_CALM and debug data equal to ASCII \"too_many_pings\"")
-
- lis, err := net.Listen("tcp", "localhost:0")
- if err != nil {
- t.Fatalf("Failed to listen. Err: %v", err)
- }
- defer lis.Close()
- connected := grpcsync.NewEvent()
- defer connected.Fire()
- go func() {
- conn, err := lis.Accept()
- if err != nil {
- t.Errorf("error accepting connection: %v", err)
- return
- }
- defer conn.Close()
- f := http2.NewFramer(conn, conn)
- // Start a goroutine to read from the conn to prevent the client from
- // blocking after it writes its preface.
- go func() {
- for {
- if _, err := f.ReadFrame(); err != nil {
- return
- }
- }
- }()
- if err := f.WriteSettings(http2.Setting{}); err != nil {
- t.Errorf("error writing settings: %v", err)
- return
- }
- <-connected.Done()
- if err := f.WriteGoAway(0, http2.ErrCodeEnhanceYourCalm, []byte("too_many_pings")); err != nil {
- t.Errorf("error writing GOAWAY: %v", err)
- return
- }
- }()
- addr := lis.Addr().String()
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
- cc, err := DialContext(ctx, addr, WithBlock(), WithTransportCredentials(insecure.NewCredentials()), WithKeepaliveParams(keepalive.ClientParameters{
- Time: 10 * time.Second,
- Timeout: 100 * time.Millisecond,
- PermitWithoutStream: true,
- }))
- if err != nil {
- t.Fatalf("DialContext(%s) failed: %v, want: nil", addr, err)
- }
- defer cc.Close()
- connected.Fire()
- for {
- time.Sleep(10 * time.Millisecond)
- cc.mu.RLock()
- v := cc.keepaliveParams.Time
- cc.mu.RUnlock()
- if v == 20*time.Second {
- // Success
- return
- }
- if ctx.Err() != nil {
- // Timeout
- t.Fatalf("cc.dopts.copts.Keepalive.Time = %v , want 20s", v)
- }
- }
-}
-
func (s) TestDisableServiceConfigOption(t *testing.T) {
r := manual.NewBuilderWithScheme("whatever")
addr := r.Scheme() + ":///non.existent"
--- /dev/null
+/*
+ *
+ * Copyright 2025 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package grpc
+
+import (
+ "context"
+ "net"
+ "strings"
+ "testing"
+ "time"
+
+ "golang.org/x/net/http2"
+ "google.golang.org/grpc/attributes"
+ "google.golang.org/grpc/backoff"
+ "google.golang.org/grpc/credentials"
+ "google.golang.org/grpc/credentials/insecure"
+ "google.golang.org/grpc/internal/grpcsync"
+ "google.golang.org/grpc/internal/grpctest"
+ "google.golang.org/grpc/internal/transport"
+ "google.golang.org/grpc/keepalive"
+ "google.golang.org/grpc/resolver"
+ "google.golang.org/grpc/resolver/manual"
+ "google.golang.org/grpc/testdata"
+)
+
+func (s) TestDialWithTimeout(t *testing.T) {
+ lis, err := net.Listen("tcp", "localhost:0")
+ if err != nil {
+ t.Fatalf("Error while listening. Err: %v", err)
+ }
+ defer lis.Close()
+ lisAddr := resolver.Address{Addr: lis.Addr().String()}
+ lisDone := make(chan struct{})
+ dialDone := make(chan struct{})
+ // 1st listener accepts the connection and then does nothing
+ go func() {
+ defer close(lisDone)
+ conn, err := lis.Accept()
+ if err != nil {
+ t.Errorf("Error while accepting. Err: %v", err)
+ return
+ }
+ framer := http2.NewFramer(conn, conn)
+ if err := framer.WriteSettings(http2.Setting{}); err != nil {
+ t.Errorf("Error while writing settings. Err: %v", err)
+ return
+ }
+ <-dialDone // Close conn only after dial returns.
+ }()
+
+ r := manual.NewBuilderWithScheme("whatever")
+ r.InitialState(resolver.State{Addresses: []resolver.Address{lisAddr}})
+ client, err := Dial(r.Scheme()+":///test.server", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r), WithTimeout(5*time.Second))
+ close(dialDone)
+ if err != nil {
+ t.Fatalf("Dial failed. Err: %v", err)
+ }
+ defer client.Close()
+ timeout := time.After(1 * time.Second)
+ select {
+ case <-timeout:
+ t.Fatal("timed out waiting for server to finish")
+ case <-lisDone:
+ }
+}
+
+func (s) TestDialWaitsForServerSettings(t *testing.T) {
+ lis, err := net.Listen("tcp", "localhost:0")
+ if err != nil {
+ t.Fatalf("Error while listening. Err: %v", err)
+ }
+ defer lis.Close()
+ done := make(chan struct{})
+ sent := make(chan struct{})
+ dialDone := make(chan struct{})
+ go func() { // Launch the server.
+ defer func() {
+ close(done)
+ }()
+ conn, err := lis.Accept()
+ if err != nil {
+ t.Errorf("Error while accepting. Err: %v", err)
+ return
+ }
+ defer conn.Close()
+ // Sleep for a little bit to make sure that Dial on client
+ // side blocks until settings are received.
+ time.Sleep(100 * time.Millisecond)
+ framer := http2.NewFramer(conn, conn)
+ close(sent)
+ if err := framer.WriteSettings(http2.Setting{}); err != nil {
+ t.Errorf("Error while writing settings. Err: %v", err)
+ return
+ }
+ <-dialDone // Close conn only after dial returns.
+ }()
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+ client, err := DialContext(ctx, lis.Addr().String(), WithTransportCredentials(insecure.NewCredentials()), WithBlock())
+ close(dialDone)
+ if err != nil {
+ t.Fatalf("Error while dialing. Err: %v", err)
+ }
+ defer client.Close()
+ select {
+ case <-sent:
+ default:
+ t.Fatalf("Dial returned before server settings were sent")
+ }
+ <-done
+}
+
+func (s) TestDialWaitsForServerSettingsAndFails(t *testing.T) {
+ lis, err := net.Listen("tcp", "localhost:0")
+ if err != nil {
+ t.Fatalf("Error while listening. Err: %v", err)
+ }
+ done := make(chan struct{})
+ numConns := 0
+ go func() { // Launch the server.
+ defer func() {
+ close(done)
+ }()
+ for {
+ conn, err := lis.Accept()
+ if err != nil {
+ break
+ }
+ numConns++
+ defer conn.Close()
+ }
+ }()
+ ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
+ defer cancel()
+ client, err := DialContext(ctx,
+ lis.Addr().String(),
+ WithTransportCredentials(insecure.NewCredentials()),
+ WithReturnConnectionError(),
+ WithConnectParams(ConnectParams{
+ Backoff: backoff.Config{},
+ MinConnectTimeout: 250 * time.Millisecond,
+ }))
+ lis.Close()
+ if err == nil {
+ client.Close()
+ t.Fatalf("Unexpected success (err=nil) while dialing")
+ }
+ expectedMsg := "server preface"
+ if !strings.Contains(err.Error(), context.DeadlineExceeded.Error()) || !strings.Contains(err.Error(), expectedMsg) {
+ t.Fatalf("DialContext(_) = %v; want a message that includes both %q and %q", err, context.DeadlineExceeded.Error(), expectedMsg)
+ }
+ <-done
+ if numConns < 2 {
+ t.Fatalf("dial attempts: %v; want > 1", numConns)
+ }
+}
+
+func (s) TestWithTimeout(t *testing.T) {
+ conn, err := Dial("passthrough:///Non-Existent.Server:80",
+ WithTimeout(time.Millisecond),
+ WithBlock(),
+ WithTransportCredentials(insecure.NewCredentials()))
+ if err == nil {
+ conn.Close()
+ }
+ if err != context.DeadlineExceeded {
+ t.Fatalf("Dial(_, _) = %v, %v, want %v", conn, err, context.DeadlineExceeded)
+ }
+}
+
+func (s) TestWithTransportCredentialsTLS(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
+ defer cancel()
+ creds, err := credentials.NewClientTLSFromFile(testdata.Path("x509/server_ca_cert.pem"), "x.test.example.com")
+ if err != nil {
+ t.Fatalf("Failed to create credentials %v", err)
+ }
+ conn, err := DialContext(ctx, "passthrough:///Non-Existent.Server:80", WithTransportCredentials(creds), WithBlock())
+ if err == nil {
+ conn.Close()
+ }
+ if err != context.DeadlineExceeded {
+ t.Fatalf("Dial(_, _) = %v, %v, want %v", conn, err, context.DeadlineExceeded)
+ }
+}
+
+func (s) TestDialContextCancel(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ cancel()
+ if _, err := DialContext(ctx, "Non-Existent.Server:80", WithBlock(), WithTransportCredentials(insecure.NewCredentials())); err != context.Canceled {
+ t.Fatalf("DialContext(%v, _) = _, %v, want _, %v", ctx, err, context.Canceled)
+ }
+}
+
+type failFastError struct{}
+
+func (failFastError) Error() string { return "failfast" }
+func (failFastError) Temporary() bool { return false }
+
+func (s) TestDialContextFailFast(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+ failErr := failFastError{}
+ dialer := func(string, time.Duration) (net.Conn, error) {
+ return nil, failErr
+ }
+
+ _, err := DialContext(ctx, "Non-Existent.Server:80", WithBlock(), WithTransportCredentials(insecure.NewCredentials()), WithDialer(dialer), FailOnNonTempDialError(true))
+ if terr, ok := err.(transport.ConnectionError); !ok || terr.Origin() != failErr {
+ t.Fatalf("DialContext() = _, %v, want _, %v", err, failErr)
+ }
+}
+
+func (s) TestClientUpdatesParamsAfterGoAway(t *testing.T) {
+ grpctest.ExpectError("Client received GoAway with error code ENHANCE_YOUR_CALM and debug data equal to ASCII \"too_many_pings\"")
+
+ lis, err := net.Listen("tcp", "localhost:0")
+ if err != nil {
+ t.Fatalf("Failed to listen. Err: %v", err)
+ }
+ defer lis.Close()
+ connected := grpcsync.NewEvent()
+ defer connected.Fire()
+ go func() {
+ conn, err := lis.Accept()
+ if err != nil {
+ t.Errorf("error accepting connection: %v", err)
+ return
+ }
+ defer conn.Close()
+ f := http2.NewFramer(conn, conn)
+ // Start a goroutine to read from the conn to prevent the client from
+ // blocking after it writes its preface.
+ go func() {
+ for {
+ if _, err := f.ReadFrame(); err != nil {
+ return
+ }
+ }
+ }()
+ if err := f.WriteSettings(http2.Setting{}); err != nil {
+ t.Errorf("error writing settings: %v", err)
+ return
+ }
+ <-connected.Done()
+ if err := f.WriteGoAway(0, http2.ErrCodeEnhanceYourCalm, []byte("too_many_pings")); err != nil {
+ t.Errorf("error writing GOAWAY: %v", err)
+ return
+ }
+ }()
+ addr := lis.Addr().String()
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ cc, err := DialContext(ctx, addr, WithBlock(), WithTransportCredentials(insecure.NewCredentials()), WithKeepaliveParams(keepalive.ClientParameters{
+ Time: 10 * time.Second,
+ Timeout: 100 * time.Millisecond,
+ PermitWithoutStream: true,
+ }))
+ if err != nil {
+ t.Fatalf("DialContext(%s) failed: %v, want: nil", addr, err)
+ }
+ defer cc.Close()
+ connected.Fire()
+ for {
+ time.Sleep(10 * time.Millisecond)
+ cc.mu.RLock()
+ v := cc.keepaliveParams.Time
+ cc.mu.RUnlock()
+ if v == 20*time.Second {
+ // Success
+ return
+ }
+ if ctx.Err() != nil {
+ // Timeout
+ t.Fatalf("cc.dopts.copts.Keepalive.Time = %v , want 20s", v)
+ }
+ }
+}
+
+// Test ensures that there is no panic if the attributes within
+// resolver.State.Addresses contains a typed-nil value.
+func (s) TestResolverAddressesWithTypedNilAttribute(t *testing.T) {
+ r := manual.NewBuilderWithScheme(t.Name())
+ resolver.Register(r)
+
+ addrAttr := attributes.New("typed_nil", (*stringerVal)(nil))
+ r.InitialState(resolver.State{Addresses: []resolver.Address{{Addr: "addr1", Attributes: addrAttr}}})
+
+ cc, err := Dial(r.Scheme()+":///", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r))
+ if err != nil {
+ t.Fatalf("Unexpected error dialing: %v", err)
+ }
+ defer cc.Close()
+}
+
+type stringerVal struct{ s string }
+
+func (s stringerVal) String() string { return s.s }
t.Fatalf("timed out waiting for endpoints")
}
}
-
-// Test ensures that there is no panic if the attributes within
-// resolver.State.Addresses contains a typed-nil value.
-func (s) TestResolverAddressesWithTypedNilAttribute(t *testing.T) {
- r := manual.NewBuilderWithScheme(t.Name())
- resolver.Register(r)
-
- addrAttr := attributes.New("typed_nil", (*stringerVal)(nil))
- r.InitialState(resolver.State{Addresses: []resolver.Address{{Addr: "addr1", Attributes: addrAttr}}})
-
- cc, err := Dial(r.Scheme()+":///", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r))
- if err != nil {
- t.Fatalf("Unexpected error dialing: %v", err)
- }
- defer cc.Close()
-}
-
-type stringerVal struct{ s string }
-
-func (s stringerVal) String() string { return s.s }