diff options
author | ichx <czn16@qq.com> | 2021-12-05 21:11:51 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-05 14:11:51 +0100 |
commit | da7ff7a2080953e370e8bd712f5ae8ef8b0cb4e1 (patch) | |
tree | d361832c2ab7da262ce69a646377cf3301968cca /http_test.go | |
parent | fix: reset request after reset user values on keep-alive connections (#1162) (diff) | |
download | fasthttp-da7ff7a2080953e370e8bd712f5ae8ef8b0cb4e1.tar.gz fasthttp-da7ff7a2080953e370e8bd712f5ae8ef8b0cb4e1.tar.bz2 fasthttp-da7ff7a2080953e370e8bd712f5ae8ef8b0cb4e1.zip |
Add trailer support (#1165)
* Add trailer support
* fix issue and add documentation
* remove redundant code
* add error return for add/set trailer method
* fix lint error
* fix bad trailer error return issue and update bad content-length error
* update errNonNumericChars
* update errNonNumericChars
* fix issue about error and fix typo
Diffstat (limited to 'http_test.go')
-rw-r--r-- | http_test.go | 392 |
1 files changed, 311 insertions, 81 deletions
diff --git a/http_test.go b/http_test.go index 8bd050b..978703a 100644 --- a/http_test.go +++ b/http_test.go @@ -155,6 +155,122 @@ func testResponseCopyTo(t *testing.T, src *Response) { } } +func TestRequestBodyStreamWithTrailer(t *testing.T) { + t.Parallel() + + testRequestBodyStreamWithTrailer(t, nil, false) + + body := createFixedBody(1e5) + testRequestBodyStreamWithTrailer(t, body, false) + testRequestBodyStreamWithTrailer(t, body, true) +} + +func testRequestBodyStreamWithTrailer(t *testing.T, body []byte, disableNormalizing bool) { + expectedTrailer := map[string]string{ + "foo": "testfoo", + "bar": "testbar", + } + + var req1 Request + req1.Header.disableNormalizing = disableNormalizing + req1.SetHost("google.com") + req1.SetBodyStream(bytes.NewBuffer(body), -1) + for k, v := range expectedTrailer { + err := req1.Header.AddTrailer(k) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + req1.Header.Set(k, v) + } + + w := &bytes.Buffer{} + bw := bufio.NewWriter(w) + if err := req1.Write(bw); err != nil { + t.Fatalf("unexpected error: %s", err) + } + if err := bw.Flush(); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + var req2 Request + req2.Header.disableNormalizing = disableNormalizing + br := bufio.NewReader(w) + if err := req2.Read(br); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + reqBody := req2.Body() + if !bytes.Equal(reqBody, body) { + t.Fatalf("unexpected body: %q. Expecting %q", reqBody, body) + } + + for k, v := range expectedTrailer { + kBytes := []byte(k) + normalizeHeaderKey(kBytes, disableNormalizing) + r := req2.Header.Peek(k) + if string(r) != v { + t.Fatalf("unexpected trailer header %q: %q. Expecting %s", kBytes, r, v) + } + } +} + +func TestResponseBodyStreamWithTrailer(t *testing.T) { + t.Parallel() + + testResponseBodyStreamWithTrailer(t, nil, false) + + body := createFixedBody(1e5) + testResponseBodyStreamWithTrailer(t, body, false) + testResponseBodyStreamWithTrailer(t, body, true) +} + +func testResponseBodyStreamWithTrailer(t *testing.T, body []byte, disableNormalizing bool) { + expectedTrailer := map[string]string{ + "foo": "testfoo", + "bar": "testbar", + } + var resp1 Response + resp1.Header.disableNormalizing = disableNormalizing + resp1.SetBodyStream(bytes.NewReader(body), -1) + for k, v := range expectedTrailer { + err := resp1.Header.AddTrailer(k) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + resp1.Header.Set(k, v) + } + + w := &bytes.Buffer{} + bw := bufio.NewWriter(w) + if err := resp1.Write(bw); err != nil { + t.Fatalf("unexpected error: %s", err) + } + if err := bw.Flush(); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + var resp2 Response + resp2.Header.disableNormalizing = disableNormalizing + br := bufio.NewReader(w) + if err := resp2.Read(br); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + respBody := resp2.Body() + if !bytes.Equal(respBody, body) { + t.Fatalf("unexpected body: %q. Expecting %q", respBody, body) + } + + for k, v := range expectedTrailer { + kBytes := []byte(k) + normalizeHeaderKey(kBytes, disableNormalizing) + r := resp2.Header.Peek(k) + if string(r) != v { + t.Fatalf("unexpected trailer header %q: %q. Expecting %s", kBytes, r, v) + } + } +} + func TestResponseBodyStreamDeflate(t *testing.T) { t.Parallel() @@ -1344,17 +1460,19 @@ func TestResponseReadLimitBody(t *testing.T) { // response with content-length testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 10) testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 100) - testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 9) + testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 9, ErrBodyTooLarge) // chunked response testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 9) + testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nFoo: bar\r\n\r\n", 9) testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 100) - testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 2) + testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nfoobar\r\n\r\n", 100) + testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 2, ErrBodyTooLarge) // identity response testResponseReadLimitBodySuccess(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 6) testResponseReadLimitBodySuccess(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 106) - testResponseReadLimitBodyError(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 5) + testResponseReadLimitBodyError(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 5, ErrBodyTooLarge) } func TestRequestReadLimitBody(t *testing.T) { @@ -1363,15 +1481,17 @@ func TestRequestReadLimitBody(t *testing.T) { // request with content-length testRequestReadLimitBodySuccess(t, "POST /foo HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 9\r\nContent-Type: aaa\r\n\r\n123456789", 9) testRequestReadLimitBodySuccess(t, "POST /foo HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 9\r\nContent-Type: aaa\r\n\r\n123456789", 92) - testRequestReadLimitBodyError(t, "POST /foo HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 9\r\nContent-Type: aaa\r\n\r\n123456789", 5) + testRequestReadLimitBodyError(t, "POST /foo HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 9\r\nContent-Type: aaa\r\n\r\n123456789", 5, ErrBodyTooLarge) // chunked request testRequestReadLimitBodySuccess(t, "POST /a HTTP/1.1\r\nHost: a.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 9) + testRequestReadLimitBodySuccess(t, "POST /a HTTP/1.1\nHost: a.com\nTransfer-Encoding: chunked\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nFoo: bar\r\n\r\n", 9) testRequestReadLimitBodySuccess(t, "POST /a HTTP/1.1\r\nHost: a.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 999) - testRequestReadLimitBodyError(t, "POST /a HTTP/1.1\r\nHost: a.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 8) + testRequestReadLimitBodySuccess(t, "POST /a HTTP/1.1\r\nHost: a.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nfoobar\r\n\r\n", 999) + testRequestReadLimitBodyError(t, "POST /a HTTP/1.1\r\nHost: a.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 8, ErrBodyTooLarge) } -func testResponseReadLimitBodyError(t *testing.T, s string, maxBodySize int) { +func testResponseReadLimitBodyError(t *testing.T, s string, maxBodySize int, expectedErr error) { var req Response r := bytes.NewBufferString(s) br := bufio.NewReader(r) @@ -1379,8 +1499,8 @@ func testResponseReadLimitBodyError(t *testing.T, s string, maxBodySize int) { if err == nil { t.Fatalf("expecting error. s=%q, maxBodySize=%d", s, maxBodySize) } - if err != ErrBodyTooLarge { - t.Fatalf("unexpected error: %s. Expecting %s. s=%q, maxBodySize=%d", err, ErrBodyTooLarge, s, maxBodySize) + if err != expectedErr { + t.Fatalf("unexpected error: %s. Expecting %s. s=%q, maxBodySize=%d", err, expectedErr, s, maxBodySize) } } @@ -1393,7 +1513,7 @@ func testResponseReadLimitBodySuccess(t *testing.T, s string, maxBodySize int) { } } -func testRequestReadLimitBodyError(t *testing.T, s string, maxBodySize int) { +func testRequestReadLimitBodyError(t *testing.T, s string, maxBodySize int, expectedErr error) { var req Request r := bytes.NewBufferString(s) br := bufio.NewReader(r) @@ -1401,8 +1521,8 @@ func testRequestReadLimitBodyError(t *testing.T, s string, maxBodySize int) { if err == nil { t.Fatalf("expecting error. s=%q, maxBodySize=%d", s, maxBodySize) } - if err != ErrBodyTooLarge { - t.Fatalf("unexpected error: %s. Expecting %s. s=%q, maxBodySize=%d", err, ErrBodyTooLarge, s, maxBodySize) + if err != expectedErr { + t.Fatalf("unexpected error: %s. Expecting %s. s=%q, maxBodySize=%d", err, expectedErr, s, maxBodySize) } } @@ -1490,52 +1610,49 @@ func TestRequestWriteRequestURINoHost(t *testing.T) { func TestSetRequestBodyStreamFixedSize(t *testing.T) { t.Parallel() - testSetRequestBodyStream(t, "a", false) - testSetRequestBodyStream(t, string(createFixedBody(4097)), false) - testSetRequestBodyStream(t, string(createFixedBody(100500)), false) + testSetRequestBodyStream(t, "a") + testSetRequestBodyStream(t, string(createFixedBody(4097))) + testSetRequestBodyStream(t, string(createFixedBody(100500))) } func TestSetResponseBodyStreamFixedSize(t *testing.T) { t.Parallel() - testSetResponseBodyStream(t, "a", false) - testSetResponseBodyStream(t, string(createFixedBody(4097)), false) - testSetResponseBodyStream(t, string(createFixedBody(100500)), false) + testSetResponseBodyStream(t, "a") + testSetResponseBodyStream(t, string(createFixedBody(4097))) + testSetResponseBodyStream(t, string(createFixedBody(100500))) } func TestSetRequestBodyStreamChunked(t *testing.T) { t.Parallel() - testSetRequestBodyStream(t, "", true) + testSetRequestBodyStreamChunked(t, "", map[string]string{"Foo": "bar"}) body := "foobar baz aaa bbb ccc" - testSetRequestBodyStream(t, body, true) + testSetRequestBodyStreamChunked(t, body, nil) body = string(createFixedBody(10001)) - testSetRequestBodyStream(t, body, true) + testSetRequestBodyStreamChunked(t, body, map[string]string{"Foo": "test", "Bar": "test"}) } func TestSetResponseBodyStreamChunked(t *testing.T) { t.Parallel() - testSetResponseBodyStream(t, "", true) + testSetResponseBodyStreamChunked(t, "", map[string]string{"Foo": "bar"}) body := "foobar baz aaa bbb ccc" - testSetResponseBodyStream(t, body, true) + testSetResponseBodyStreamChunked(t, body, nil) body = string(createFixedBody(10001)) - testSetResponseBodyStream(t, body, true) + testSetResponseBodyStreamChunked(t, body, map[string]string{"Foo": "test", "Bar": "test"}) } -func testSetRequestBodyStream(t *testing.T, body string, chunked bool) { +func testSetRequestBodyStream(t *testing.T, body string) { var req Request req.Header.SetHost("foobar.com") req.Header.SetMethod(MethodPost) bodySize := len(body) - if chunked { - bodySize = -1 - } if req.IsBodyStream() { t.Fatalf("IsBodyStream must return false") } @@ -1563,12 +1680,56 @@ func testSetRequestBodyStream(t *testing.T, body string, chunked bool) { } } -func testSetResponseBodyStream(t *testing.T, body string, chunked bool) { +func testSetRequestBodyStreamChunked(t *testing.T, body string, trailer map[string]string) { + var req Request + req.Header.SetHost("foobar.com") + req.Header.SetMethod(MethodPost) + + if req.IsBodyStream() { + t.Fatalf("IsBodyStream must return false") + } + req.SetBodyStream(bytes.NewBufferString(body), -1) + if !req.IsBodyStream() { + t.Fatalf("IsBodyStream must return true") + } + + var w bytes.Buffer + bw := bufio.NewWriter(&w) + for k := range trailer { + err := req.Header.AddTrailer(k) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + } + if err := req.Write(bw); err != nil { + t.Fatalf("unexpected error when writing request: %s. body=%q", err, body) + } + for k, v := range trailer { + req.Header.Set(k, v) + } + if err := bw.Flush(); err != nil { + t.Fatalf("unexpected error when flushing request: %s. body=%q", err, body) + } + + var req1 Request + br := bufio.NewReader(&w) + if err := req1.Read(br); err != nil { + t.Fatalf("unexpected error when reading request: %s. body=%q", err, body) + } + if string(req1.Body()) != body { + t.Fatalf("unexpected body %q. Expecting %q", req1.Body(), body) + } + for k, v := range trailer { + r := req.Header.Peek(k) + if string(r) != v { + t.Fatalf("unexpected trailer %s. Expecting %s. Got %q", k, v, r) + } + } +} + +func testSetResponseBodyStream(t *testing.T, body string) { var resp Response bodySize := len(body) - if chunked { - bodySize = -1 - } if resp.IsBodyStream() { t.Fatalf("IsBodyStream must return false") } @@ -1596,6 +1757,50 @@ func testSetResponseBodyStream(t *testing.T, body string, chunked bool) { } } +func testSetResponseBodyStreamChunked(t *testing.T, body string, trailer map[string]string) { + var resp Response + if resp.IsBodyStream() { + t.Fatalf("IsBodyStream must return false") + } + resp.SetBodyStream(bytes.NewBufferString(body), -1) + if !resp.IsBodyStream() { + t.Fatalf("IsBodyStream must return true") + } + + var w bytes.Buffer + bw := bufio.NewWriter(&w) + for k := range trailer { + err := resp.Header.AddTrailer(k) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + } + if err := resp.Write(bw); err != nil { + t.Fatalf("unexpected error when writing response: %s. body=%q", err, body) + } + if err := bw.Flush(); err != nil { + t.Fatalf("unexpected error when flushing response: %s. body=%q", err, body) + } + for k, v := range trailer { + resp.Header.Set(k, v) + } + + var resp1 Response + br := bufio.NewReader(&w) + if err := resp1.Read(br); err != nil { + t.Fatalf("unexpected error when reading response: %s. body=%q", err, body) + } + if string(resp1.Body()) != body { + t.Fatalf("unexpected body %q. Expecting %q", resp1.Body(), body) + } + for k, v := range trailer { + r := resp.Header.Peek(k) + if string(r) != v { + t.Fatalf("unexpected trailer %s. Expecting %s. Got %q", k, v, r) + } + } +} + func TestRound2(t *testing.T) { t.Parallel() @@ -1622,7 +1827,7 @@ func TestRequestReadChunked(t *testing.T) { var req Request - s := "POST /foo HTTP/1.1\r\nHost: google.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa/bb\r\n\r\n3\r\nabc\r\n5\r\n12345\r\n0\r\n\r\ntrail" + s := "POST /foo HTTP/1.1\r\nHost: google.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa/bb\r\n\r\n3\r\nabc\r\n5\r\n12345\r\n0\r\n\r\nTrail: test\r\n\r\n" r := bytes.NewBufferString(s) rb := bufio.NewReader(r) err := req.Read(rb) @@ -1633,8 +1838,8 @@ func TestRequestReadChunked(t *testing.T) { if string(req.Body()) != expectedBody { t.Fatalf("Unexpected body %q. Expected %q", req.Body(), expectedBody) } - verifyRequestHeader(t, &req.Header, 8, "/foo", "google.com", "", "aa/bb") - verifyTrailer(t, rb, "trail") + verifyRequestHeader(t, &req.Header, -1, "/foo", "google.com", "", "aa/bb") + verifyTrailer(t, rb, map[string]string{"Trail": "test"}, true) } // See: https://github.com/erikdubbelboer/fasthttp/issues/34 @@ -1661,25 +1866,25 @@ func TestResponseReadWithoutBody(t *testing.T) { var resp Response - testResponseReadWithoutBody(t, &resp, "HTTP/1.1 304 Not Modified\r\nContent-Type: aa\r\nContent-Length: 1235\r\n\r\nfoobar", false, - 304, 1235, "aa", "foobar") + testResponseReadWithoutBody(t, &resp, "HTTP/1.1 304 Not Modified\r\nContent-Type: aa\r\nContent-Length: 1235\r\n\r\n", false, + 304, 1235, "aa", nil) - testResponseReadWithoutBody(t, &resp, "HTTP/1.1 204 Foo Bar\r\nContent-Type: aab\r\nTransfer-Encoding: chunked\r\n\r\n123\r\nss", false, - 204, -1, "aab", "123\r\nss") + testResponseReadWithoutBody(t, &resp, "HTTP/1.1 204 Foo Bar\r\nContent-Type: aab\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo: bar\r\n\r\n", false, + 204, -1, "aab", map[string]string{"Foo": "bar"}) - testResponseReadWithoutBody(t, &resp, "HTTP/1.1 123 AAA\r\nContent-Type: xxx\r\nContent-Length: 3434\r\n\r\naaaa", false, - 123, 3434, "xxx", "aaaa") + testResponseReadWithoutBody(t, &resp, "HTTP/1.1 123 AAA\r\nContent-Type: xxx\r\nContent-Length: 3434\r\n\r\n", false, + 123, 3434, "xxx", nil) - testResponseReadWithoutBody(t, &resp, "HTTP 200 OK\r\nContent-Type: text/xml\r\nContent-Length: 123\r\n\r\nxxxx", true, - 200, 123, "text/xml", "xxxx") + testResponseReadWithoutBody(t, &resp, "HTTP 200 OK\r\nContent-Type: text/xml\r\nContent-Length: 123\r\n\r\nfoobar\r\n", true, + 200, 123, "text/xml", nil) // '100 Continue' must be skipped. - testResponseReadWithoutBody(t, &resp, "HTTP/1.1 100 Continue\r\nFoo-bar: baz\r\n\r\nHTTP/1.1 329 aaa\r\nContent-Type: qwe\r\nContent-Length: 894\r\n\r\nfoobar", true, - 329, 894, "qwe", "foobar") + testResponseReadWithoutBody(t, &resp, "HTTP/1.1 100 Continue\r\nFoo-bar: baz\r\n\r\nHTTP/1.1 329 aaa\r\nContent-Type: qwe\r\nContent-Length: 894\r\n\r\n", true, + 329, 894, "qwe", nil) } func testResponseReadWithoutBody(t *testing.T, resp *Response, s string, skipBody bool, - expectedStatusCode, expectedContentLength int, expectedContentType, expectedTrailer string) { + expectedStatusCode, expectedContentLength int, expectedContentType string, expectedTrailer map[string]string) { r := bytes.NewBufferString(s) rb := bufio.NewReader(r) resp.SkipBody = skipBody @@ -1691,12 +1896,12 @@ func testResponseReadWithoutBody(t *testing.T, resp *Response, s string, skipBod t.Fatalf("Unexpected response body %q. Expected %q. response=%q", resp.Body(), "", s) } verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType) - verifyTrailer(t, rb, expectedTrailer) + verifyResponseTrailer(t, &resp.Header, expectedTrailer) // verify that ordinal response is read after null-body response resp.SkipBody = false testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nContent-Length: 5\r\nContent-Type: bar\r\n\r\n56789aaa", - 300, 5, "bar", "56789", "aaa") + 300, 5, "bar", "56789", nil) } func TestRequestSuccess(t *testing.T) { @@ -1872,40 +2077,54 @@ func TestResponseReadSuccess(t *testing.T) { // usual response testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Type: foo/bar\r\n\r\n0123456789", - 200, 10, "foo/bar", "0123456789", "") + 200, 10, "foo/bar", "0123456789", nil) // zero response testResponseReadSuccess(t, resp, "HTTP/1.1 500 OK\r\nContent-Length: 0\r\nContent-Type: foo/bar\r\n\r\n", - 500, 0, "foo/bar", "", "") + 500, 0, "foo/bar", "", nil) // response with trailer - testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nContent-Length: 5\r\nContent-Type: bar\r\n\r\n56789aaa", - 300, 5, "bar", "56789", "aaa") + testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n", + 300, -1, "bar", "56789", map[string]string{"Foo": "bar"}) - // no conent-length ('identity' transfer-encoding) - testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foobar\r\n\r\nzxxc", - 200, 4, "foobar", "zxxc", "") + // response with trailer disableNormalizing + resp.Header.DisableNormalizing() + testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n", + 300, -1, "bar", "56789", map[string]string{"foo": "bar"}) + + // no content-length ('identity' transfer-encoding) + testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foobar\r\n\r\nzxxxx", + 200, 5, "foobar", "zxxxx", nil) // explicitly stated 'Transfer-Encoding: identity' testResponseReadSuccess(t, resp, "HTTP/1.1 234 ss\r\nContent-Type: xxx\r\n\r\nxag", - 234, 3, "xxx", "xag", "") + 234, 3, "xxx", "xag", nil) // big 'identity' response body := string(createFixedBody(100500)) testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\n\r\n"+body, - 200, 100500, "aa", body, "") + 200, 100500, "aa", body, nil) // chunked response - testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\n\r\nzzzzz", - 200, 6, "text/html", "qwerty", "zzzzz") + testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nFoo2: bar2\r\n\r\n", + 200, -1, "text/html", "qwerty", map[string]string{"Foo2": "bar2"}) // chunked response with non-chunked Transfer-Encoding. - testResponseReadSuccess(t, resp, "HTTP/1.1 230 OK\r\nContent-Type: text\r\nTransfer-Encoding: aaabbb\r\n\r\n2\r\ner\r\n2\r\nty\r\n0\r\n\r\nwe", - 230, 4, "text", "erty", "we") + testResponseReadSuccess(t, resp, "HTTP/1.1 230 OK\r\nContent-Type: text\r\nTransfer-Encoding: aaabbb\r\n\r\n2\r\ner\r\n2\r\nty\r\n0\r\nFoo3: bar3\r\n\r\n", + 230, -1, "text", "erty", map[string]string{"Foo3": "bar3"}) + + // chunked response with content-length + testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foo/bar\r\nContent-Length: 123\r\nTransfer-Encoding: chunked\r\n\r\n4\r\ntest\r\n0\r\nFoo4:bar4\r\n\r\n", + 200, -1, "foo/bar", "test", map[string]string{"Foo4": "bar4"}) + + // chunked response with empty body + testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo5: bar5\r\n\r\n", + 200, -1, "text/html", "", map[string]string{"Foo5": "bar5"}) + + // chunked response with chunk extension + testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n3;ext\r\naaa\r\n0\r\nFoo6: bar6\r\n\r\n", + 200, -1, "text/html", "aaa", map[string]string{"Foo6": "bar6"}) - // zero chunked response - testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nzzz", - 200, 0, "text/html", "", "zzz") } func TestResponseReadError(t *testing.T) { @@ -1922,8 +2141,13 @@ func TestResponseReadError(t *testing.T) { // empty body testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nContent-Length: 1234\r\n\r\n") - // short body + // invalid chunked body testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nContent-Length: 1234\r\n\r\nshort") + + // chunked body without end chunk + testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nTransfer-Encoding: chunked\r\n\r\nfoo") + + testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nfoo") } func testResponseReadError(t *testing.T, resp *Response, response string) { @@ -1934,12 +2158,12 @@ func testResponseReadError(t *testing.T, resp *Response, response string) { t.Fatalf("Expecting error for response=%q", response) } - testResponseReadSuccess(t, resp, "HTTP/1.1 303 Redisred sedfs sdf\r\nContent-Type: aaa\r\nContent-Length: 5\r\n\r\nHELLOaaa", - 303, 5, "aaa", "HELLO", "aaa") + testResponseReadSuccess(t, resp, "HTTP/1.1 303 Redisred sedfs sdf\r\nContent-Type: aaa\r\nContent-Length: 5\r\n\r\nHELLO", + 303, 5, "aaa", "HELLO", nil) } func testResponseReadSuccess(t *testing.T, resp *Response, response string, expectedStatusCode, expectedContentLength int, - expectedContenType, expectedBody, expectedTrailer string) { + expectedContentType, expectedBody string, expectedTrailer map[string]string) { r := bytes.NewBufferString(response) rb := bufio.NewReader(r) @@ -1948,11 +2172,11 @@ func testResponseReadSuccess(t *testing.T, resp *Response, response string, expe t.Fatalf("Unexpected error: %s", err) } - verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContenType) + verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType) if !bytes.Equal(resp.Body(), []byte(expectedBody)) { t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), []byte(expectedBody)) } - verifyTrailer(t, rb, expectedTrailer) + verifyResponseTrailer(t, &resp.Header, expectedTrailer) } func TestReadBodyFixedSize(t *testing.T) { @@ -2109,28 +2333,24 @@ func testRequestPostArgsSuccess(t *testing.T, req *Request, s string, expectedAr func testReadBodyChunked(t *testing.T, bodySize int) { body := createFixedBody(bodySize) - chunkedBody := createChunkedBody(body) - expectedTrailer := []byte("chunked shit") - chunkedBody = append(chunkedBody, expectedTrailer...) + expectedTrailer := map[string]string{"Foo": "bar"} + chunkedBody := createChunkedBody(body, expectedTrailer, true) r := bytes.NewBuffer(chunkedBody) br := bufio.NewReader(r) - b, err := readBody(br, -1, 0, nil) + b, err := readBodyChunked(br, 0, nil) if err != nil { t.Fatalf("Unexpected error for bodySize=%d: %s. body=%q, chunkedBody=%q", bodySize, err, body, chunkedBody) } if !bytes.Equal(b, body) { t.Fatalf("Unexpected response read for bodySize=%d: %q. Expected %q. chunkedBody=%q", bodySize, b, body, chunkedBody) } - verifyTrailer(t, br, string(expectedTrailer)) + verifyTrailer(t, br, expectedTrailer, false) } func testReadBodyFixedSize(t *testing.T, bodySize int) { body := createFixedBody(bodySize) - expectedTrailer := []byte("traler aaaa") - bodyWithTrailer := append(body, expectedTrailer...) - - r := bytes.NewBuffer(bodyWithTrailer) + r := bytes.NewBuffer(body) br := bufio.NewReader(r) b, err := readBody(br, bodySize, 0, nil) if err != nil { @@ -2139,7 +2359,7 @@ func testReadBodyFixedSize(t *testing.T, bodySize int) { if !bytes.Equal(b, body) { t.Fatalf("Unexpected response read for bodySize=%d: %q. Expected %q", bodySize, b, body) } - verifyTrailer(t, br, string(expectedTrailer)) + verifyTrailer(t, br, nil, false) } func createFixedBody(bodySize int) []byte { @@ -2150,7 +2370,7 @@ func createFixedBody(bodySize int) []byte { return b } -func createChunkedBody(body []byte) []byte { +func createChunkedBody(body []byte, trailer map[string]string, withEnd bool) []byte { var b []byte chunkSize := 1 for len(body) > 0 { @@ -2163,7 +2383,17 @@ func createChunkedBody(body []byte) []byte { body = body[chunkSize:] chunkSize++ } - return append(b, []byte("0\r\n\r\n")...) + if withEnd { + b = append(b, "0\r\n"...) + for k, v := range trailer { + b = append(b, k...) + b = append(b, ": "...) + b = append(b, v...) + b = append(b, "\r\n"...) + } + b = append(b, "\r\n"...) + } + return b } func TestWriteMultipartForm(t *testing.T) { |