diff --git a/x/mongo/driver/topology/connection.go b/x/mongo/driver/topology/connection.go index 24ad6a3a51..e5f98cfe5c 100644 --- a/x/mongo/driver/topology/connection.go +++ b/x/mongo/driver/topology/connection.go @@ -212,7 +212,7 @@ func (c *connection) connect(ctx context.Context) (err error) { // Assign the result of DialContext to a temporary net.Conn to ensure that c.nc is not set in an error case. tempNc, err := c.config.dialer.DialContext(ctx, c.addr.Network(), c.addr.String()) if err != nil { - return ConnectionError{Wrapped: err, init: true} + return ConnectionError{Wrapped: err, init: true, message: fmt.Sprintf("failed to connect to %s", c.addr)} } c.nc = tempNc @@ -229,7 +229,7 @@ func (c *connection) connect(ctx context.Context) (err error) { tlsNc, err := configureTLS(ctx, c.config.tlsConnectionSource, c.nc, c.addr, tlsConfig, ocspOpts) if err != nil { - return ConnectionError{Wrapped: err, init: true} + return ConnectionError{Wrapped: err, init: true, message: fmt.Sprintf("failed to configure TLS for %s", c.addr)} } c.nc = tlsNc } @@ -413,9 +413,6 @@ func (c *connection) readWireMessage(ctx context.Context) ([]byte, error) { c.close() } message := errMsg - if errors.Is(err, io.EOF) { - message = "socket was unexpectedly closed" - } return nil, ConnectionError{ ConnectionID: c.id, Wrapped: transformNetworkError(ctx, err, contextDeadlineUsed), diff --git a/x/mongo/driver/topology/connection_test.go b/x/mongo/driver/topology/connection_test.go index f4d0cd9d7d..13c9a7f6e7 100644 --- a/x/mongo/driver/topology/connection_test.go +++ b/x/mongo/driver/topology/connection_test.go @@ -63,8 +63,8 @@ func TestConnection(t *testing.T) { t.Run("connect", func(t *testing.T) { t.Run("dialer error", func(t *testing.T) { err := errors.New("dialer error") - var want error = ConnectionError{Wrapped: err, init: true} - conn := newConnection(address.Address(""), WithDialer(func(Dialer) Dialer { + var want error = ConnectionError{Wrapped: err, init: true, message: "failed to connect to testaddr:27017"} + conn := newConnection(address.Address("testaddr"), WithDialer(func(Dialer) Dialer { return DialerFunc(func(context.Context, string, string) (net.Conn, error) { return nil, err }) })) got := conn.connect(context.Background()) diff --git a/x/mongo/driver/topology/errors.go b/x/mongo/driver/topology/errors.go index 79f11f7f79..6740d54a95 100644 --- a/x/mongo/driver/topology/errors.go +++ b/x/mongo/driver/topology/errors.go @@ -10,6 +10,10 @@ import ( "context" "errors" "fmt" + "io" + "net" + "os" + "strings" "time" "go.mongodb.org/mongo-driver/v2/x/mongo/driver/description" @@ -28,21 +32,28 @@ type ConnectionError struct { // Error implements the error interface. func (e ConnectionError) Error() string { - message := e.message + var messages []string if e.init { - fullMsg := "error occurred during connection handshake" - if message != "" { - fullMsg = fmt.Sprintf("%s: %s", fullMsg, message) - } - message = fullMsg + messages = append(messages, "error occurred during connection handshake") } - if e.Wrapped != nil && message != "" { - return fmt.Sprintf("connection(%s) %s: %s", e.ConnectionID, message, e.Wrapped.Error()) + if e.message != "" { + messages = append(messages, e.message) } if e.Wrapped != nil { - return fmt.Sprintf("connection(%s) %s", e.ConnectionID, e.Wrapped.Error()) + if errors.Is(e.Wrapped, io.EOF) { + messages = append(messages, "socket was unexpectedly closed") + } + if errors.Is(e.Wrapped, os.ErrDeadlineExceeded) { + messages = append(messages, "client timed out waiting for server response") + } else if err, ok := e.Wrapped.(net.Error); ok && err.Timeout() { + messages = append(messages, "client timed out waiting for server response") + } + messages = append(messages, e.Wrapped.Error()) + } + if len(messages) > 0 { + return fmt.Sprintf("connection(%s) %s", e.ConnectionID, strings.Join(messages, ": ")) } - return fmt.Sprintf("connection(%s) %s", e.ConnectionID, message) + return fmt.Sprintf("connection(%s)", e.ConnectionID) } // Unwrap returns the underlying error. diff --git a/x/mongo/driver/topology/pool_test.go b/x/mongo/driver/topology/pool_test.go index 3d270de2e0..6cc7d54287 100644 --- a/x/mongo/driver/topology/pool_test.go +++ b/x/mongo/driver/topology/pool_test.go @@ -471,6 +471,7 @@ func TestPool_checkOut(t *testing.T) { dialErr := errors.New("create new connection error") p := newPool(poolConfig{ + Address: "testaddr", ConnectTimeout: defaultConnectionTimeout, }, WithDialer(func(Dialer) Dialer { return DialerFunc(func(context.Context, string, string) (net.Conn, error) { @@ -481,7 +482,7 @@ func TestPool_checkOut(t *testing.T) { require.NoError(t, err) _, err = p.checkOut(context.Background()) - var want error = ConnectionError{Wrapped: dialErr, init: true} + var want error = ConnectionError{Wrapped: dialErr, init: true, message: "failed to connect to testaddr:27017"} assert.Equalf(t, want, err, "should return error from calling checkOut()") // If a connection initialization error occurs during checkOut, removing and closing the // failed connection both happen asynchronously with the checkOut. Wait for up to 2s for diff --git a/x/mongo/driver/topology/server_test.go b/x/mongo/driver/topology/server_test.go index 5ab5692840..818a7818f6 100644 --- a/x/mongo/driver/topology/server_test.go +++ b/x/mongo/driver/topology/server_test.go @@ -376,7 +376,7 @@ func TestServer(t *testing.T) { } authErr := ConnectionError{Wrapped: &auth.Error{}, init: true} - netErr := ConnectionError{Wrapped: &net.AddrError{}, init: true} + netErr := ConnectionError{Wrapped: &net.AddrError{}, init: true, message: "failed to connect to localhost:27017"} for _, tt := range serverTestTable { t.Run(tt.name, func(t *testing.T) { var returnConnectionError bool