Skip to content

Commit 70d8502

Browse files
fraenkeldmitshur
authored andcommitted
[release-branch.go1.14] http2: close Transport connection on write errors
When a new connection is created, and a write error occurs during the initial exchange, the connection must be closed. There is no guarantee that the caller will close the connection. When a connection with an existing write error is used or being used, it will stay in use until its read loop completes. Requests will continue to use this connection and fail when writing its header. These connections should be closed to force the cleanup in its readLoop. Updates golang/go#39337. For golang/go#42112. Change-Id: I45e1293309e40629531f4cbb69387864f4f71bc2 Reviewed-on: https://go-review.googlesource.com/c/net/+/240337 Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Go Bot <[email protected]> Trust: Brad Fitzpatrick <[email protected]> Trust: Damien Neil <[email protected]> (cherry picked from commit f585440) Reviewed-on: https://go-review.googlesource.com/c/net/+/266157 Trust: Dmitri Shuralyov <[email protected]> Run-TryBot: Dmitri Shuralyov <[email protected]> Reviewed-by: Michael Fraenkel <[email protected]> Reviewed-by: Alexander Rakoczy <[email protected]>
1 parent 9ef5ab9 commit 70d8502

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

http2/transport.go

+10
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
668668
cc.inflow.add(transportDefaultConnFlow + initialWindowSize)
669669
cc.bw.Flush()
670670
if cc.werr != nil {
671+
cc.Close()
671672
return nil, cc.werr
672673
}
673674

@@ -1033,6 +1034,15 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
10331034
bodyWriter := cc.t.getBodyWriterState(cs, body)
10341035
cs.on100 = bodyWriter.on100
10351036

1037+
defer func() {
1038+
cc.wmu.Lock()
1039+
werr := cc.werr
1040+
cc.wmu.Unlock()
1041+
if werr != nil {
1042+
cc.Close()
1043+
}
1044+
}()
1045+
10361046
cc.wmu.Lock()
10371047
endStream := !hasBody && !hasTrailers
10381048
werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs)

http2/transport_test.go

+62
Original file line numberDiff line numberDiff line change
@@ -4521,3 +4521,65 @@ func TestClientConnTooIdle(t *testing.T) {
45214521
}
45224522
}
45234523
}
4524+
4525+
type fakeConnErr struct {
4526+
net.Conn
4527+
writeErr error
4528+
closed bool
4529+
}
4530+
4531+
func (fce *fakeConnErr) Write(b []byte) (n int, err error) {
4532+
return 0, fce.writeErr
4533+
}
4534+
4535+
func (fce *fakeConnErr) Close() error {
4536+
fce.closed = true
4537+
return nil
4538+
}
4539+
4540+
// issue 39337: close the connection on a failed write
4541+
func TestTransportNewClientConnCloseOnWriteError(t *testing.T) {
4542+
tr := &Transport{}
4543+
writeErr := errors.New("write error")
4544+
fakeConn := &fakeConnErr{writeErr: writeErr}
4545+
_, err := tr.NewClientConn(fakeConn)
4546+
if err != writeErr {
4547+
t.Fatalf("expected %v, got %v", writeErr, err)
4548+
}
4549+
if !fakeConn.closed {
4550+
t.Error("expected closed conn")
4551+
}
4552+
}
4553+
4554+
func TestTransportRoundtripCloseOnWriteError(t *testing.T) {
4555+
req, err := http.NewRequest("GET", "https://dummy.tld/", nil)
4556+
if err != nil {
4557+
t.Fatal(err)
4558+
}
4559+
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {}, optOnlyServer)
4560+
defer st.Close()
4561+
4562+
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
4563+
defer tr.CloseIdleConnections()
4564+
cc, err := tr.dialClientConn(st.ts.Listener.Addr().String(), false)
4565+
if err != nil {
4566+
t.Fatal(err)
4567+
}
4568+
4569+
writeErr := errors.New("write error")
4570+
cc.wmu.Lock()
4571+
cc.werr = writeErr
4572+
cc.wmu.Unlock()
4573+
4574+
_, err = cc.RoundTrip(req)
4575+
if err != writeErr {
4576+
t.Fatalf("expected %v, got %v", writeErr, err)
4577+
}
4578+
4579+
cc.mu.Lock()
4580+
closed := cc.closed
4581+
cc.mu.Unlock()
4582+
if !closed {
4583+
t.Fatal("expected closed")
4584+
}
4585+
}

0 commit comments

Comments
 (0)