From 16632cbaa403320e75bfe2e3478df24ba642ba9d Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 13 Nov 2015 12:27:47 +0200 Subject: [PATCH] Set ConnectionClose for non-http/1.1 requests/responses. Do not support Connection: keep-alive for http/1.0 intentionally, since it will complicate the code without measurable benefits --- header.go | 11 +++++++++- header_test.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/header.go b/header.go index 3013541..f9c6c1f 100644 --- a/header.go +++ b/header.go @@ -781,11 +781,15 @@ func (h *ResponseHeader) parseFirstLine(buf []byte) (b []byte, err error) { } } - // skip protocol + // parse protocol n := bytes.IndexByte(b, ' ') if n < 0 { return nil, fmt.Errorf("cannot find whitespace in the first line of response %q", buf) } + if !bytes.Equal(b[:n], strHTTP11) { + // Non-http/1.1 response. Close connection after it. + h.ConnectionClose = true + } b = b[n+1:] // parse status code @@ -819,9 +823,14 @@ func (h *RequestHeader) parseFirstLine(buf []byte) (b []byte, err error) { // parse requestURI n = bytes.LastIndexByte(b, ' ') if n < 0 { + // no http protocol found. Close connection after the request. + h.ConnectionClose = true n = len(b) } else if n == 0 { return nil, fmt.Errorf("RequestURI cannot be empty in %q", buf) + } else if !bytes.Equal(b[n+1:], strHTTP11) { + // non-http/1.1 protocol. Close connection after the request. + h.ConnectionClose = true } h.RequestURI = append(h.RequestURI[:0], b[:n]...) diff --git a/header_test.go b/header_test.go index f8ed6a6..df67e84 100644 --- a/header_test.go +++ b/header_test.go @@ -10,6 +10,56 @@ import ( "testing" ) +func TestResponseHeaderHTTPVer(t *testing.T) { + // non-http/1.1 + testResponseHeaderHTTPVer(t, "HTTP/1.0 200 OK\r\nContent-Type: aaa\r\nContent-Length: 123\r\n\r\n", true) + testResponseHeaderHTTPVer(t, "HTTP/0.9 200 OK\r\nContent-Type: aaa\r\nContent-Length: 123\r\n\r\n", true) + testResponseHeaderHTTPVer(t, "foobar 200 OK\r\nContent-Type: aaa\r\nContent-Length: 123\r\n\r\n", true) + + // http/1.1 + testResponseHeaderHTTPVer(t, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nContent-Length: 123\r\n\r\n", false) +} + +func TestRequestHeaderHTTPVer(t *testing.T) { + // non-http/1.1 + testRequestHeaderHTTPVer(t, "GET / HTTP/1.0\r\nHost: aa.com\r\n\r\n", true) + testRequestHeaderHTTPVer(t, "GET / HTTP/0.9\r\nHost: aa.com\r\n\r\n", true) + testRequestHeaderHTTPVer(t, "GET / foobar\r\nHost: aa.com\r\n\r\n", true) + + // empty http version + testRequestHeaderHTTPVer(t, "GET /\r\nHost: aaa.com\r\n\r\n", true) + testRequestHeaderHTTPVer(t, "GET / \r\nHost: aaa.com\r\n\r\n", true) + + // http/1.1 + testRequestHeaderHTTPVer(t, "GET / HTTP/1.1\r\nHost: a.com\r\n\r\n", false) +} + +func testResponseHeaderHTTPVer(t *testing.T, s string, connectionClose bool) { + var h ResponseHeader + + r := bytes.NewBufferString(s) + br := bufio.NewReader(r) + if err := h.Read(br); err != nil { + t.Fatalf("unexpected error: %s. response=%q", err, s) + } + if h.ConnectionClose != connectionClose { + t.Fatalf("unexpected connectionClose %v. Expecting %v. response=%q", h.ConnectionClose, connectionClose, s) + } +} + +func testRequestHeaderHTTPVer(t *testing.T, s string, connectionClose bool) { + var h RequestHeader + + r := bytes.NewBufferString(s) + br := bufio.NewReader(r) + if err := h.Read(br); err != nil { + t.Fatalf("unexpected error: %s. request=%q", err, s) + } + if h.ConnectionClose != connectionClose { + t.Fatalf("unexpected connectionClose %v. Expecting %v. request=%q", h.ConnectionClose, connectionClose, s) + } +} + func TestResponseHeaderCopyTo(t *testing.T) { var h ResponseHeader @@ -719,13 +769,16 @@ func TestRequestHeaderReadSuccess(t *testing.T) { // ancient http protocol testRequestHeaderReadSuccess(t, h, "GET /bar HTTP/1.0\r\nHost: gole\r\n\r\npppp", 0, "/bar", "gole", "", "", "pppp") - if h.ConnectionClose { - t.Fatalf("unexpected connection: close header") + if !h.ConnectionClose { + t.Fatalf("expecting connectionClose for ancient http protocol") } // complex headers with body testRequestHeaderReadSuccess(t, h, "GET /aabar HTTP/1.1\r\nAAA: bbb\r\nHost: ole.com\r\nAA: bb\r\n\r\nzzz", 0, "/aabar", "ole.com", "", "", "zzz") + if h.ConnectionClose { + t.Fatalf("unexpected connection: close") + } // lf instead of crlf testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\nHost: google.com\n\n",