Skip to content

Commit ea1437a

Browse files
committed
net/http: fix handling of HTTP/2 upgrade failures
If an error occurs during the HTTP/2 upgrade phase, originally this resulted in a pconn with pconn.alt set to an http2erringRoundTripper, which always fails. This is not wanted - we want to retry in this case. CL 202078 added a check for the http2erringRoundTripper to treat it as a failed pconn, but the handling of the failure was wrong in the case where the pconn is not in the idle list at all (common in HTTP/2). This made the added test TestDontCacheBrokenHTTP2Conn flaky. CL 218097 (unsubmitted) proposed to expand the handling of the http2erringRoundTripper after the new check, to dispose of the pconn more thoroughly. Bryan Mills pointed out in that review that we probably shouldn't make the never-going-to-work pconn in the first place. This CL changes the upgrade phase look for the http2erringRoundTripper and return the underlying error instead of claiming to have a working connection. Having done that, the CL undoes the change in CL 202078 and with it the need for CL 218097, but it keeps the new test added by CL 202078. On my laptop, before this commit, TestDontCacheBrokenHTTP2Conn failed 66 times out of 20,000. With this commit, I see 0 out of 20,000. Fixes #34978. Fixes #35113. Change-Id: Ibd908b63c2ae96e159e8e604213d8373afb350e3 Reviewed-on: https://go-review.googlesource.com/c/go/+/220905 Reviewed-by: Bryan C. Mills <[email protected]> Run-TryBot: Russ Cox <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 96acb74 commit ea1437a

File tree

2 files changed

+9
-7
lines changed

2 files changed

+9
-7
lines changed

src/net/http/omithttp2.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type http2Transport struct {
3232
func (*http2Transport) RoundTrip(*Request) (*Response, error) { panic(noHTTP2) }
3333
func (*http2Transport) CloseIdleConnections() {}
3434

35-
type http2erringRoundTripper struct{}
35+
type http2erringRoundTripper struct{ err error }
3636

3737
func (http2erringRoundTripper) RoundTrip(*Request) (*Response, error) { panic(noHTTP2) }
3838

src/net/http/transport.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -569,14 +569,11 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
569569
}
570570

571571
// Failed. Clean up and determine whether to retry.
572-
573-
_, isH2DialError := pconn.alt.(http2erringRoundTripper)
574-
if http2isNoCachedConnError(err) || isH2DialError {
572+
if http2isNoCachedConnError(err) {
575573
if t.removeIdleConn(pconn) {
576574
t.decConnsPerHost(pconn.cacheKey)
577575
}
578-
}
579-
if !pconn.shouldRetryRequest(req, err) {
576+
} else if !pconn.shouldRetryRequest(req, err) {
580577
// Issue 16465: return underlying net.Conn.Read error from peek,
581578
// as we've historically done.
582579
if e, ok := err.(transportReadFromServerError); ok {
@@ -1637,7 +1634,12 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (pconn *pers
16371634

16381635
if s := pconn.tlsState; s != nil && s.NegotiatedProtocolIsMutual && s.NegotiatedProtocol != "" {
16391636
if next, ok := t.TLSNextProto[s.NegotiatedProtocol]; ok {
1640-
return &persistConn{t: t, cacheKey: pconn.cacheKey, alt: next(cm.targetAddr, pconn.conn.(*tls.Conn))}, nil
1637+
alt := next(cm.targetAddr, pconn.conn.(*tls.Conn))
1638+
if e, ok := alt.(http2erringRoundTripper); ok {
1639+
// pconn.conn was closed by next (http2configureTransport.upgradeFn).
1640+
return nil, e.err
1641+
}
1642+
return &persistConn{t: t, cacheKey: pconn.cacheKey, alt: alt}, nil
16411643
}
16421644
}
16431645

0 commit comments

Comments
 (0)