Skip to content

Commit eb9062b

Browse files
committed
net/http: keep HTTP/1.0 keep-alive conns open if response can't have a body
Fixes #15647 Change-Id: I588bfa4eb336d1da1fcda8d06e32ed13c0b51c70 Reviewed-on: https://go-review.googlesource.com/23061 Run-TryBot: Brad Fitzpatrick <[email protected]> Reviewed-by: Andrew Gerrand <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 114051a commit eb9062b

File tree

2 files changed

+44
-1
lines changed

2 files changed

+44
-1
lines changed

src/net/http/serve_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,31 @@ func testTCPConnectionCloses(t *testing.T, req string, h Handler) {
714714
}
715715
}
716716

717+
func testTCPConnectionStaysOpen(t *testing.T, req string, handler Handler) {
718+
defer afterTest(t)
719+
ts := httptest.NewServer(handler)
720+
defer ts.Close()
721+
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
722+
if err != nil {
723+
t.Fatal(err)
724+
}
725+
defer conn.Close()
726+
br := bufio.NewReader(conn)
727+
for i := 0; i < 2; i++ {
728+
if _, err := io.WriteString(conn, req); err != nil {
729+
t.Fatal(err)
730+
}
731+
res, err := ReadResponse(br, nil)
732+
if err != nil {
733+
t.Fatalf("res %d: %v", i+1, err)
734+
}
735+
if _, err := io.Copy(ioutil.Discard, res.Body); err != nil {
736+
t.Fatalf("res %d body copy: %v", i+1, err)
737+
}
738+
res.Body.Close()
739+
}
740+
}
741+
717742
// TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive.
718743
func TestServeHTTP10Close(t *testing.T) {
719744
testTCPConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -749,6 +774,24 @@ func TestHTTP2UpgradeClosesConnection(t *testing.T) {
749774
}))
750775
}
751776

777+
func send204(w ResponseWriter, r *Request) { w.WriteHeader(204) }
778+
func send304(w ResponseWriter, r *Request) { w.WriteHeader(304) }
779+
780+
// Issue 15647: 204 responses can't have bodies, so HTTP/1.0 keep-alive conns should stay open.
781+
func TestHTTP10KeepAlive204Response(t *testing.T) {
782+
testTCPConnectionStaysOpen(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(send204))
783+
}
784+
785+
func TestHTTP11KeepAlive204Response(t *testing.T) {
786+
testTCPConnectionStaysOpen(t, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n", HandlerFunc(send204))
787+
}
788+
789+
func TestHTTP10KeepAlive304Response(t *testing.T) {
790+
testTCPConnectionStaysOpen(t,
791+
"GET / HTTP/1.0\r\nConnection: keep-alive\r\nIf-Modified-Since: Mon, 02 Jan 2006 15:04:05 GMT\r\n\r\n",
792+
HandlerFunc(send304))
793+
}
794+
752795
func TestSetsRemoteAddr_h1(t *testing.T) { testSetsRemoteAddr(t, h1Mode) }
753796
func TestSetsRemoteAddr_h2(t *testing.T) { testSetsRemoteAddr(t, h2Mode) }
754797

src/net/http/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
10081008
// Check for a explicit (and valid) Content-Length header.
10091009
hasCL := w.contentLength != -1
10101010

1011-
if w.wants10KeepAlive && (isHEAD || hasCL) {
1011+
if w.wants10KeepAlive && (isHEAD || hasCL || !bodyAllowedForStatus(w.status)) {
10121012
_, connectionHeaderSet := header["Connection"]
10131013
if !connectionHeaderSet {
10141014
setHeader.connection = "keep-alive"

0 commit comments

Comments
 (0)