Skip to content

Commit 268d2f7

Browse files
neildgopherbot
authored andcommitted
net/http: handle WriteHeader(101) as a non-informational header
Prior to Go 1.19 adding support for sending 1xx informational headers with ResponseWriter.WriteHeader, WriteHeader(101) would send a 101 status and disable further writes to the response. This behavior was not documented, but is intentional: Writing to the response body explicitly checks to see if a 101 status has been sent before writing. Restore the pre-1.19 behavior when writing a 101 Switching Protocols header: The header is sent, no subsequent headers are sent, and subsequent writes to the response body fail. For #59564 Change-Id: I72c116f88405b1ef5067b510f8c7cff0b36951ee Reviewed-on: https://go-review.googlesource.com/c/go/+/485775 Reviewed-by: Bryan Mills <[email protected]> Auto-Submit: Damien Neil <[email protected]> Run-TryBot: Damien Neil <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 949d0f4 commit 268d2f7

File tree

2 files changed

+74
-2
lines changed

2 files changed

+74
-2
lines changed

src/net/http/serve_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6431,6 +6431,75 @@ func testDisableKeepAliveUpgrade(t *testing.T, mode testMode) {
64316431
}
64326432
}
64336433

6434+
type tlogWriter struct{ t *testing.T }
6435+
6436+
func (w tlogWriter) Write(p []byte) (int, error) {
6437+
w.t.Log(string(p))
6438+
return len(p), nil
6439+
}
6440+
6441+
func TestWriteHeaderSwitchingProtocols(t *testing.T) {
6442+
run(t, testWriteHeaderSwitchingProtocols, []testMode{http1Mode})
6443+
}
6444+
func testWriteHeaderSwitchingProtocols(t *testing.T, mode testMode) {
6445+
const wantBody = "want"
6446+
const wantUpgrade = "someProto"
6447+
ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
6448+
w.Header().Set("Connection", "Upgrade")
6449+
w.Header().Set("Upgrade", wantUpgrade)
6450+
w.WriteHeader(StatusSwitchingProtocols)
6451+
NewResponseController(w).Flush()
6452+
6453+
// Writing headers or the body after sending a 101 header should fail.
6454+
w.WriteHeader(200)
6455+
if _, err := w.Write([]byte("x")); err == nil {
6456+
t.Errorf("Write to body after 101 Switching Protocols unexpectedly succeeded")
6457+
}
6458+
6459+
c, _, err := NewResponseController(w).Hijack()
6460+
if err != nil {
6461+
t.Errorf("Hijack: %v", err)
6462+
return
6463+
}
6464+
defer c.Close()
6465+
if _, err := c.Write([]byte(wantBody)); err != nil {
6466+
t.Errorf("Write to hijacked body: %v", err)
6467+
}
6468+
}), func(ts *httptest.Server) {
6469+
// Don't spam log with warning about superfluous WriteHeader call.
6470+
ts.Config.ErrorLog = log.New(tlogWriter{t}, "log: ", 0)
6471+
}).ts
6472+
6473+
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
6474+
if err != nil {
6475+
t.Fatalf("net.Dial: %v", err)
6476+
}
6477+
_, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: foo\r\n\r\n"))
6478+
if err != nil {
6479+
t.Fatalf("conn.Write: %v", err)
6480+
}
6481+
defer conn.Close()
6482+
6483+
r := bufio.NewReader(conn)
6484+
res, err := ReadResponse(r, &Request{Method: "GET"})
6485+
if err != nil {
6486+
t.Fatal("ReadResponse error:", err)
6487+
}
6488+
if res.StatusCode != StatusSwitchingProtocols {
6489+
t.Errorf("Response StatusCode=%v, want 101", res.StatusCode)
6490+
}
6491+
if got := res.Header.Get("Upgrade"); got != wantUpgrade {
6492+
t.Errorf("Response Upgrade header = %q, want %q", got, wantUpgrade)
6493+
}
6494+
body, err := io.ReadAll(r)
6495+
if err != nil {
6496+
t.Error(err)
6497+
}
6498+
if string(body) != wantBody {
6499+
t.Errorf("Response body = %q, want %q", string(body), wantBody)
6500+
}
6501+
}
6502+
64346503
func TestMuxRedirectRelative(t *testing.T) {
64356504
setParallel(t)
64366505
req, err := ReadRequest(bufio.NewReader(strings.NewReader("GET http://example.com HTTP/1.1\r\nHost: test\r\n\r\n")))

src/net/http/server.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,8 +1154,11 @@ func (w *response) WriteHeader(code int) {
11541154
}
11551155
checkWriteHeaderCode(code)
11561156

1157-
// Handle informational headers
1158-
if code >= 100 && code <= 199 {
1157+
// Handle informational headers.
1158+
//
1159+
// We shouldn't send any further headers after 101 Switching Protocols,
1160+
// so it takes the non-informational path.
1161+
if code >= 100 && code <= 199 && code != StatusSwitchingProtocols {
11591162
// Prevent a potential race with an automatically-sent 100 Continue triggered by Request.Body.Read()
11601163
if code == 100 && w.canWriteContinue.Load() {
11611164
w.writeContinueMu.Lock()

0 commit comments

Comments
 (0)