Skip to content

Commit 2780563

Browse files
transport: Invoke net.Conn.SetWriteDeadline in http2_client.Close (#8534)
Fixes: #8425 This PR adds a call to `net.Conn.SetWriteDeadline`, as discussed in #8425 (comment). Additionally, it updates the previous call to `SetReadDeadline` to log any non-nil error value (this doesn't affect behavior but proved helpful in some earlier debugging). RELEASE NOTES: * client: Set a read deadline when closing a transport to prevent it from blocking indefinitely on a broken connection.
1 parent b4000c8 commit 2780563

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

internal/transport/http2_client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,9 @@ func (t *http2Client) closeStream(s *ClientStream, err error, rst bool, rstCode
10021002
// accessed anymore.
10031003
func (t *http2Client) Close(err error) {
10041004
t.conn.SetWriteDeadline(time.Now().Add(time.Second * 10))
1005+
// For background on the deadline value chosen here, see
1006+
// https://github.com/grpc/grpc-go/issues/8425#issuecomment-3057938248 .
1007+
t.conn.SetReadDeadline(time.Now().Add(time.Second))
10051008
t.mu.Lock()
10061009
// Make sure we only close once.
10071010
if t.state == closing {

internal/transport/transport_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3008,6 +3008,64 @@ func (s) TestClientCloseReturnsEarlyWhenGoAwayWriteHangs(t *testing.T) {
30083008
ct.Close(errors.New("manually closed by client"))
30093009
}
30103010

3011+
// deadlineTestConn is a net.Conn wrapper used to assert that deadlines are set
3012+
// during http2Client.Close().
3013+
type deadlineTestConn struct {
3014+
net.Conn
3015+
// We use atomic.Bool here since there may be more than one call to
3016+
// http2Client.Close -- which sets these deadlines -- and not all of them
3017+
// from the same goroutine as our test. In fact we only care about the first
3018+
// such invocation, which *does* come from the main goroutine of our test,
3019+
// but the race detector can't know that and complains (understandably)
3020+
// about writes from those successive calls when these variables are not
3021+
// atomic.Bool.
3022+
//
3023+
// For more detailed background, see
3024+
// https://github.com/grpc/grpc-go/pull/8534#discussion_r2297717445 .
3025+
observedReadDeadline atomic.Bool
3026+
observedWriteDeadline atomic.Bool
3027+
}
3028+
3029+
func (c *deadlineTestConn) SetReadDeadline(t time.Time) error {
3030+
c.observedReadDeadline.Store(true)
3031+
return c.Conn.SetReadDeadline(t)
3032+
}
3033+
3034+
func (c *deadlineTestConn) SetWriteDeadline(t time.Time) error {
3035+
c.observedWriteDeadline.Store(true)
3036+
return c.Conn.SetWriteDeadline(t)
3037+
}
3038+
3039+
// Tests that connection read and write deadlines are set as expected during
3040+
// Close().
3041+
func (s) TestCloseSetsConnectionDeadlines(t *testing.T) {
3042+
dialer := func(_ context.Context, addr string) (net.Conn, error) {
3043+
conn, err := net.Dial("tcp", addr)
3044+
if err != nil {
3045+
return nil, err
3046+
}
3047+
return &deadlineTestConn{Conn: conn}, nil
3048+
}
3049+
co := ConnectOptions{
3050+
Dialer: dialer,
3051+
}
3052+
server, client, cancel := setUpWithOptions(t, 0, &ServerConfig{}, normal, co)
3053+
defer cancel()
3054+
defer server.stop()
3055+
dConn := client.conn.(*deadlineTestConn)
3056+
// Set both to false before invoking Close() in case some other code set a
3057+
// deadline above.
3058+
dConn.observedReadDeadline.Store(false)
3059+
dConn.observedWriteDeadline.Store(false)
3060+
client.Close(fmt.Errorf("closed manually by test"))
3061+
if !dConn.observedReadDeadline.Load() {
3062+
t.Errorf("Connection read deadline was never set")
3063+
}
3064+
if !dConn.observedWriteDeadline.Load() {
3065+
t.Errorf("Connection write deadline was never set")
3066+
}
3067+
}
3068+
30113069
// TestReadHeaderMultipleBuffers tests the stream when the gRPC headers are
30123070
// split across multiple buffers. It verifies that the reporting of the
30133071
// number of bytes read for flow control is correct.

0 commit comments

Comments
 (0)