Skip to content

Commit b02de00

Browse files
authored
client: option to surface connection errors to callers (#3430)
This commit allows blocking clients to receive a more informative error message than "context deadline exceeded", which is especially helpful in tracking down persistent client misconfiguration (such as an invalid TLS certificate, an invalid server that's refusing connections, etc.)
1 parent a9555d0 commit b02de00

3 files changed

Lines changed: 39 additions & 14 deletions

File tree

clientconn.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,14 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
217217
defer func() {
218218
select {
219219
case <-ctx.Done():
220-
conn, err = nil, ctx.Err()
220+
switch {
221+
case ctx.Err() == err:
222+
conn = nil
223+
case err == nil || !cc.dopts.returnLastError:
224+
conn, err = nil, ctx.Err()
225+
default:
226+
conn, err = nil, fmt.Errorf("%v: %v", ctx.Err(), err)
227+
}
221228
default:
222229
}
223230
}()
@@ -322,6 +329,9 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
322329
}
323330
if !cc.WaitForStateChange(ctx, s) {
324331
// ctx got timeout or canceled.
332+
if err = cc.blockingpicker.connectionError(); err != nil && cc.dopts.returnLastError {
333+
return nil, err
334+
}
325335
return nil, ctx.Err()
326336
}
327337
}

clientconn_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,16 +221,17 @@ func (s) TestDialWaitsForServerSettingsAndFails(t *testing.T) {
221221
client, err := DialContext(ctx,
222222
lis.Addr().String(),
223223
WithInsecure(),
224-
WithBlock(),
224+
WithReturnConnectionError(),
225225
withBackoff(noBackoff{}),
226226
withMinConnectDeadline(func() time.Duration { return time.Second / 4 }))
227227
lis.Close()
228228
if err == nil {
229229
client.Close()
230230
t.Fatalf("Unexpected success (err=nil) while dialing")
231231
}
232-
if err != context.DeadlineExceeded {
233-
t.Fatalf("DialContext(_) = %v; want context.DeadlineExceeded", err)
232+
expectedMsg := "server handshake"
233+
if !strings.Contains(err.Error(), context.DeadlineExceeded.Error()) || !strings.Contains(err.Error(), expectedMsg) {
234+
t.Fatalf("DialContext(_) = %v; want a message that includes both %q and %q", err, context.DeadlineExceeded.Error(), expectedMsg)
234235
}
235236
<-done
236237
if numConns < 2 {

dialoptions.go

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,17 @@ type dialOptions struct {
4646
chainUnaryInts []UnaryClientInterceptor
4747
chainStreamInts []StreamClientInterceptor
4848

49-
cp Compressor
50-
dc Decompressor
51-
bs internalbackoff.Strategy
52-
block bool
53-
insecure bool
54-
timeout time.Duration
55-
scChan <-chan ServiceConfig
56-
authority string
57-
copts transport.ConnectOptions
58-
callOptions []CallOption
49+
cp Compressor
50+
dc Decompressor
51+
bs internalbackoff.Strategy
52+
block bool
53+
returnLastError bool
54+
insecure bool
55+
timeout time.Duration
56+
scChan <-chan ServiceConfig
57+
authority string
58+
copts transport.ConnectOptions
59+
callOptions []CallOption
5960
// This is used by v1 balancer dial option WithBalancer to support v1
6061
// balancer, and also by WithBalancerName dial option.
6162
balancerBuilder balancer.Builder
@@ -299,6 +300,19 @@ func WithBlock() DialOption {
299300
})
300301
}
301302

303+
// WithReturnConnectionError returns a DialOption which makes the client connection
304+
// return a string containing both the last connection error that occurred and
305+
// the context.DeadlineExceeded error.
306+
// Implies WithBlock()
307+
//
308+
// This API is EXPERIMENTAL.
309+
func WithReturnConnectionError() DialOption {
310+
return newFuncDialOption(func(o *dialOptions) {
311+
o.block = true
312+
o.returnLastError = true
313+
})
314+
}
315+
302316
// WithInsecure returns a DialOption which disables transport security for this
303317
// ClientConn. Note that transport security is required unless WithInsecure is
304318
// set.

0 commit comments

Comments
 (0)