Skip to content

Commit 4fc2eb9

Browse files
committed
http2: revert Transport change from CL 486156
https://go.dev/cl/486156 was problematic, breaking applications like load balancers where cancellations are normal. We tried reverting only part of that change but it turns out all three of the cancellation paths tainting the connection were wrong and could ultimately be triggered by a request cancellation. We need a different solution for golang/go#59690 which we'll now reopen. This isn't a straight revert of CL 486156 because subsequent changes (CL 496335) depended on its cancelRequest closure, so this was reverted by hand. Fixes golang/go#60818 Updates golang/go#59690 (no longer fixed) Change-Id: Ic83b746d7cf89d07655d9efbd04b4d957487cb35 Reviewed-on: https://go-review.googlesource.com/c/net/+/507395 Reviewed-by: Damien Neil <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 63727cc commit 4fc2eb9

File tree

2 files changed

+8
-112
lines changed

2 files changed

+8
-112
lines changed

http2/transport.go

+5-17
Original file line numberDiff line numberDiff line change
@@ -1268,22 +1268,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
12681268

12691269
cancelRequest := func(cs *clientStream, err error) error {
12701270
cs.cc.mu.Lock()
1271-
cs.abortStreamLocked(err)
12721271
bodyClosed := cs.reqBodyClosed
1273-
if cs.ID != 0 {
1274-
// This request may have failed because of a problem with the connection,
1275-
// or for some unrelated reason. (For example, the user might have canceled
1276-
// the request without waiting for a response.) Mark the connection as
1277-
// not reusable, since trying to reuse a dead connection is worse than
1278-
// unnecessarily creating a new one.
1279-
//
1280-
// If cs.ID is 0, then the request was never allocated a stream ID and
1281-
// whatever went wrong was unrelated to the connection. We might have
1282-
// timed out waiting for a stream slot when StrictMaxConcurrentStreams
1283-
// is set, for example, in which case retrying on a different connection
1284-
// will not help.
1285-
cs.cc.doNotReuse = true
1286-
}
12871272
cs.cc.mu.Unlock()
12881273
// Wait for the request body to be closed.
12891274
//
@@ -1318,11 +1303,14 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
13181303
return handleResponseHeaders()
13191304
default:
13201305
waitDone()
1321-
return nil, cancelRequest(cs, cs.abortErr)
1306+
return nil, cs.abortErr
13221307
}
13231308
case <-ctx.Done():
1324-
return nil, cancelRequest(cs, ctx.Err())
1309+
err := ctx.Err()
1310+
cs.abortStream(err)
1311+
return nil, cancelRequest(cs, err)
13251312
case <-cs.reqCancel:
1313+
cs.abortStream(errRequestCanceled)
13261314
return nil, cancelRequest(cs, errRequestCanceled)
13271315
}
13281316
}

http2/transport_test.go

+3-95
Original file line numberDiff line numberDiff line change
@@ -3873,10 +3873,11 @@ func TestTransportRetryAfterRefusedStream(t *testing.T) {
38733873
}
38743874
}
38753875

3876-
server := func(count int, ct *clientTester) {
3876+
server := func(_ int, ct *clientTester) {
38773877
ct.greet()
38783878
var buf bytes.Buffer
38793879
enc := hpack.NewEncoder(&buf)
3880+
var count int
38803881
for {
38813882
f, err := ct.fr.ReadFrame()
38823883
if err != nil {
@@ -3897,6 +3898,7 @@ func TestTransportRetryAfterRefusedStream(t *testing.T) {
38973898
t.Errorf("headers should have END_HEADERS be ended: %v", f)
38983899
return
38993900
}
3901+
count++
39003902
if count == 1 {
39013903
ct.fr.WriteRSTStream(f.StreamID, ErrCodeRefusedStream)
39023904
} else {
@@ -6364,97 +6366,3 @@ func TestTransportSlowClose(t *testing.T) {
63646366
}
63656367
res.Body.Close()
63666368
}
6367-
6368-
type blockReadConn struct {
6369-
net.Conn
6370-
blockc chan struct{}
6371-
}
6372-
6373-
func (c *blockReadConn) Read(b []byte) (n int, err error) {
6374-
<-c.blockc
6375-
return c.Conn.Read(b)
6376-
}
6377-
6378-
func TestTransportReuseAfterError(t *testing.T) {
6379-
serverReqc := make(chan struct{}, 3)
6380-
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
6381-
serverReqc <- struct{}{}
6382-
}, optOnlyServer)
6383-
defer st.Close()
6384-
6385-
var (
6386-
unblockOnce sync.Once
6387-
blockc = make(chan struct{})
6388-
connCountMu sync.Mutex
6389-
connCount int
6390-
)
6391-
tr := &Transport{
6392-
TLSClientConfig: tlsConfigInsecure,
6393-
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
6394-
// The first connection dialed will block on reads until blockc is closed.
6395-
connCountMu.Lock()
6396-
defer connCountMu.Unlock()
6397-
connCount++
6398-
conn, err := tls.Dial(network, addr, cfg)
6399-
if err != nil {
6400-
return nil, err
6401-
}
6402-
if connCount == 1 {
6403-
return &blockReadConn{
6404-
Conn: conn,
6405-
blockc: blockc,
6406-
}, nil
6407-
}
6408-
return conn, nil
6409-
},
6410-
}
6411-
defer tr.CloseIdleConnections()
6412-
defer unblockOnce.Do(func() {
6413-
// Ensure that reads on blockc are unblocked if we return early.
6414-
close(blockc)
6415-
})
6416-
6417-
req, _ := http.NewRequest("GET", st.ts.URL, nil)
6418-
6419-
// Request 1 is made on conn 1.
6420-
// Reading the response will block.
6421-
// Wait until the server receives the request, and continue.
6422-
req1c := make(chan struct{})
6423-
go func() {
6424-
defer close(req1c)
6425-
res1, err := tr.RoundTrip(req.Clone(context.Background()))
6426-
if err != nil {
6427-
t.Errorf("request 1: %v", err)
6428-
} else {
6429-
res1.Body.Close()
6430-
}
6431-
}()
6432-
<-serverReqc
6433-
6434-
// Request 2 is also made on conn 1.
6435-
// Reading the response will block.
6436-
// The request is canceled once the server receives it.
6437-
// Conn 1 should now be flagged as unfit for reuse.
6438-
req2Ctx, cancel := context.WithCancel(context.Background())
6439-
go func() {
6440-
<-serverReqc
6441-
cancel()
6442-
}()
6443-
_, err := tr.RoundTrip(req.Clone(req2Ctx))
6444-
if err == nil {
6445-
t.Errorf("request 2 unexpectedly succeeded (want cancel)")
6446-
}
6447-
6448-
// Request 3 is made on a new conn, and succeeds.
6449-
res3, err := tr.RoundTrip(req.Clone(context.Background()))
6450-
if err != nil {
6451-
t.Fatalf("request 3: %v", err)
6452-
}
6453-
res3.Body.Close()
6454-
6455-
// Unblock conn 1, and verify that request 1 completes.
6456-
unblockOnce.Do(func() {
6457-
close(blockc)
6458-
})
6459-
<-req1c
6460-
}

0 commit comments

Comments
 (0)