aboutsummaryrefslogtreecommitdiff
path: root/header_test.go
diff options
context:
space:
mode:
authorGravatar ichx <czn16@qq.com> 2021-12-05 21:11:51 +0800
committerGravatar GitHub <noreply@github.com> 2021-12-05 14:11:51 +0100
commitda7ff7a2080953e370e8bd712f5ae8ef8b0cb4e1 (patch)
treed361832c2ab7da262ce69a646377cf3301968cca /header_test.go
parentfix: reset request after reset user values on keep-alive connections (#1162) (diff)
downloadfasthttp-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.go317
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)
}