diff --git a/src/net/http/transport.go b/src/net/http/transport.go index c3d4a95c03..59bffd0ae8 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -83,6 +83,13 @@ const DefaultMaxIdleConnsPerHost = 2 // being written while the response body is streamed. Go's HTTP/2 // implementation does support full duplex, but many CONNECT proxies speak // HTTP/1.x. +// +// Responses with status codes in the 1xx range are either handled +// automatically (100 expect-continue) or ignored. The one +// exception is HTTP status code 101 (Switching Protocols), which is +// considered a terminal status and returned by RoundTrip. To see the +// ignored 1xx responses, use the httptrace trace package's +// ClientTrace.Got1xxResponse. type Transport struct { idleMu sync.Mutex wantIdle bool // user has requested to close all idle conns @@ -1674,7 +1681,10 @@ func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTr continueCh = nil } } - if 100 <= resCode && resCode <= 199 { + is1xx := 100 <= resCode && resCode <= 199 + // treat 101 as a terminal status, see issue 26161 + is1xxNonTerminal := is1xx && resCode != StatusSwitchingProtocols + if is1xxNonTerminal { num1xx++ if num1xx > max1xxResponses { return nil, errors.New("net/http: too many 1xx informational responses") diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 979b8a9009..87361e81ca 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -2375,6 +2375,29 @@ func TestTransportLimits1xxResponses(t *testing.T) { } } +// Issue 26161: the HTTP client must treat 101 responses +// as the final response. +func TestTransportTreat101Terminal(t *testing.T) { + setParallel(t) + defer afterTest(t) + cst := newClientServerTest(t, h1Mode, HandlerFunc(func(w ResponseWriter, r *Request) { + conn, buf, _ := w.(Hijacker).Hijack() + buf.Write([]byte("HTTP/1.1 101 Switching Protocols\r\n\r\n")) + buf.Write([]byte("HTTP/1.1 204 No Content\r\n\r\n")) + buf.Flush() + conn.Close() + })) + defer cst.close() + res, err := cst.c.Get(cst.ts.URL) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + if res.StatusCode != StatusSwitchingProtocols { + t.Errorf("StatusCode = %v; want 101 Switching Protocols", res.StatusCode) + } +} + type proxyFromEnvTest struct { req string // URL to fetch; blank means "http://example.com"