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 /header_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 'header_test.go')
-rw-r--r-- | header_test.go | 317 |
1 files changed, 223 insertions, 94 deletions
diff --git a/header_test.go b/header_test.go index 27a292c..f041577 100644 --- a/header_test.go +++ b/header_test.go @@ -4,9 +4,9 @@ import ( "bufio" "bytes" "encoding/base64" + "errors" "fmt" "io" - "io/ioutil" "net/http" "reflect" "strings" @@ -535,6 +535,7 @@ func TestRequestHeaderDel(t *testing.T) { h.Set("User-Agent", "asdfas") h.Set("Content-Length", "1123") h.Set("Cookie", "foobar=baz") + h.Set(HeaderTrailer, "foo, bar") h.Del("foo-bar") h.Del("connection") @@ -543,6 +544,7 @@ func TestRequestHeaderDel(t *testing.T) { h.Del("user-agent") h.Del("content-length") h.Del("cookie") + h.Del("trailer") hv := h.Peek("aaa") if string(hv) != "bbb" { @@ -576,6 +578,10 @@ func TestRequestHeaderDel(t *testing.T) { if len(hv) > 0 { t.Fatalf("non-zero value: %q", hv) } + hv = h.Peek(HeaderTrailer) + if len(hv) > 0 { + t.Fatalf("non-zero value: %q", hv) + } cv := h.Cookie("foobar") if len(cv) > 0 { @@ -596,6 +602,7 @@ func TestResponseHeaderDel(t *testing.T) { h.Set(HeaderContentType, "aaa") h.Set(HeaderServer, "aaabbb") h.Set(HeaderContentLength, "1123") + h.Set(HeaderTrailer, "foo, bar") var c Cookie c.SetKey("foo") @@ -608,6 +615,7 @@ func TestResponseHeaderDel(t *testing.T) { h.Del(HeaderServer) h.Del("content-length") h.Del("set-cookie") + h.Del("trailer") hv := h.Peek("aaa") if string(hv) != "bbb" { @@ -633,6 +641,10 @@ func TestResponseHeaderDel(t *testing.T) { if len(hv) > 0 { t.Fatalf("non-zero value: %q", hv) } + hv = h.Peek(HeaderTrailer) + if len(hv) > 0 { + t.Fatalf("non-zero value: %q", hv) + } if h.Cookie(&c) { t.Fatalf("unexpected cookie obtianed: %q", &c) @@ -642,6 +654,51 @@ func TestResponseHeaderDel(t *testing.T) { } } +func TestResponseHeaderSetTrailerGetBytes(t *testing.T) { + t.Parallel() + + h := &ResponseHeader{} + h.noDefaultDate = true + h.Set("Foo", "bar") + h.Set(HeaderTrailer, "Baz") + h.Set("Baz", "test") + + headerBytes := h.Header() + n, err := h.parseFirstLine(headerBytes) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if string(headerBytes[n:]) != "Foo: bar\r\nTrailer: Baz\r\n\r\n" { + t.Fatalf("Unexpected header: %q. Expected %s", headerBytes[n:], "Foo: bar\nTrailer: Baz\n\n") + } + if string(h.TrailerHeader()) != "Baz: test\r\n\r\n" { + t.Fatalf("Unexpected trailer header: %q. Expected %s", h.TrailerHeader(), "Baz: test\r\n\r\n") + } +} + +func TestRequestHeaderSetTrailerGetBytes(t *testing.T) { + t.Parallel() + + h := &RequestHeader{} + h.Set("Foo", "bar") + h.Set(HeaderTrailer, "Baz") + h.Set("Baz", "test") + + headerBytes := h.Header() + n, err := h.parseFirstLine(headerBytes) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if string(headerBytes[n:]) != "Foo: bar\r\nTrailer: Baz\r\n\r\n" { + t.Fatalf("Unexpected header: %q. Expected %s", headerBytes[n:], "Foo: bar\nTrailer: Baz\n\n") + } + if string(h.TrailerHeader()) != "Baz: test\r\n\r\n" { + t.Fatalf("Unexpected trailer header: %q. Expected %s", h.TrailerHeader(), "Baz: test\r\n\r\n") + } +} + func TestAppendNormalizedHeaderKeyBytes(t *testing.T) { t.Parallel() @@ -1218,6 +1275,7 @@ func TestResponseHeaderCopyTo(t *testing.T) { h.Set(HeaderSetCookie, "foo=bar") h.Set(HeaderContentType, "foobar") h.Set("AAA-BBB", "aaaa") + h.Set(HeaderTrailer, "foo, bar") var h1 ResponseHeader h.CopyTo(&h1) @@ -1230,6 +1288,9 @@ func TestResponseHeaderCopyTo(t *testing.T) { if !bytes.Equal(h1.Peek("aaa-bbb"), h.Peek("AAA-BBB")) { t.Fatalf("unexpected aaa-bbb %q. Expected %q", h1.Peek("aaa-bbb"), h.Peek("aaa-bbb")) } + if !bytes.Equal(h1.Peek(HeaderTrailer), h.Peek(HeaderTrailer)) { + t.Fatalf("unexpected trailer %q. Expected %q", h1.Peek(HeaderTrailer), h.Peek(HeaderTrailer)) + } // flush buf h.bufKV = argsKV{} @@ -1249,6 +1310,7 @@ func TestRequestHeaderCopyTo(t *testing.T) { h.Set(HeaderContentType, "foobar") h.Set(HeaderHost, "aaaa") h.Set("aaaxxx", "123") + h.Set(HeaderTrailer, "foo, bar") var h1 RequestHeader h.CopyTo(&h1) @@ -1264,6 +1326,9 @@ func TestRequestHeaderCopyTo(t *testing.T) { if !bytes.Equal(h1.Peek("aaaxxx"), h.Peek("aaaxxx")) { t.Fatalf("unexpected aaaxxx %q. Expected %q", h1.Peek("aaaxxx"), h.Peek("aaaxxx")) } + if !bytes.Equal(h1.Peek(HeaderTrailer), h.Peek(HeaderTrailer)) { + t.Fatalf("unexpected trailer %q. Expected %q", h1.Peek(HeaderTrailer), h.Peek(HeaderTrailer)) + } // flush buf h.bufKV = argsKV{} @@ -1421,14 +1486,14 @@ func TestResponseHeaderVisitAll(t *testing.T) { var h ResponseHeader - r := bytes.NewBufferString("HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 123\r\nSet-Cookie: aa=bb; path=/foo/bar\r\nSet-Cookie: ccc\r\n\r\n") + r := bytes.NewBufferString("HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 123\r\nSet-Cookie: aa=bb; path=/foo/bar\r\nSet-Cookie: ccc\r\nTrailer: Foo, Bar\r\n\r\n") br := bufio.NewReader(r) if err := h.Read(br); err != nil { t.Fatalf("Unexpected error: %s", err) } - if h.Len() != 4 { - t.Fatalf("Unexpected number of headers: %d. Expected 4", h.Len()) + if h.Len() != 5 { + t.Fatalf("Unexpected number of headers: %d. Expected 5", h.Len()) } contentLengthCount := 0 contentTypeCount := 0 @@ -1455,6 +1520,10 @@ func TestResponseHeaderVisitAll(t *testing.T) { t.Fatalf("unexpected cookie header: %q. Expected %q", v, "ccc") } cookieCount++ + case HeaderTrailer: + if v != "Foo, Bar" { + t.Fatalf("Unexpected trailer header %q. Expected %q", v, "Foo, Bar") + } default: t.Fatalf("unexpected header %q=%q", k, v) } @@ -1475,14 +1544,14 @@ func TestRequestHeaderVisitAll(t *testing.T) { var h RequestHeader - r := bytes.NewBufferString("GET / HTTP/1.1\r\nHost: aa.com\r\nXX: YYY\r\nXX: ZZ\r\nCookie: a=b; c=d\r\n\r\n") + r := bytes.NewBufferString("GET / HTTP/1.1\r\nHost: aa.com\r\nXX: YYY\r\nXX: ZZ\r\nCookie: a=b; c=d\r\nTrailer: Foo, Bar\r\n\r\n") br := bufio.NewReader(r) if err := h.Read(br); err != nil { t.Fatalf("Unexpected error: %s", err) } - if h.Len() != 4 { - t.Fatalf("Unexpected number of header: %d. Expected 4", h.Len()) + if h.Len() != 5 { + t.Fatalf("Unexpected number of header: %d. Expected 5", h.Len()) } hostCount := 0 xxCount := 0 @@ -1509,6 +1578,10 @@ func TestRequestHeaderVisitAll(t *testing.T) { t.Fatalf("Unexpected cookie %q. Expected %q", v, "a=b; c=d") } cookieCount++ + case HeaderTrailer: + if v != "Foo, Bar" { + t.Fatalf("Unexpected trailer header %q. Expected %q", v, "Foo, Bar") + } default: t.Fatalf("Unexpected header %q=%q", k, v) } @@ -1524,7 +1597,7 @@ func TestRequestHeaderVisitAll(t *testing.T) { } } -func TestResponseHeaderVisitAllInOrder(t *testing.T) { +func TestRequestHeaderVisitAllInOrder(t *testing.T) { t.Parallel() var h RequestHeader @@ -1567,6 +1640,38 @@ func TestResponseHeaderVisitAllInOrder(t *testing.T) { }) } +func TestResponseHeaderAddTrailerError(t *testing.T) { + t.Parallel() + + var h ResponseHeader + err := h.AddTrailer("Foo, Content-Length , Bar,Transfer-Encoding,") + expectedTrailer := "Foo, Bar" + + if !errors.Is(err, ErrBadTrailer) { + t.Fatalf("unexpected err %q. Expected %q", err, ErrBadTrailer) + } + if trailer := string(h.Peek(HeaderTrailer)); trailer != expectedTrailer { + t.Fatalf("unexpected trailer %q. Expected %q", trailer, expectedTrailer) + } + +} + +func TestRequestHeaderAddTrailerError(t *testing.T) { + t.Parallel() + + var h RequestHeader + err := h.AddTrailer("Foo, Content-Length , Bar,Transfer-Encoding,") + expectedTrailer := "Foo, Bar" + + if !errors.Is(err, ErrBadTrailer) { + t.Fatalf("unexpected err %q. Expected %q", err, ErrBadTrailer) + } + if trailer := string(h.Peek(HeaderTrailer)); trailer != expectedTrailer { + t.Fatalf("unexpected trailer %q. Expected %q", trailer, expectedTrailer) + } + +} + func TestResponseHeaderCookie(t *testing.T) { t.Parallel() @@ -2106,7 +2211,6 @@ func TestRequestHeaderBufioPeek(t *testing.T) { t.Fatalf("Unexpected error when reading request: %s", err) } verifyRequestHeader(t, h, -2, "/", "foobar.com", "", "") - verifyTrailer(t, br, "aaaa") } func TestResponseHeaderBufioPeek(t *testing.T) { @@ -2121,7 +2225,6 @@ func TestResponseHeaderBufioPeek(t *testing.T) { t.Fatalf("Unexpected error when reading response: %s", err) } verifyResponseHeader(t, h, 200, 10, "aaa") - verifyTrailer(t, br, "0123456789") } func getHeaders(n int) string { @@ -2139,143 +2242,127 @@ func TestResponseHeaderReadSuccess(t *testing.T) { // straight order of content-length and content-type testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Length: 123\r\nContent-Type: text/html\r\n\r\n", - 200, 123, "text/html", "") + 200, 123, "text/html") if h.ConnectionClose() { t.Fatalf("unexpected connection: close") } // reverse order of content-length and content-type testResponseHeaderReadSuccess(t, h, "HTTP/1.1 202 OK\r\nContent-Type: text/plain; encoding=utf-8\r\nContent-Length: 543\r\nConnection: close\r\n\r\n", - 202, 543, "text/plain; encoding=utf-8", "") + 202, 543, "text/plain; encoding=utf-8") if !h.ConnectionClose() { t.Fatalf("expecting connection: close") } // tranfer-encoding: chunked testResponseHeaderReadSuccess(t, h, "HTTP/1.1 505 Internal error\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n", - 505, -1, "text/html", "") + 505, -1, "text/html") if h.ConnectionClose() { t.Fatalf("unexpected connection: close") } // reverse order of content-type and tranfer-encoding testResponseHeaderReadSuccess(t, h, "HTTP/1.1 343 foobar\r\nTransfer-Encoding: chunked\r\nContent-Type: text/json\r\n\r\n", - 343, -1, "text/json", "") + 343, -1, "text/json") // additional headers testResponseHeaderReadSuccess(t, h, "HTTP/1.1 100 Continue\r\nFoobar: baz\r\nContent-Type: aaa/bbb\r\nUser-Agent: x\r\nContent-Length: 123\r\nZZZ: werer\r\n\r\n", - 100, 123, "aaa/bbb", "") - - // trailer (aka body) - testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 32245\r\n\r\nqwert aaa", - 200, 32245, "text/plain", "qwert aaa") + 100, 123, "aaa/bbb") // ancient http protocol testResponseHeaderReadSuccess(t, h, "HTTP/0.9 300 OK\r\nContent-Length: 123\r\nContent-Type: text/html\r\n\r\nqqqq", - 300, 123, "text/html", "qqqq") + 300, 123, "text/html") // lf instead of crlf testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\nContent-Length: 123\nContent-Type: text/html\n\n", - 200, 123, "text/html", "") + 200, 123, "text/html") // Zero-length headers with mixed crlf and lf testResponseHeaderReadSuccess(t, h, "HTTP/1.1 400 OK\nContent-Length: 345\nZero-Value: \r\nContent-Type: aaa\n: zero-key\r\n\r\nooa", - 400, 345, "aaa", "ooa") + 400, 345, "aaa") // No space after colon testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\nContent-Length:34\nContent-Type: sss\n\naaaa", - 200, 34, "sss", "aaaa") + 200, 34, "sss") // invalid case testResponseHeaderReadSuccess(t, h, "HTTP/1.1 400 OK\nconTEnt-leNGTH: 123\nConTENT-TYPE: ass\n\n", - 400, 123, "ass", "") + 400, 123, "ass") // duplicate content-length testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Length: 456\r\nContent-Type: foo/bar\r\nContent-Length: 321\r\n\r\n", - 200, 321, "foo/bar", "") + 200, 321, "foo/bar") // duplicate content-type testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Length: 234\r\nContent-Type: foo/bar\r\nContent-Type: baz/bar\r\n\r\n", - 200, 234, "baz/bar", "") - - // both transfer-encoding: chunked and content-length - testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: foo/bar\r\nContent-Length: 123\r\nTransfer-Encoding: chunked\r\n\r\n", - 200, -1, "foo/bar", "") + 200, 234, "baz/bar") testResponseHeaderReadSuccess(t, h, "HTTP/1.1 300 OK\r\nContent-Type: foo/barr\r\nTransfer-Encoding: chunked\r\nContent-Length: 354\r\n\r\n", - 300, -1, "foo/barr", "") + 300, -1, "foo/barr") // duplicate transfer-encoding: chunked testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\nTransfer-Encoding: chunked\r\n\r\n", - 200, -1, "text/html", "") + 200, -1, "text/html") // no reason string in the first line testResponseHeaderReadSuccess(t, h, "HTTP/1.1 456\r\nContent-Type: xxx/yyy\r\nContent-Length: 134\r\n\r\naaaxxx", - 456, 134, "xxx/yyy", "aaaxxx") + 456, 134, "xxx/yyy") // blank lines before the first line testResponseHeaderReadSuccess(t, h, "\r\nHTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 0\r\n\r\nsss", - 200, 0, "aa", "sss") + 200, 0, "aa") if h.ConnectionClose() { t.Fatalf("unexpected connection: close") } // no content-length (informational responses) testResponseHeaderReadSuccess(t, h, "HTTP/1.1 101 OK\r\n\r\n", - 101, -2, "text/plain; charset=utf-8", "") + 101, -2, "text/plain; charset=utf-8") if h.ConnectionClose() { t.Fatalf("expecting connection: keep-alive for informational response") } // no content-length (no-content responses) testResponseHeaderReadSuccess(t, h, "HTTP/1.1 204 OK\r\n\r\n", - 204, -2, "text/plain; charset=utf-8", "") + 204, -2, "text/plain; charset=utf-8") if h.ConnectionClose() { t.Fatalf("expecting connection: keep-alive for no-content response") } // no content-length (not-modified responses) testResponseHeaderReadSuccess(t, h, "HTTP/1.1 304 OK\r\n\r\n", - 304, -2, "text/plain; charset=utf-8", "") + 304, -2, "text/plain; charset=utf-8") if h.ConnectionClose() { t.Fatalf("expecting connection: keep-alive for not-modified response") } // no content-length (identity transfer-encoding) testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: foo/bar\r\n\r\nabcdefg", - 200, -2, "foo/bar", "abcdefg") + 200, -2, "foo/bar") if !h.ConnectionClose() { t.Fatalf("expecting connection: close for identity response") } - // non-numeric content-length - testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Length: faaa\r\nContent-Type: text/html\r\n\r\nfoobar", - 200, -2, "text/html", "foobar") - testResponseHeaderReadSuccess(t, h, "HTTP/1.1 201 OK\r\nContent-Length: 123aa\r\nContent-Type: text/ht\r\n\r\naaa", - 201, -2, "text/ht", "aaa") - testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Length: aa124\r\nContent-Type: html\r\n\r\nxx", - 200, -2, "html", "xx") - // no content-type testResponseHeaderReadSuccess(t, h, "HTTP/1.1 400 OK\r\nContent-Length: 123\r\n\r\nfoiaaa", - 400, 123, string(defaultContentType), "foiaaa") + 400, 123, string(defaultContentType)) // no content-type and no default h.SetNoDefaultContentType(true) testResponseHeaderReadSuccess(t, h, "HTTP/1.1 400 OK\r\nContent-Length: 123\r\n\r\nfoiaaa", - 400, 123, "", "foiaaa") + 400, 123, "") h.SetNoDefaultContentType(false) // no headers testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\n\r\naaaabbb", - 200, -2, string(defaultContentType), "aaaabbb") + 200, -2, string(defaultContentType)) if !h.IsHTTP11() { t.Fatalf("expecting http/1.1 protocol") } // ancient http protocol testResponseHeaderReadSuccess(t, h, "HTTP/1.0 203 OK\r\nContent-Length: 123\r\nContent-Type: foobar\r\n\r\naaa", - 203, 123, "foobar", "aaa") + 203, 123, "foobar") if h.IsHTTP11() { t.Fatalf("ancient protocol must be non-http/1.1") } @@ -2285,7 +2372,7 @@ func TestResponseHeaderReadSuccess(t *testing.T) { // ancient http protocol with 'Connection: keep-alive' header. testResponseHeaderReadSuccess(t, h, "HTTP/1.0 403 aa\r\nContent-Length: 0\r\nContent-Type: 2\r\nConnection: Keep-Alive\r\n\r\nww", - 403, 0, "2", "ww") + 403, 0, "2") if h.IsHTTP11() { t.Fatalf("ancient protocol must be non-http/1.1") } @@ -2301,21 +2388,21 @@ func TestRequestHeaderReadSuccess(t *testing.T) { // simple headers testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\nHost: google.com\r\n\r\n", - -2, "/foo/bar", "google.com", "", "", "") + -2, "/foo/bar", "google.com", "", "", nil) if h.ConnectionClose() { t.Fatalf("unexpected connection: close header") } // simple headers with body testRequestHeaderReadSuccess(t, h, "GET /a/bar HTTP/1.1\r\nHost: gole.com\r\nconneCTION: close\r\n\r\nfoobar", - -2, "/a/bar", "gole.com", "", "", "foobar") + -2, "/a/bar", "gole.com", "", "", nil) if !h.ConnectionClose() { t.Fatalf("connection: close unset") } // ancient http protocol testRequestHeaderReadSuccess(t, h, "GET /bar HTTP/1.0\r\nHost: gole\r\n\r\npppp", - -2, "/bar", "gole", "", "", "pppp") + -2, "/bar", "gole", "", "", nil) if h.IsHTTP11() { t.Fatalf("ancient http protocol cannot be http/1.1") } @@ -2325,7 +2412,7 @@ func TestRequestHeaderReadSuccess(t *testing.T) { // ancient http protocol with 'Connection: keep-alive' header testRequestHeaderReadSuccess(t, h, "GET /aa HTTP/1.0\r\nHost: bb\r\nConnection: keep-alive\r\n\r\nxxx", - -2, "/aa", "bb", "", "", "xxx") + -2, "/aa", "bb", "", "", nil) if h.IsHTTP11() { t.Fatalf("ancient http protocol cannot be http/1.1") } @@ -2335,7 +2422,7 @@ func TestRequestHeaderReadSuccess(t *testing.T) { // 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", - -2, "/aabar", "ole.com", "", "", "zzz") + -2, "/aabar", "ole.com", "", "", nil) if !h.IsHTTP11() { t.Fatalf("expecting http/1.1 protocol") } @@ -2345,103 +2432,103 @@ func TestRequestHeaderReadSuccess(t *testing.T) { // lf instead of crlf testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\nHost: google.com\n\n", - -2, "/foo/bar", "google.com", "", "", "") + -2, "/foo/bar", "google.com", "", "", nil) // post method testRequestHeaderReadSuccess(t, h, "POST /aaa?bbb HTTP/1.1\r\nHost: foobar.com\r\nContent-Length: 1235\r\nContent-Type: aaa\r\n\r\nabcdef", - 1235, "/aaa?bbb", "foobar.com", "", "aaa", "abcdef") + 1235, "/aaa?bbb", "foobar.com", "", "aaa", nil) // zero-length headers with mixed crlf and lf testRequestHeaderReadSuccess(t, h, "GET /a HTTP/1.1\nHost: aaa\r\nZero: \n: Zero-Value\n\r\nxccv", - -2, "/a", "aaa", "", "", "xccv") + -2, "/a", "aaa", "", "", nil) // no space after colon testRequestHeaderReadSuccess(t, h, "GET /a HTTP/1.1\nHost:aaaxd\n\nsdfds", - -2, "/a", "aaaxd", "", "", "sdfds") + -2, "/a", "aaaxd", "", "", nil) // get with zero content-length testRequestHeaderReadSuccess(t, h, "GET /xxx HTTP/1.1\nHost: aaa.com\nContent-Length: 0\n\n", - 0, "/xxx", "aaa.com", "", "", "") + 0, "/xxx", "aaa.com", "", "", nil) // get with non-zero content-length testRequestHeaderReadSuccess(t, h, "GET /xxx HTTP/1.1\nHost: aaa.com\nContent-Length: 123\n\n", - 123, "/xxx", "aaa.com", "", "", "") + 123, "/xxx", "aaa.com", "", "", nil) // invalid case testRequestHeaderReadSuccess(t, h, "GET /aaa HTTP/1.1\nhoST: bbb.com\n\naas", - -2, "/aaa", "bbb.com", "", "", "aas") + -2, "/aaa", "bbb.com", "", "", nil) // referer testRequestHeaderReadSuccess(t, h, "GET /asdf HTTP/1.1\nHost: aaa.com\nReferer: bb.com\n\naaa", - -2, "/asdf", "aaa.com", "bb.com", "", "aaa") + -2, "/asdf", "aaa.com", "bb.com", "", nil) // duplicate host testRequestHeaderReadSuccess(t, h, "GET /aa HTTP/1.1\r\nHost: aaaaaa.com\r\nHost: bb.com\r\n\r\n", - -2, "/aa", "bb.com", "", "", "") + -2, "/aa", "bb.com", "", "", nil) // post with duplicate content-type testRequestHeaderReadSuccess(t, h, "POST /a HTTP/1.1\r\nHost: aa\r\nContent-Type: ab\r\nContent-Length: 123\r\nContent-Type: xx\r\n\r\n", - 123, "/a", "aa", "", "xx", "") + 123, "/a", "aa", "", "xx", nil) // post with duplicate content-length testRequestHeaderReadSuccess(t, h, "POST /xx HTTP/1.1\r\nHost: aa\r\nContent-Type: s\r\nContent-Length: 13\r\nContent-Length: 1\r\n\r\n", - 1, "/xx", "aa", "", "s", "") + 1, "/xx", "aa", "", "s", nil) // non-post with content-type testRequestHeaderReadSuccess(t, h, "GET /aaa HTTP/1.1\r\nHost: bbb.com\r\nContent-Type: aaab\r\n\r\n", - -2, "/aaa", "bbb.com", "", "aaab", "") + -2, "/aaa", "bbb.com", "", "aaab", nil) // non-post with content-length testRequestHeaderReadSuccess(t, h, "HEAD / HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 123\r\n\r\n", - 123, "/", "aaa.com", "", "", "") + 123, "/", "aaa.com", "", "", nil) // non-post with content-type and content-length testRequestHeaderReadSuccess(t, h, "GET /aa HTTP/1.1\r\nHost: aa.com\r\nContent-Type: abd/test\r\nContent-Length: 123\r\n\r\n", - 123, "/aa", "aa.com", "", "abd/test", "") + 123, "/aa", "aa.com", "", "abd/test", nil) // request uri with hostname testRequestHeaderReadSuccess(t, h, "GET http://gooGle.com/foO/%20bar?xxx#aaa HTTP/1.1\r\nHost: aa.cOM\r\n\r\ntrail", - -2, "http://gooGle.com/foO/%20bar?xxx#aaa", "aa.cOM", "", "", "trail") + -2, "http://gooGle.com/foO/%20bar?xxx#aaa", "aa.cOM", "", "", nil) // no protocol in the first line testRequestHeaderReadSuccess(t, h, "GET /foo/bar\r\nHost: google.com\r\n\r\nisdD", - -2, "/foo/bar", "google.com", "", "", "isdD") + -2, "/foo/bar", "google.com", "", "", nil) // blank lines before the first line testRequestHeaderReadSuccess(t, h, "\r\n\n\r\nGET /aaa HTTP/1.1\r\nHost: aaa.com\r\n\r\nsss", - -2, "/aaa", "aaa.com", "", "", "sss") + -2, "/aaa", "aaa.com", "", "", nil) // request uri with spaces testRequestHeaderReadSuccess(t, h, "GET /foo/ bar baz HTTP/1.1\r\nHost: aa.com\r\n\r\nxxx", - -2, "/foo/ bar baz", "aa.com", "", "", "xxx") + -2, "/foo/ bar baz", "aa.com", "", "", nil) // no host testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\nFOObar: assdfd\r\n\r\naaa", - -2, "/foo/bar", "", "", "", "aaa") + -2, "/foo/bar", "", "", "", nil) // no host, no headers testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\n\r\nfoobar", - -2, "/foo/bar", "", "", "", "foobar") + -2, "/foo/bar", "", "", "", nil) // post without content-length and content-type testRequestHeaderReadSuccess(t, h, "POST /aaa HTTP/1.1\r\nHost: aaa.com\r\n\r\nzxc", - -2, "/aaa", "aaa.com", "", "", "zxc") + -2, "/aaa", "aaa.com", "", "", nil) // post without content-type testRequestHeaderReadSuccess(t, h, "POST /abc HTTP/1.1\r\nHost: aa.com\r\nContent-Length: 123\r\n\r\npoiuy", - 123, "/abc", "aa.com", "", "", "poiuy") + 123, "/abc", "aa.com", "", "", nil) // post without content-length testRequestHeaderReadSuccess(t, h, "POST /abc HTTP/1.1\r\nHost: aa.com\r\nContent-Type: adv\r\n\r\n123456", - -2, "/abc", "aa.com", "", "adv", "123456") + -2, "/abc", "aa.com", "", "adv", nil) // invalid method testRequestHeaderReadSuccess(t, h, "POST /foo/bar HTTP/1.1\r\nHost: google.com\r\n\r\nmnbv", - -2, "/foo/bar", "google.com", "", "", "mnbv") + -2, "/foo/bar", "google.com", "", "", nil) // put request testRequestHeaderReadSuccess(t, h, "PUT /faa HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 123\r\nContent-Type: aaa\r\n\r\nxwwere", - 123, "/faa", "aaa.com", "", "aaa", "xwwere") + 123, "/faa", "aaa.com", "", "aaa", nil) } func TestResponseHeaderReadError(t *testing.T) { @@ -2462,11 +2549,19 @@ func TestResponseHeaderReadError(t *testing.T) { testResponseHeaderReadError(t, h, "HTTP/1.1 123foobar OK\r\nContent-Length: 123\r\nContent-Type: text/html\r\n\r\n") testResponseHeaderReadError(t, h, "HTTP/1.1 foobar344 OK\r\nContent-Length: 123\r\nContent-Type: text/html\r\n\r\n") + // non-numeric content-length + testResponseHeaderReadError(t, h, "HTTP/1.1 200 OK\r\nContent-Length: faaa\r\nContent-Type: text/html\r\n\r\nfoobar") + testResponseHeaderReadError(t, h, "HTTP/1.1 201 OK\r\nContent-Length: 123aa\r\nContent-Type: text/ht\r\n\r\naaa") + testResponseHeaderReadError(t, h, "HTTP/1.1 200 OK\r\nContent-Length: aa124\r\nContent-Type: html\r\n\r\nxx") + // no headers testResponseHeaderReadError(t, h, "HTTP/1.1 200 OK\r\n") // no trailing crlf testResponseHeaderReadError(t, h, "HTTP/1.1 200 OK\r\nContent-Length: 123\r\nContent-Type: text/html\r\n") + + // forbidden trailer + testResponseHeaderReadError(t, h, "HTTP/1.1 200 OK\r\nContent-Length: -1\r\nTrailer: Foo, Content-Length\r\n\r\n") } func TestResponseHeaderReadErrorSecureLog(t *testing.T) { @@ -2511,6 +2606,9 @@ func TestRequestHeaderReadError(t *testing.T) { // post with invalid content-length testRequestHeaderReadError(t, h, "POST /a HTTP/1.1\r\nHost: bb\r\nContent-Type: aa\r\nContent-Length: dff\r\n\r\nqwerty") + + // forbidden trailer + testRequestHeaderReadError(t, h, "POST /a HTTP/1.1\r\nContent-Length: -1\r\nTrailer: Foo, Content-Length\r\n\r\n") } func TestRequestHeaderReadSecuredError(t *testing.T) { @@ -2541,7 +2639,7 @@ func testResponseHeaderReadError(t *testing.T, h *ResponseHeader, headers string } // make sure response header works after error testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: foo/bar\r\nContent-Length: 12345\r\n\r\nsss", - 200, 12345, "foo/bar", "sss") + 200, 12345, "foo/bar") } func testResponseHeaderReadSecuredError(t *testing.T, h *ResponseHeader, headers string) { @@ -2556,7 +2654,7 @@ func testResponseHeaderReadSecuredError(t *testing.T, h *ResponseHeader, headers } // make sure response header works after error testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: foo/bar\r\nContent-Length: 12345\r\n\r\nsss", - 200, 12345, "foo/bar", "sss") + 200, 12345, "foo/bar") } func testRequestHeaderReadError(t *testing.T, h *RequestHeader, headers string) { @@ -2569,7 +2667,7 @@ func testRequestHeaderReadError(t *testing.T, h *RequestHeader, headers string) // make sure request header works after error testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\nHost: aaaa\r\n\r\nxxx", - -2, "/foo/bar", "aaaa", "", "", "xxx") + -2, "/foo/bar", "aaaa", "", "", nil) } func testRequestHeaderReadSecuredError(t *testing.T, h *RequestHeader, headers string) { @@ -2584,11 +2682,11 @@ func testRequestHeaderReadSecuredError(t *testing.T, h *RequestHeader, headers s } // make sure request header works after error testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\nHost: aaaa\r\n\r\nxxx", - -2, "/foo/bar", "aaaa", "", "", "xxx") + -2, "/foo/bar", "aaaa", "", "", nil) } func testResponseHeaderReadSuccess(t *testing.T, h *ResponseHeader, headers string, expectedStatusCode, expectedContentLength int, - expectedContentType, expectedTrailer string) { + expectedContentType string) { r := bytes.NewBufferString(headers) br := bufio.NewReader(r) err := h.Read(br) @@ -2596,11 +2694,10 @@ func testResponseHeaderReadSuccess(t *testing.T, h *ResponseHeader, headers stri t.Fatalf("Unexpected error when parsing response headers: %s. headers=%q", err, headers) } verifyResponseHeader(t, h, expectedStatusCode, expectedContentLength, expectedContentType) - verifyTrailer(t, br, expectedTrailer) } func testRequestHeaderReadSuccess(t *testing.T, h *RequestHeader, headers string, expectedContentLength int, - expectedRequestURI, expectedHost, expectedReferer, expectedContentType, expectedTrailer string) { + expectedRequestURI, expectedHost, expectedReferer, expectedContentType string, expectedTrailer map[string]string) { r := bytes.NewBufferString(headers) br := bufio.NewReader(r) err := h.Read(br) @@ -2608,7 +2705,6 @@ func testRequestHeaderReadSuccess(t *testing.T, h *RequestHeader, headers string t.Fatalf("Unexpected error when parsing request headers: %s. headers=%q", err, headers) } verifyRequestHeader(t, h, expectedContentLength, expectedRequestURI, expectedHost, expectedReferer, expectedContentType) - verifyTrailer(t, br, expectedTrailer) } func verifyResponseHeader(t *testing.T, h *ResponseHeader, expectedStatusCode, expectedContentLength int, expectedContentType string) { @@ -2648,12 +2744,45 @@ func verifyRequestHeader(t *testing.T, h *RequestHeader, expectedContentLength i } } -func verifyTrailer(t *testing.T, r *bufio.Reader, expectedTrailer string) { - trailer, err := ioutil.ReadAll(r) +func verifyResponseTrailer(t *testing.T, h *ResponseHeader, expectedTrailers map[string]string) { + for k, v := range expectedTrailers { + got := h.Peek(k) + if !bytes.Equal(got, []byte(v)) { + t.Fatalf("Unexpected trailer %s. Expected %s. Got %q", k, v, got) + } + } +} + +func verifyRequestTrailer(t *testing.T, h *RequestHeader, expectedTrailers map[string]string) { + for k, v := range expectedTrailers { + got := h.Peek(k) + if !bytes.Equal(got, []byte(v)) { + t.Fatalf("Unexpected trailer %s. Expected %s. Got %q", k, v, got) + } + } +} + +func verifyTrailer(t *testing.T, r *bufio.Reader, expectedTrailers map[string]string, isReq bool) { + if isReq { + req := Request{} + err := req.Header.ReadTrailer(r) + if err == io.EOF && expectedTrailers == nil { + return + } + if err != nil { + t.Fatalf("Cannot read trailer: %s", err) + } + verifyRequestTrailer(t, &req.Header, expectedTrailers) + return + } + + resp := Response{} + err := resp.Header.ReadTrailer(r) + if err == io.EOF && expectedTrailers == nil { + return + } if err != nil { t.Fatalf("Cannot read trailer: %s", err) } - if !bytes.Equal(trailer, []byte(expectedTrailer)) { - t.Fatalf("Unexpected trailer %q. Expected %q", trailer, expectedTrailer) - } + verifyResponseTrailer(t, &resp.Header, expectedTrailers) } |