]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: make Transport treat 101 as a terminal status
authorMark Fischer <meirfischer@gmail.com>
Mon, 2 Jul 2018 04:56:46 +0000 (00:56 -0400)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 3 Jul 2018 01:11:30 +0000 (01:11 +0000)
Before CL 116855, Transport would only skip over 100 (expect-continue)
responses automatically and treat all other 1xx responses as if they
were the final status. CL 116855 made the Transport more spec
compliant (ignoring unknown 1xx responses), but broke "101 Switching
Protocols" in the process. Since 101 is already in use and defined to
not have a following message, treat it as terminal.

Note that because the Client/Transport don't support hijacking the
underlying Conn, most clients doing a WebSocket or other protocol
upgrade are probably using net.Dial + http.ReadResponse instead, which
remained unaffected (before & after this CL).

The main affect of this CL is to fix tests that were using the
Client/Transport to test that a server returns 101, presumably without
actually switching to another protocol.

Fixes #26161

Change-Id: Ie3cd3a465f948c4d6f7ddf2a6a78a7fb935d0672
Reviewed-on: https://go-review.googlesource.com/121860
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/net/http/transport.go
src/net/http/transport_test.go

index c3d4a95c03b6632e38cd2a21eb809218bbec8e48..59bffd0ae89139e6a50070681daa019e96329a16 100644 (file)
@@ -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")
index 979b8a900985a150fd887e24a468da44158cb623..87361e81ca50bb17246b441ed8bc0d9ad4f85040 100644 (file)
@@ -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"