From cc5cbee1b6a942d2f55c01697f464be9d2a56818 Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Mon, 23 Apr 2012 22:00:16 -0300 Subject: [PATCH] net/http: allow clients to disable keep-alive Fixes #3540. R=golang-dev, bradfitz, gustavo CC=golang-dev https://golang.org/cl/5996044 --- src/pkg/net/http/request.go | 16 ++++++++++++++-- src/pkg/net/http/serve_test.go | 15 +++++++++++---- src/pkg/net/http/server.go | 3 +-- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/pkg/net/http/request.go b/src/pkg/net/http/request.go index f5bc6eb910..219db483b4 100644 --- a/src/pkg/net/http/request.go +++ b/src/pkg/net/http/request.go @@ -732,12 +732,24 @@ func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, e } func (r *Request) expectsContinue() bool { - return strings.ToLower(r.Header.Get("Expect")) == "100-continue" + return hasToken(r.Header.Get("Expect"), "100-continue") } func (r *Request) wantsHttp10KeepAlive() bool { if r.ProtoMajor != 1 || r.ProtoMinor != 0 { return false } - return strings.Contains(strings.ToLower(r.Header.Get("Connection")), "keep-alive") + return hasToken(r.Header.Get("Connection"), "keep-alive") +} + +func (r *Request) wantsClose() bool { + return hasToken(r.Header.Get("Connection"), "close") +} + +func hasToken(s, token string) bool { + if s == "" { + return false + } + // TODO This is a poor implementation of the RFC. See http://golang.org/issue/3535 + return strings.Contains(strings.ToLower(s), token) } diff --git a/src/pkg/net/http/serve_test.go b/src/pkg/net/http/serve_test.go index b6a6b4c77d..8b9592d181 100644 --- a/src/pkg/net/http/serve_test.go +++ b/src/pkg/net/http/serve_test.go @@ -370,7 +370,7 @@ func TestIdentityResponse(t *testing.T) { }) } -func testTcpConnectionCloses(t *testing.T, req string, h Handler) { +func testTCPConnectionCloses(t *testing.T, req string, h Handler) { s := httptest.NewServer(h) defer s.Close() @@ -410,21 +410,28 @@ func testTcpConnectionCloses(t *testing.T, req string, h Handler) { // TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive. func TestServeHTTP10Close(t *testing.T) { - testTcpConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { + testTCPConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { ServeFile(w, r, "testdata/file") })) } +// TestClientCanClose verifies that clients can also force a connection to close. +func TestClientCanClose(t *testing.T) { + testTCPConnectionCloses(t, "GET / HTTP/1.1\r\nConnection: close\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { + // Nothing. + })) +} + // TestHandlersCanSetConnectionClose verifies that handlers can force a connection to close, // even for HTTP/1.1 requests. func TestHandlersCanSetConnectionClose11(t *testing.T) { - testTcpConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { + testTCPConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Connection", "close") })) } func TestHandlersCanSetConnectionClose10(t *testing.T) { - testTcpConnectionCloses(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { + testTCPConnectionCloses(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Connection", "close") })) } diff --git a/src/pkg/net/http/server.go b/src/pkg/net/http/server.go index 924ffd3481..ae93db8070 100644 --- a/src/pkg/net/http/server.go +++ b/src/pkg/net/http/server.go @@ -303,8 +303,7 @@ func (w *response) WriteHeader(code int) { if !connectionHeaderSet { w.header.Set("Connection", "keep-alive") } - } else if !w.req.ProtoAtLeast(1, 1) { - // Client did not ask to keep connection alive. + } else if !w.req.ProtoAtLeast(1, 1) || w.req.wantsClose() { w.closeAfterReply = true }