hanayo/vendor/github.com/valyala/fasthttp/header_test.go
2019-02-23 13:29:15 +00:00

1973 lines
61 KiB
Go

package fasthttp
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"strings"
"testing"
)
func TestResponseHeaderDefaultStatusCode(t *testing.T) {
var h ResponseHeader
statusCode := h.StatusCode()
if statusCode != StatusOK {
t.Fatalf("unexpected status code: %d. Expecting %d", statusCode, StatusOK)
}
}
func TestResponseHeaderDelClientCookie(t *testing.T) {
cookieName := "foobar"
var h ResponseHeader
c := AcquireCookie()
c.SetKey(cookieName)
c.SetValue("aasdfsdaf")
h.SetCookie(c)
h.DelClientCookieBytes([]byte(cookieName))
if !h.Cookie(c) {
t.Fatalf("expecting cookie %q", c.Key())
}
if !c.Expire().Equal(CookieExpireDelete) {
t.Fatalf("unexpected cookie expiration time: %s. Expecting %s", c.Expire(), CookieExpireDelete)
}
if len(c.Value()) > 0 {
t.Fatalf("unexpected cookie value: %q. Expecting empty value", c.Value())
}
ReleaseCookie(c)
}
func TestResponseHeaderAdd(t *testing.T) {
m := make(map[string]struct{})
var h ResponseHeader
h.Add("aaa", "bbb")
m["bbb"] = struct{}{}
for i := 0; i < 10; i++ {
v := fmt.Sprintf("%d", i)
h.Add("Foo-Bar", v)
m[v] = struct{}{}
}
if h.Len() != 12 {
t.Fatalf("unexpected header len %d. Expecting 12", h.Len())
}
h.VisitAll(func(k, v []byte) {
switch string(k) {
case "Aaa", "Foo-Bar":
if _, ok := m[string(v)]; !ok {
t.Fatalf("unexpected value found %q. key %q", v, k)
}
delete(m, string(v))
case "Content-Type":
default:
t.Fatalf("unexpected key found: %q", k)
}
})
if len(m) > 0 {
t.Fatalf("%d headers are missed", len(m))
}
s := h.String()
br := bufio.NewReader(bytes.NewBufferString(s))
var h1 ResponseHeader
if err := h1.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
h.VisitAll(func(k, v []byte) {
switch string(k) {
case "Aaa", "Foo-Bar":
m[string(v)] = struct{}{}
case "Content-Type":
default:
t.Fatalf("unexpected key found: %q", k)
}
})
if len(m) != 11 {
t.Fatalf("unexpected number of headers: %d. Expecting 11", len(m))
}
}
func TestRequestHeaderAdd(t *testing.T) {
m := make(map[string]struct{})
var h RequestHeader
h.Add("aaa", "bbb")
m["bbb"] = struct{}{}
for i := 0; i < 10; i++ {
v := fmt.Sprintf("%d", i)
h.Add("Foo-Bar", v)
m[v] = struct{}{}
}
if h.Len() != 11 {
t.Fatalf("unexpected header len %d. Expecting 11", h.Len())
}
h.VisitAll(func(k, v []byte) {
switch string(k) {
case "Aaa", "Foo-Bar":
if _, ok := m[string(v)]; !ok {
t.Fatalf("unexpected value found %q. key %q", v, k)
}
delete(m, string(v))
default:
t.Fatalf("unexpected key found: %q", k)
}
})
if len(m) > 0 {
t.Fatalf("%d headers are missed", len(m))
}
s := h.String()
br := bufio.NewReader(bytes.NewBufferString(s))
var h1 RequestHeader
if err := h1.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
h.VisitAll(func(k, v []byte) {
switch string(k) {
case "Aaa", "Foo-Bar":
m[string(v)] = struct{}{}
case "User-Agent":
default:
t.Fatalf("unexpected key found: %q", k)
}
})
if len(m) != 11 {
t.Fatalf("unexpected number of headers: %d. Expecting 11", len(m))
}
s1 := h1.String()
if s != s1 {
t.Fatalf("unexpected headers %q. Expecting %q", s1, s)
}
}
func TestHasHeaderValue(t *testing.T) {
testHasHeaderValue(t, "foobar", "foobar", true)
testHasHeaderValue(t, "foobar", "foo", false)
testHasHeaderValue(t, "foobar", "bar", false)
testHasHeaderValue(t, "keep-alive, Upgrade", "keep-alive", true)
testHasHeaderValue(t, "keep-alive , Upgrade", "Upgrade", true)
testHasHeaderValue(t, "keep-alive, Upgrade", "Upgrade-foo", false)
testHasHeaderValue(t, "keep-alive, Upgrade", "Upgr", false)
testHasHeaderValue(t, "foo , bar, baz ,", "foo", true)
testHasHeaderValue(t, "foo , bar, baz ,", "bar", true)
testHasHeaderValue(t, "foo , bar, baz ,", "baz", true)
testHasHeaderValue(t, "foo , bar, baz ,", "ba", false)
testHasHeaderValue(t, "foo, ", "", true)
testHasHeaderValue(t, "foo", "", false)
}
func testHasHeaderValue(t *testing.T, s, value string, has bool) {
ok := hasHeaderValue([]byte(s), []byte(value))
if ok != has {
t.Fatalf("unexpected hasHeaderValue(%q, %q)=%v. Expecting %v", s, value, ok, has)
}
}
func TestRequestHeaderDel(t *testing.T) {
var h RequestHeader
h.Set("Foo-Bar", "baz")
h.Set("aaa", "bbb")
h.Set("Connection", "keep-alive")
h.Set("Content-Type", "aaa")
h.Set("Host", "aaabbb")
h.Set("User-Agent", "asdfas")
h.Set("Content-Length", "1123")
h.Set("Cookie", "foobar=baz")
h.Del("foo-bar")
h.Del("connection")
h.DelBytes([]byte("content-type"))
h.Del("Host")
h.Del("user-agent")
h.Del("content-length")
h.Del("cookie")
hv := h.Peek("aaa")
if string(hv) != "bbb" {
t.Fatalf("unexpected header value: %q. Expecting %q", hv, "bbb")
}
hv = h.Peek("Foo-Bar")
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
hv = h.Peek("Connection")
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
hv = h.Peek("Content-Type")
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
hv = h.Peek("Host")
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
hv = h.Peek("User-Agent")
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
hv = h.Peek("Content-Length")
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
hv = h.Peek("Cookie")
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
cv := h.Cookie("foobar")
if len(cv) > 0 {
t.Fatalf("unexpected cookie obtianed: %q", cv)
}
if h.ContentLength() != 0 {
t.Fatalf("unexpected content-length: %d. Expecting 0", h.ContentLength())
}
}
func TestResponseHeaderDel(t *testing.T) {
var h ResponseHeader
h.Set("Foo-Bar", "baz")
h.Set("aaa", "bbb")
h.Set("Connection", "keep-alive")
h.Set("Content-Type", "aaa")
h.Set("Server", "aaabbb")
h.Set("Content-Length", "1123")
var c Cookie
c.SetKey("foo")
c.SetValue("bar")
h.SetCookie(&c)
h.Del("foo-bar")
h.Del("connection")
h.DelBytes([]byte("content-type"))
h.Del("Server")
h.Del("content-length")
h.Del("set-cookie")
hv := h.Peek("aaa")
if string(hv) != "bbb" {
t.Fatalf("unexpected header value: %q. Expecting %q", hv, "bbb")
}
hv = h.Peek("Foo-Bar")
if len(hv) > 0 {
t.Fatalf("non-zero header value: %q", hv)
}
hv = h.Peek("Connection")
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
hv = h.Peek("Content-Type")
if string(hv) != string(defaultContentType) {
t.Fatalf("unexpected content-type: %q. Expecting %q", hv, defaultContentType)
}
hv = h.Peek("Server")
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
hv = h.Peek("Content-Length")
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
if h.Cookie(&c) {
t.Fatalf("unexpected cookie obtianed: %q", &c)
}
if h.ContentLength() != 0 {
t.Fatalf("unexpected content-length: %d. Expecting 0", h.ContentLength())
}
}
func TestAppendNormalizedHeaderKeyBytes(t *testing.T) {
testAppendNormalizedHeaderKeyBytes(t, "", "")
testAppendNormalizedHeaderKeyBytes(t, "Content-Type", "Content-Type")
testAppendNormalizedHeaderKeyBytes(t, "foO-bAr-BAZ", "Foo-Bar-Baz")
}
func testAppendNormalizedHeaderKeyBytes(t *testing.T, key, expectedKey string) {
buf := []byte("foobar")
result := AppendNormalizedHeaderKeyBytes(buf, []byte(key))
normalizedKey := result[len(buf):]
if string(normalizedKey) != expectedKey {
t.Fatalf("unexpected normalized key %q. Expecting %q", normalizedKey, expectedKey)
}
}
func TestRequestHeaderHTTP10ConnectionClose(t *testing.T) {
s := "GET / HTTP/1.0\r\nHost: foobar\r\n\r\n"
var h RequestHeader
br := bufio.NewReader(bytes.NewBufferString(s))
if err := h.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !h.connectionCloseFast() {
t.Fatalf("expecting 'Connection: close' request header")
}
if !h.ConnectionClose() {
t.Fatalf("expecting 'Connection: close' request header")
}
}
func TestRequestHeaderHTTP10ConnectionKeepAlive(t *testing.T) {
s := "GET / HTTP/1.0\r\nHost: foobar\r\nConnection: keep-alive\r\n\r\n"
var h RequestHeader
br := bufio.NewReader(bytes.NewBufferString(s))
if err := h.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if h.ConnectionClose() {
t.Fatalf("unexpected 'Connection: close' request header")
}
}
func TestBufferStartEnd(t *testing.T) {
testBufferStartEnd(t, "", "", "")
testBufferStartEnd(t, "foobar", "foobar", "")
b := string(createFixedBody(199))
testBufferStartEnd(t, b, b, "")
for i := 0; i < 10; i++ {
b += "foobar"
testBufferStartEnd(t, b, b, "")
}
b = string(createFixedBody(400))
testBufferStartEnd(t, b, b, "")
for i := 0; i < 10; i++ {
b += "sadfqwer"
testBufferStartEnd(t, b, b[:200], b[len(b)-200:])
}
}
func testBufferStartEnd(t *testing.T, buf, expectedStart, expectedEnd string) {
start, end := bufferStartEnd([]byte(buf))
if string(start) != expectedStart {
t.Fatalf("unexpected start %q. Expecting %q. buf %q", start, expectedStart, buf)
}
if string(end) != expectedEnd {
t.Fatalf("unexpected end %q. Expecting %q. buf %q", end, expectedEnd, buf)
}
}
func TestResponseHeaderTrailingCRLFSuccess(t *testing.T) {
trailingCRLF := "\r\n\r\n\r\n"
s := "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 123\r\n\r\n" + trailingCRLF
var r ResponseHeader
br := bufio.NewReader(bytes.NewBufferString(s))
if err := r.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
// try reading the trailing CRLF. It must return EOF
err := r.Read(br)
if err == nil {
t.Fatalf("expecting error")
}
if err != io.EOF {
t.Fatalf("unexpected error: %s. Expecting %s", err, io.EOF)
}
}
func TestResponseHeaderTrailingCRLFError(t *testing.T) {
trailingCRLF := "\r\nerror\r\n\r\n"
s := "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 123\r\n\r\n" + trailingCRLF
var r ResponseHeader
br := bufio.NewReader(bytes.NewBufferString(s))
if err := r.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
// try reading the trailing CRLF. It must return EOF
err := r.Read(br)
if err == nil {
t.Fatalf("expecting error")
}
if err == io.EOF {
t.Fatalf("unexpected error: %s", err)
}
}
func TestRequestHeaderTrailingCRLFSuccess(t *testing.T) {
trailingCRLF := "\r\n\r\n\r\n"
s := "GET / HTTP/1.1\r\nHost: aaa.com\r\n\r\n" + trailingCRLF
var r RequestHeader
br := bufio.NewReader(bytes.NewBufferString(s))
if err := r.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
// try reading the trailing CRLF. It must return EOF
err := r.Read(br)
if err == nil {
t.Fatalf("expecting error")
}
if err != io.EOF {
t.Fatalf("unexpected error: %s. Expecting %s", err, io.EOF)
}
}
func TestRequestHeaderTrailingCRLFError(t *testing.T) {
trailingCRLF := "\r\nerror\r\n\r\n"
s := "GET / HTTP/1.1\r\nHost: aaa.com\r\n\r\n" + trailingCRLF
var r RequestHeader
br := bufio.NewReader(bytes.NewBufferString(s))
if err := r.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
// try reading the trailing CRLF. It must return EOF
err := r.Read(br)
if err == nil {
t.Fatalf("expecting error")
}
if err == io.EOF {
t.Fatalf("unexpected error: %s", err)
}
}
func TestRequestHeaderReadEOF(t *testing.T) {
var r RequestHeader
br := bufio.NewReader(&bytes.Buffer{})
err := r.Read(br)
if err == nil {
t.Fatalf("expecting error")
}
if err != io.EOF {
t.Fatalf("unexpected error: %s. Expecting %s", err, io.EOF)
}
// incomplete request header mustn't return io.EOF
br = bufio.NewReader(bytes.NewBufferString("GET "))
err = r.Read(br)
if err == nil {
t.Fatalf("expecting error")
}
if err == io.EOF {
t.Fatalf("expecting non-EOF error")
}
}
func TestResponseHeaderReadEOF(t *testing.T) {
var r ResponseHeader
br := bufio.NewReader(&bytes.Buffer{})
err := r.Read(br)
if err == nil {
t.Fatalf("expecting error")
}
if err != io.EOF {
t.Fatalf("unexpected error: %s. Expecting %s", err, io.EOF)
}
// incomplete response header mustn't return io.EOF
br = bufio.NewReader(bytes.NewBufferString("HTTP/1.1 "))
err = r.Read(br)
if err == nil {
t.Fatalf("expecting error")
}
if err == io.EOF {
t.Fatalf("expecting non-EOF error")
}
}
func TestResponseHeaderOldVersion(t *testing.T) {
var h ResponseHeader
s := "HTTP/1.0 200 OK\r\nContent-Length: 5\r\nContent-Type: aaa\r\n\r\n12345"
s += "HTTP/1.0 200 OK\r\nContent-Length: 2\r\nContent-Type: ass\r\nConnection: keep-alive\r\n\r\n42"
br := bufio.NewReader(bytes.NewBufferString(s))
if err := h.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !h.ConnectionClose() {
t.Fatalf("expecting 'Connection: close' for the response with old http protocol")
}
if err := h.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if h.ConnectionClose() {
t.Fatalf("unexpected 'Connection: close' for keep-alive response with old http protocol")
}
}
func TestRequestHeaderSetByteRange(t *testing.T) {
testRequestHeaderSetByteRange(t, 0, 10, "bytes=0-10")
testRequestHeaderSetByteRange(t, 123, -1, "bytes=123-")
testRequestHeaderSetByteRange(t, -234, 58349, "bytes=-234")
}
func testRequestHeaderSetByteRange(t *testing.T, startPos, endPos int, expectedV string) {
var h RequestHeader
h.SetByteRange(startPos, endPos)
v := h.Peek("Range")
if string(v) != expectedV {
t.Fatalf("unexpected range: %q. Expecting %q. startPos=%d, endPos=%d", v, expectedV, startPos, endPos)
}
}
func TestResponseHeaderSetContentRange(t *testing.T) {
testResponseHeaderSetContentRange(t, 0, 0, 1, "bytes 0-0/1")
testResponseHeaderSetContentRange(t, 123, 456, 789, "bytes 123-456/789")
}
func testResponseHeaderSetContentRange(t *testing.T, startPos, endPos, contentLength int, expectedV string) {
var h ResponseHeader
h.SetContentRange(startPos, endPos, contentLength)
v := h.Peek("Content-Range")
if string(v) != expectedV {
t.Fatalf("unexpected content-range: %q. Expecting %q. startPos=%d, endPos=%d, contentLength=%d",
v, expectedV, startPos, endPos, contentLength)
}
}
func TestRequestHeaderHasAcceptEncoding(t *testing.T) {
testRequestHeaderHasAcceptEncoding(t, "", "gzip", false)
testRequestHeaderHasAcceptEncoding(t, "gzip", "sdhc", false)
testRequestHeaderHasAcceptEncoding(t, "deflate", "deflate", true)
testRequestHeaderHasAcceptEncoding(t, "gzip, deflate, sdhc", "gzi", false)
testRequestHeaderHasAcceptEncoding(t, "gzip, deflate, sdhc", "dhc", false)
testRequestHeaderHasAcceptEncoding(t, "gzip, deflate, sdhc", "sdh", false)
testRequestHeaderHasAcceptEncoding(t, "gzip, deflate, sdhc", "zip", false)
testRequestHeaderHasAcceptEncoding(t, "gzip, deflate, sdhc", "flat", false)
testRequestHeaderHasAcceptEncoding(t, "gzip, deflate, sdhc", "flate", false)
testRequestHeaderHasAcceptEncoding(t, "gzip, deflate, sdhc", "def", false)
testRequestHeaderHasAcceptEncoding(t, "gzip, deflate, sdhc", "gzip", true)
testRequestHeaderHasAcceptEncoding(t, "gzip, deflate, sdhc", "deflate", true)
testRequestHeaderHasAcceptEncoding(t, "gzip, deflate, sdhc", "sdhc", true)
}
func testRequestHeaderHasAcceptEncoding(t *testing.T, ae, v string, resultExpected bool) {
var h RequestHeader
h.Set("Accept-Encoding", ae)
result := h.HasAcceptEncoding(v)
if result != resultExpected {
t.Fatalf("unexpected result in HasAcceptEncoding(%q, %q): %v. Expecting %v", ae, v, result, resultExpected)
}
}
func TestRequestMultipartFormBoundary(t *testing.T) {
testRequestMultipartFormBoundary(t, "POST / HTTP/1.1\r\nContent-Type: multipart/form-data; boundary=foobar\r\n\r\n", "foobar")
// incorrect content-type
testRequestMultipartFormBoundary(t, "POST / HTTP/1.1\r\nContent-Type: foo/bar\r\n\r\n", "")
// empty boundary
testRequestMultipartFormBoundary(t, "POST / HTTP/1.1\r\nContent-Type: multipart/form-data; boundary=\r\n\r\n", "")
// missing boundary
testRequestMultipartFormBoundary(t, "POST / HTTP/1.1\r\nContent-Type: multipart/form-data\r\n\r\n", "")
// boundary after other content-type params
testRequestMultipartFormBoundary(t, "POST / HTTP/1.1\r\nContent-Type: multipart/form-data; foo=bar; boundary=--aaabb \r\n\r\n", "--aaabb")
var h RequestHeader
h.SetMultipartFormBoundary("foobarbaz")
b := h.MultipartFormBoundary()
if string(b) != "foobarbaz" {
t.Fatalf("unexpected boundary %q. Expecting %q", b, "foobarbaz")
}
}
func testRequestMultipartFormBoundary(t *testing.T, s, boundary string) {
var h RequestHeader
r := bytes.NewBufferString(s)
br := bufio.NewReader(r)
if err := h.Read(br); err != nil {
t.Fatalf("unexpected error: %s. s=%q, boundary=%q", err, s, boundary)
}
b := h.MultipartFormBoundary()
if string(b) != boundary {
t.Fatalf("unexpected boundary %q. Expecting %q. s=%q", b, boundary, s)
}
}
func TestResponseHeaderConnectionUpgrade(t *testing.T) {
testResponseHeaderConnectionUpgrade(t, "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nConnection: Upgrade, HTTP2-Settings\r\n\r\n",
true, true)
testResponseHeaderConnectionUpgrade(t, "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nConnection: keep-alive, Upgrade\r\n\r\n",
true, true)
// non-http/1.1 protocol has 'connection: close' by default, which also disables 'connection: upgrade'
testResponseHeaderConnectionUpgrade(t, "HTTP/1.0 200 OK\r\nContent-Length: 10\r\nConnection: Upgrade, HTTP2-Settings\r\n\r\n",
false, false)
// explicit keep-alive for non-http/1.1, so 'connection: upgrade' works
testResponseHeaderConnectionUpgrade(t, "HTTP/1.0 200 OK\r\nContent-Length: 10\r\nConnection: Upgrade, keep-alive\r\n\r\n",
true, true)
// implicit keep-alive for http/1.1
testResponseHeaderConnectionUpgrade(t, "HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\n", false, true)
// no content-length, so 'connection: close' is assumed
testResponseHeaderConnectionUpgrade(t, "HTTP/1.1 200 OK\r\n\r\n", false, false)
}
func testResponseHeaderConnectionUpgrade(t *testing.T, s string, isUpgrade, isKeepAlive 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 header %q", err, s)
}
upgrade := h.ConnectionUpgrade()
if upgrade != isUpgrade {
t.Fatalf("unexpected 'connection: upgrade' when parsing response header: %v. Expecting %v. header %q. v=%q",
upgrade, isUpgrade, s, h.Peek("Connection"))
}
keepAlive := !h.ConnectionClose()
if keepAlive != isKeepAlive {
t.Fatalf("unexpected 'connection: keep-alive' when parsing response header: %v. Expecting %v. header %q. v=%q",
keepAlive, isKeepAlive, s, &h)
}
}
func TestRequestHeaderConnectionUpgrade(t *testing.T) {
testRequestHeaderConnectionUpgrade(t, "GET /foobar HTTP/1.1\r\nConnection: Upgrade, HTTP2-Settings\r\nHost: foobar.com\r\n\r\n",
true, true)
testRequestHeaderConnectionUpgrade(t, "GET /foobar HTTP/1.1\r\nConnection: keep-alive,Upgrade\r\nHost: foobar.com\r\n\r\n",
true, true)
// non-http/1.1 has 'connection: close' by default, which resets 'connection: upgrade'
testRequestHeaderConnectionUpgrade(t, "GET /foobar HTTP/1.0\r\nConnection: Upgrade, HTTP2-Settings\r\nHost: foobar.com\r\n\r\n",
false, false)
// explicit 'connection: keep-alive' in non-http/1.1
testRequestHeaderConnectionUpgrade(t, "GET /foobar HTTP/1.0\r\nConnection: foo, Upgrade, keep-alive\r\nHost: foobar.com\r\n\r\n",
true, true)
// no upgrade
testRequestHeaderConnectionUpgrade(t, "GET /foobar HTTP/1.1\r\nConnection: Upgradess, foobar\r\nHost: foobar.com\r\n\r\n",
false, true)
testRequestHeaderConnectionUpgrade(t, "GET /foobar HTTP/1.1\r\nHost: foobar.com\r\n\r\n",
false, true)
// explicit connection close
testRequestHeaderConnectionUpgrade(t, "GET /foobar HTTP/1.1\r\nConnection: close\r\nHost: foobar.com\r\n\r\n",
false, false)
}
func testRequestHeaderConnectionUpgrade(t *testing.T, s string, isUpgrade, isKeepAlive 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 header %q", err, s)
}
upgrade := h.ConnectionUpgrade()
if upgrade != isUpgrade {
t.Fatalf("unexpected 'connection: upgrade' when parsing request header: %v. Expecting %v. header %q",
upgrade, isUpgrade, s)
}
keepAlive := !h.ConnectionClose()
if keepAlive != isKeepAlive {
t.Fatalf("unexpected 'connection: keep-alive' when parsing request header: %v. Expecting %v. header %q",
keepAlive, isKeepAlive, s)
}
}
func TestRequestHeaderProxyWithCookie(t *testing.T) {
// Proxy request header (read it, then write it without touching any headers).
var h RequestHeader
r := bytes.NewBufferString("GET /foo HTTP/1.1\r\nFoo: bar\r\nHost: aaa.com\r\nCookie: foo=bar; bazzz=aaaaaaa; x=y\r\nCookie: aqqqqq=123\r\n\r\n")
br := bufio.NewReader(r)
if err := h.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
w := &bytes.Buffer{}
bw := bufio.NewWriter(w)
if err := h.Write(bw); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if err := bw.Flush(); err != nil {
t.Fatalf("unexpected error: %s", err)
}
var h1 RequestHeader
br.Reset(w)
if err := h1.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if string(h1.RequestURI()) != "/foo" {
t.Fatalf("unexpected requestURI: %q. Expecting %q", h1.RequestURI(), "/foo")
}
if string(h1.Host()) != "aaa.com" {
t.Fatalf("unexpected host: %q. Expecting %q", h1.Host(), "aaa.com")
}
if string(h1.Peek("Foo")) != "bar" {
t.Fatalf("unexpected Foo: %q. Expecting %q", h1.Peek("Foo"), "bar")
}
if string(h1.Cookie("foo")) != "bar" {
t.Fatalf("unexpected coookie foo=%q. Expecting %q", h1.Cookie("foo"), "bar")
}
if string(h1.Cookie("bazzz")) != "aaaaaaa" {
t.Fatalf("unexpected cookie bazzz=%q. Expecting %q", h1.Cookie("bazzz"), "aaaaaaa")
}
if string(h1.Cookie("x")) != "y" {
t.Fatalf("unexpected cookie x=%q. Expecting %q", h1.Cookie("x"), "y")
}
if string(h1.Cookie("aqqqqq")) != "123" {
t.Fatalf("unexpected cookie aqqqqq=%q. Expecting %q", h1.Cookie("aqqqqq"), "123")
}
}
func TestPeekRawHeader(t *testing.T) {
// empty header
testPeekRawHeader(t, "", "Foo-Bar", "")
// different case
testPeekRawHeader(t, "Content-Length: 3443\r\n", "content-length", "")
// no trailing crlf
testPeekRawHeader(t, "Content-Length: 234", "Content-Length", "")
// single header
testPeekRawHeader(t, "Content-Length: 12345\r\n", "Content-Length", "12345")
// multiple headers
testPeekRawHeader(t, "Host: foobar\r\nContent-Length: 434\r\nFoo: bar\r\n\r\n", "Content-Length", "434")
// lf without cr
testPeekRawHeader(t, "Foo: bar\nConnection: close\nAaa: bbb\ncc: ddd\n", "Connection", "close")
}
func testPeekRawHeader(t *testing.T, rawHeaders, key string, expectedValue string) {
v := peekRawHeader([]byte(rawHeaders), []byte(key))
if string(v) != expectedValue {
t.Fatalf("unexpected raw headers value %q. Expected %q. key %q, rawHeaders %q", v, expectedValue, key, rawHeaders)
}
}
func TestResponseHeaderFirstByteReadEOF(t *testing.T) {
var h ResponseHeader
r := &errorReader{fmt.Errorf("non-eof error")}
br := bufio.NewReader(r)
err := h.Read(br)
if err == nil {
t.Fatalf("expecting error")
}
if err != io.EOF {
t.Fatalf("unexpected error %s. Expecting %s", err, io.EOF)
}
}
func TestRequestHeaderFirstByteReadEOF(t *testing.T) {
var h RequestHeader
r := &errorReader{fmt.Errorf("non-eof error")}
br := bufio.NewReader(r)
err := h.Read(br)
if err == nil {
t.Fatalf("expecting error")
}
if err != io.EOF {
t.Fatalf("unexpected error %s. Expecting %s", err, io.EOF)
}
}
type errorReader struct {
err error
}
func (r *errorReader) Read(p []byte) (int, error) {
return 0, r.err
}
func TestRequestHeaderEmptyMethod(t *testing.T) {
var h RequestHeader
if !h.IsGet() {
t.Fatalf("empty method must be equivalent to GET")
}
if h.IsPost() {
t.Fatalf("empty method cannot be POST")
}
if h.IsHead() {
t.Fatalf("empty method cannot be HEAD")
}
if h.IsDelete() {
t.Fatalf("empty method cannot be DELETE")
}
}
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
h.Set("Set-Cookie", "foo=bar")
h.Set("Content-Type", "foobar")
h.Set("AAA-BBB", "aaaa")
var h1 ResponseHeader
h.CopyTo(&h1)
if !bytes.Equal(h1.Peek("Set-cookie"), h.Peek("Set-Cookie")) {
t.Fatalf("unexpected cookie %q. Expected %q", h1.Peek("set-cookie"), h.Peek("set-cookie"))
}
if !bytes.Equal(h1.Peek("Content-Type"), h.Peek("Content-Type")) {
t.Fatalf("unexpected content-type %q. Expected %q", h1.Peek("content-type"), h.Peek("content-type"))
}
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"))
}
}
func TestRequestHeaderCopyTo(t *testing.T) {
var h RequestHeader
h.Set("Cookie", "aa=bb; cc=dd")
h.Set("Content-Type", "foobar")
h.Set("Host", "aaaa")
h.Set("aaaxxx", "123")
var h1 RequestHeader
h.CopyTo(&h1)
if !bytes.Equal(h1.Peek("cookie"), h.Peek("Cookie")) {
t.Fatalf("unexpected cookie after copying: %q. Expected %q", h1.Peek("cookie"), h.Peek("cookie"))
}
if !bytes.Equal(h1.Peek("content-type"), h.Peek("Content-Type")) {
t.Fatalf("unexpected content-type %q. Expected %q", h1.Peek("content-type"), h.Peek("content-type"))
}
if !bytes.Equal(h1.Peek("host"), h.Peek("host")) {
t.Fatalf("unexpected host %q. Expected %q", h1.Peek("host"), h.Peek("host"))
}
if !bytes.Equal(h1.Peek("aaaxxx"), h.Peek("aaaxxx")) {
t.Fatalf("unexpected aaaxxx %q. Expected %q", h1.Peek("aaaxxx"), h.Peek("aaaxxx"))
}
}
func TestRequestHeaderConnectionClose(t *testing.T) {
var h RequestHeader
h.Set("Connection", "close")
h.Set("Host", "foobar")
if !h.ConnectionClose() {
t.Fatalf("connection: close not set")
}
var w bytes.Buffer
bw := bufio.NewWriter(&w)
if err := h.Write(bw); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if err := bw.Flush(); err != nil {
t.Fatalf("unexpected error: %s", err)
}
var h1 RequestHeader
br := bufio.NewReader(&w)
if err := h1.Read(br); err != nil {
t.Fatalf("error when reading request header: %s", err)
}
if !h1.ConnectionClose() {
t.Fatalf("unexpected connection: close value: %v", h1.ConnectionClose())
}
if string(h1.Peek("Connection")) != "close" {
t.Fatalf("unexpected connection value: %q. Expecting %q", h.Peek("Connection"), "close")
}
}
func TestRequestHeaderSetCookie(t *testing.T) {
var h RequestHeader
h.Set("Cookie", "foo=bar; baz=aaa")
h.Set("cOOkie", "xx=yyy")
if string(h.Cookie("foo")) != "bar" {
t.Fatalf("Unexpected cookie %q. Expecting %q", h.Cookie("foo"), "bar")
}
if string(h.Cookie("baz")) != "aaa" {
t.Fatalf("Unexpected cookie %q. Expecting %q", h.Cookie("baz"), "aaa")
}
if string(h.Cookie("xx")) != "yyy" {
t.Fatalf("unexpected cookie %q. Expecting %q", h.Cookie("xx"), "yyy")
}
}
func TestResponseHeaderSetCookie(t *testing.T) {
var h ResponseHeader
h.Set("set-cookie", "foo=bar; path=/aa/bb; domain=aaa.com")
h.Set("Set-Cookie", "aaaaa=bxx")
var c Cookie
c.SetKey("foo")
if !h.Cookie(&c) {
t.Fatalf("cannot obtain %q cookie", c.Key())
}
if string(c.Value()) != "bar" {
t.Fatalf("unexpected cookie value %q. Expected %q", c.Value(), "bar")
}
if string(c.Path()) != "/aa/bb" {
t.Fatalf("unexpected cookie path %q. Expected %q", c.Path(), "/aa/bb")
}
if string(c.Domain()) != "aaa.com" {
t.Fatalf("unexpected cookie domain %q. Expected %q", c.Domain(), "aaa.com")
}
c.SetKey("aaaaa")
if !h.Cookie(&c) {
t.Fatalf("cannot obtain %q cookie", c.Key())
}
if string(c.Value()) != "bxx" {
t.Fatalf("unexpected cookie value %q. Expecting %q", c.Value(), "bxx")
}
}
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")
br := bufio.NewReader(r)
if err := h.Read(br); err != nil {
t.Fatalf("Unepxected error: %s", err)
}
if h.Len() != 4 {
t.Fatalf("Unexpected number of headers: %d. Expected 4", h.Len())
}
contentLengthCount := 0
contentTypeCount := 0
cookieCount := 0
h.VisitAll(func(key, value []byte) {
k := string(key)
v := string(value)
switch k {
case "Content-Length":
if v != string(h.Peek(k)) {
t.Fatalf("unexpected content-length: %q. Expecting %q", v, h.Peek(k))
}
contentLengthCount++
case "Content-Type":
if v != string(h.Peek(k)) {
t.Fatalf("Unexpected content-type: %q. Expected %q", v, h.Peek(k))
}
contentTypeCount++
case "Set-Cookie":
if cookieCount == 0 && v != "aa=bb; path=/foo/bar" {
t.Fatalf("unexpected cookie header: %q. Expected %q", v, "aa=bb; path=/foo/bar")
}
if cookieCount == 1 && v != "ccc" {
t.Fatalf("unexpected cookie header: %q. Expected %q", v, "ccc")
}
cookieCount++
default:
t.Fatalf("unexpected header %q=%q", k, v)
}
})
if contentLengthCount != 1 {
t.Fatalf("unexpected number of content-length headers: %d. Expected 1", contentLengthCount)
}
if contentTypeCount != 1 {
t.Fatalf("unexpected number of content-type headers: %d. Expected 1", contentTypeCount)
}
if cookieCount != 2 {
t.Fatalf("unexpected number of cookie header: %d. Expected 2", cookieCount)
}
}
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")
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())
}
hostCount := 0
xxCount := 0
cookieCount := 0
h.VisitAll(func(key, value []byte) {
k := string(key)
v := string(value)
switch k {
case "Host":
if v != string(h.Peek(k)) {
t.Fatalf("Unexpected host value %q. Expected %q", v, h.Peek(k))
}
hostCount++
case "Xx":
if xxCount == 0 && v != "YYY" {
t.Fatalf("Unexpected value %q. Expected %q", v, "YYY")
}
if xxCount == 1 && v != "ZZ" {
t.Fatalf("Unexpected value %q. Expected %q", v, "ZZ")
}
xxCount++
case "Cookie":
if v != "a=b; c=d" {
t.Fatalf("Unexpected cookie %q. Expected %q", v, "a=b; c=d")
}
cookieCount++
default:
t.Fatalf("Unepxected header %q=%q", k, v)
}
})
if hostCount != 1 {
t.Fatalf("Unepxected number of host headers detected %d. Expected 1", hostCount)
}
if xxCount != 2 {
t.Fatalf("Unexpected number of xx headers detected %d. Expected 2", xxCount)
}
if cookieCount != 1 {
t.Fatalf("Unexpected number of cookie headers %d. Expected 1", cookieCount)
}
}
func TestResponseHeaderCookie(t *testing.T) {
var h ResponseHeader
var c Cookie
c.SetKey("foobar")
c.SetValue("aaa")
h.SetCookie(&c)
c.SetKey("йцук")
c.SetDomain("foobar.com")
h.SetCookie(&c)
c.Reset()
c.SetKey("foobar")
if !h.Cookie(&c) {
t.Fatalf("Cannot find cookie %q", c.Key())
}
var expectedC1 Cookie
expectedC1.SetKey("foobar")
expectedC1.SetValue("aaa")
if !equalCookie(&expectedC1, &c) {
t.Fatalf("unexpected cookie\n%#v\nExpected\n%#v\n", &c, &expectedC1)
}
c.SetKey("йцук")
if !h.Cookie(&c) {
t.Fatalf("cannot find cookie %q", c.Key())
}
var expectedC2 Cookie
expectedC2.SetKey("йцук")
expectedC2.SetValue("aaa")
expectedC2.SetDomain("foobar.com")
if !equalCookie(&expectedC2, &c) {
t.Fatalf("unexpected cookie\n%v\nExpected\n%v\n", &c, &expectedC2)
}
h.VisitAllCookie(func(key, value []byte) {
var cc Cookie
cc.ParseBytes(value)
if !bytes.Equal(key, cc.Key()) {
t.Fatalf("Unexpected cookie key %q. Expected %q", key, cc.Key())
}
switch {
case bytes.Equal(key, []byte("foobar")):
if !equalCookie(&expectedC1, &cc) {
t.Fatalf("unexpected cookie\n%v\nExpected\n%v\n", &cc, &expectedC1)
}
case bytes.Equal(key, []byte("йцук")):
if !equalCookie(&expectedC2, &cc) {
t.Fatalf("unexpected cookie\n%v\nExpected\n%v\n", &cc, &expectedC2)
}
default:
t.Fatalf("unexpected cookie key %q", key)
}
})
w := &bytes.Buffer{}
bw := bufio.NewWriter(w)
if err := h.Write(bw); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if err := bw.Flush(); err != nil {
t.Fatalf("unexpected error: %s", err)
}
h.DelAllCookies()
var h1 ResponseHeader
br := bufio.NewReader(w)
if err := h1.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
c.SetKey("foobar")
if !h1.Cookie(&c) {
t.Fatalf("Cannot find cookie %q", c.Key())
}
if !equalCookie(&expectedC1, &c) {
t.Fatalf("unexpected cookie\n%v\nExpected\n%v\n", &c, &expectedC1)
}
h1.DelCookie("foobar")
if h.Cookie(&c) {
t.Fatalf("Unexpected cookie found: %v", &c)
}
if h1.Cookie(&c) {
t.Fatalf("Unexpected cookie found: %v", &c)
}
c.SetKey("йцук")
if !h1.Cookie(&c) {
t.Fatalf("cannot find cookie %q", c.Key())
}
if !equalCookie(&expectedC2, &c) {
t.Fatalf("unexpected cookie\n%v\nExpected\n%v\n", &c, &expectedC2)
}
h1.DelCookie("йцук")
if h.Cookie(&c) {
t.Fatalf("Unexpected cookie found: %v", &c)
}
if h1.Cookie(&c) {
t.Fatalf("Unexpected cookie found: %v", &c)
}
}
func equalCookie(c1, c2 *Cookie) bool {
if !bytes.Equal(c1.Key(), c2.Key()) {
return false
}
if !bytes.Equal(c1.Value(), c2.Value()) {
return false
}
if !c1.Expire().Equal(c2.Expire()) {
return false
}
if !bytes.Equal(c1.Domain(), c2.Domain()) {
return false
}
if !bytes.Equal(c1.Path(), c2.Path()) {
return false
}
return true
}
func TestRequestHeaderCookie(t *testing.T) {
var h RequestHeader
h.SetRequestURI("/foobar")
h.Set("Host", "foobar.com")
h.SetCookie("foo", "bar")
h.SetCookie("привет", "мир")
if string(h.Cookie("foo")) != "bar" {
t.Fatalf("Unexpected cookie value %q. Exepcted %q", h.Cookie("foo"), "bar")
}
if string(h.Cookie("привет")) != "мир" {
t.Fatalf("Unexpected cookie value %q. Expected %q", h.Cookie("привет"), "мир")
}
w := &bytes.Buffer{}
bw := bufio.NewWriter(w)
if err := h.Write(bw); err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if err := bw.Flush(); err != nil {
t.Fatalf("Unexpected error: %s", err)
}
var h1 RequestHeader
br := bufio.NewReader(w)
if err := h1.Read(br); err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if !bytes.Equal(h1.Cookie("foo"), h.Cookie("foo")) {
t.Fatalf("Unexpected cookie value %q. Exepcted %q", h1.Cookie("foo"), h.Cookie("foo"))
}
h1.DelCookie("foo")
if len(h1.Cookie("foo")) > 0 {
t.Fatalf("Unexpected cookie found: %q", h1.Cookie("foo"))
}
if !bytes.Equal(h1.Cookie("привет"), h.Cookie("привет")) {
t.Fatalf("Unexpected cookie value %q. Expected %q", h1.Cookie("привет"), h.Cookie("привет"))
}
h1.DelCookie("привет")
if len(h1.Cookie("привет")) > 0 {
t.Fatalf("Unexpected cookie found: %q", h1.Cookie("привет"))
}
h.DelAllCookies()
if len(h.Cookie("foo")) > 0 {
t.Fatalf("Unexpected cookie found: %q", h.Cookie("foo"))
}
if len(h.Cookie("привет")) > 0 {
t.Fatalf("Unexpected cookie found: %q", h.Cookie("привет"))
}
}
func TestRequestHeaderMethod(t *testing.T) {
// common http methods
testRequestHeaderMethod(t, "GET")
testRequestHeaderMethod(t, "POST")
testRequestHeaderMethod(t, "HEAD")
testRequestHeaderMethod(t, "DELETE")
// non-http methods
testRequestHeaderMethod(t, "foobar")
testRequestHeaderMethod(t, "ABC")
}
func testRequestHeaderMethod(t *testing.T, expectedMethod string) {
var h RequestHeader
h.SetMethod(expectedMethod)
m := h.Method()
if string(m) != expectedMethod {
t.Fatalf("unexpected method: %q. Expecting %q", m, expectedMethod)
}
s := h.String()
var h1 RequestHeader
br := bufio.NewReader(bytes.NewBufferString(s))
if err := h1.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
m1 := h1.Method()
if string(m) != string(m1) {
t.Fatalf("unexpected method: %q. Expecting %q", m, m1)
}
}
func TestRequestHeaderSetGet(t *testing.T) {
h := &RequestHeader{}
h.SetRequestURI("/aa/bbb")
h.SetMethod("POST")
h.Set("foo", "bar")
h.Set("host", "12345")
h.Set("content-type", "aaa/bbb")
h.Set("content-length", "1234")
h.Set("user-agent", "aaabbb")
h.Set("referer", "axcv")
h.Set("baz", "xxxxx")
h.Set("transfer-encoding", "chunked")
h.Set("connection", "close")
expectRequestHeaderGet(t, h, "Foo", "bar")
expectRequestHeaderGet(t, h, "Host", "12345")
expectRequestHeaderGet(t, h, "Content-Type", "aaa/bbb")
expectRequestHeaderGet(t, h, "Content-Length", "1234")
expectRequestHeaderGet(t, h, "USER-AGent", "aaabbb")
expectRequestHeaderGet(t, h, "Referer", "axcv")
expectRequestHeaderGet(t, h, "baz", "xxxxx")
expectRequestHeaderGet(t, h, "Transfer-Encoding", "")
expectRequestHeaderGet(t, h, "connecTION", "close")
if !h.ConnectionClose() {
t.Fatalf("unset connection: close")
}
if h.ContentLength() != 1234 {
t.Fatalf("Unexpected content-length %d. Expected %d", h.ContentLength(), 1234)
}
w := &bytes.Buffer{}
bw := bufio.NewWriter(w)
err := h.Write(bw)
if err != nil {
t.Fatalf("Unexpected error when writing request header: %s", err)
}
if err := bw.Flush(); err != nil {
t.Fatalf("Unexpected error when flushing request header: %s", err)
}
var h1 RequestHeader
br := bufio.NewReader(w)
if err = h1.Read(br); err != nil {
t.Fatalf("Unexpected error when reading request header: %s", err)
}
if h1.ContentLength() != h.ContentLength() {
t.Fatalf("Unexpected Content-Length %d. Expected %d", h1.ContentLength(), h.ContentLength())
}
expectRequestHeaderGet(t, &h1, "Foo", "bar")
expectRequestHeaderGet(t, &h1, "HOST", "12345")
expectRequestHeaderGet(t, &h1, "Content-Type", "aaa/bbb")
expectRequestHeaderGet(t, &h1, "Content-Length", "1234")
expectRequestHeaderGet(t, &h1, "USER-AGent", "aaabbb")
expectRequestHeaderGet(t, &h1, "Referer", "axcv")
expectRequestHeaderGet(t, &h1, "baz", "xxxxx")
expectRequestHeaderGet(t, &h1, "Transfer-Encoding", "")
expectRequestHeaderGet(t, &h1, "Connection", "close")
if !h1.ConnectionClose() {
t.Fatalf("unset connection: close")
}
}
func TestResponseHeaderSetGet(t *testing.T) {
h := &ResponseHeader{}
h.Set("foo", "bar")
h.Set("content-type", "aaa/bbb")
h.Set("connection", "close")
h.Set("content-length", "1234")
h.Set("Server", "aaaa")
h.Set("baz", "xxxxx")
h.Set("Transfer-Encoding", "chunked")
expectResponseHeaderGet(t, h, "Foo", "bar")
expectResponseHeaderGet(t, h, "Content-Type", "aaa/bbb")
expectResponseHeaderGet(t, h, "Connection", "close")
expectResponseHeaderGet(t, h, "Content-Length", "1234")
expectResponseHeaderGet(t, h, "seRVer", "aaaa")
expectResponseHeaderGet(t, h, "baz", "xxxxx")
expectResponseHeaderGet(t, h, "Transfer-Encoding", "")
if h.ContentLength() != 1234 {
t.Fatalf("Unexpected content-length %d. Expected %d", h.ContentLength(), 1234)
}
if !h.ConnectionClose() {
t.Fatalf("Unexpected Connection: close value %v. Expected %v", h.ConnectionClose(), true)
}
w := &bytes.Buffer{}
bw := bufio.NewWriter(w)
err := h.Write(bw)
if err != nil {
t.Fatalf("Unexpected error when writing response header: %s", err)
}
if err := bw.Flush(); err != nil {
t.Fatalf("Unexpected error when flushing response header: %s", err)
}
var h1 ResponseHeader
br := bufio.NewReader(w)
if err = h1.Read(br); err != nil {
t.Fatalf("Unexpected error when reading response header: %s", err)
}
if h1.ContentLength() != h.ContentLength() {
t.Fatalf("Unexpected Content-Length %d. Expected %d", h1.ContentLength(), h.ContentLength())
}
if h1.ConnectionClose() != h.ConnectionClose() {
t.Fatalf("unexpected connection: close %v. Expected %v", h1.ConnectionClose(), h.ConnectionClose())
}
expectResponseHeaderGet(t, &h1, "Foo", "bar")
expectResponseHeaderGet(t, &h1, "Content-Type", "aaa/bbb")
expectResponseHeaderGet(t, &h1, "Connection", "close")
expectResponseHeaderGet(t, &h1, "seRVer", "aaaa")
expectResponseHeaderGet(t, &h1, "baz", "xxxxx")
}
func expectRequestHeaderGet(t *testing.T, h *RequestHeader, key, expectedValue string) {
if string(h.Peek(key)) != expectedValue {
t.Fatalf("Unexpected value for key %q: %q. Expected %q", key, h.Peek(key), expectedValue)
}
}
func expectResponseHeaderGet(t *testing.T, h *ResponseHeader, key, expectedValue string) {
if string(h.Peek(key)) != expectedValue {
t.Fatalf("Unexpected value for key %q: %q. Expected %q", key, h.Peek(key), expectedValue)
}
}
func TestResponseHeaderConnectionClose(t *testing.T) {
testResponseHeaderConnectionClose(t, true)
testResponseHeaderConnectionClose(t, false)
}
func testResponseHeaderConnectionClose(t *testing.T, connectionClose bool) {
h := &ResponseHeader{}
if connectionClose {
h.SetConnectionClose()
}
h.SetContentLength(123)
w := &bytes.Buffer{}
bw := bufio.NewWriter(w)
err := h.Write(bw)
if err != nil {
t.Fatalf("Unexpected error when writing response header: %s", err)
}
if err := bw.Flush(); err != nil {
t.Fatalf("Unexpected error when flushing response header: %s", err)
}
var h1 ResponseHeader
br := bufio.NewReader(w)
err = h1.Read(br)
if err != nil {
t.Fatalf("Unexpected error when reading response header: %s", err)
}
if h1.ConnectionClose() != h.ConnectionClose() {
t.Fatalf("Unexpected value for ConnectionClose: %v. Expected %v", h1.ConnectionClose(), h.ConnectionClose())
}
}
func TestRequestHeaderTooBig(t *testing.T) {
s := "GET / HTTP/1.1\r\nHost: aaa.com\r\n" + getHeaders(10500) + "\r\n"
r := bytes.NewBufferString(s)
br := bufio.NewReaderSize(r, 4096)
h := &RequestHeader{}
err := h.Read(br)
if err == nil {
t.Fatalf("Expecting error when reading too big header")
}
}
func TestResponseHeaderTooBig(t *testing.T) {
s := "HTTP/1.1 200 OK\r\nContent-Type: sss\r\nContent-Length: 0\r\n" + getHeaders(100500) + "\r\n"
r := bytes.NewBufferString(s)
br := bufio.NewReaderSize(r, 4096)
h := &ResponseHeader{}
err := h.Read(br)
if err == nil {
t.Fatalf("Expecting error when reading too big header")
}
}
type bufioPeekReader struct {
s string
n int
}
func (r *bufioPeekReader) Read(b []byte) (int, error) {
if len(r.s) == 0 {
return 0, io.EOF
}
r.n++
n := r.n
if len(r.s) < n {
n = len(r.s)
}
src := []byte(r.s[:n])
r.s = r.s[n:]
n = copy(b, src)
return n, nil
}
func TestRequestHeaderBufioPeek(t *testing.T) {
r := &bufioPeekReader{
s: "GET / HTTP/1.1\r\nHost: foobar.com\r\n" + getHeaders(10) + "\r\naaaa",
}
br := bufio.NewReaderSize(r, 4096)
h := &RequestHeader{}
if err := h.Read(br); err != nil {
t.Fatalf("Unexpected error when reading request: %s", err)
}
verifyRequestHeader(t, h, 0, "/", "foobar.com", "", "")
verifyTrailer(t, br, "aaaa")
}
func TestResponseHeaderBufioPeek(t *testing.T) {
r := &bufioPeekReader{
s: "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Type: aaa\r\n" + getHeaders(10) + "\r\n0123456789",
}
br := bufio.NewReaderSize(r, 4096)
h := &ResponseHeader{}
if err := h.Read(br); err != nil {
t.Fatalf("Unexpected error when reading response: %s", err)
}
verifyResponseHeader(t, h, 200, 10, "aaa")
verifyTrailer(t, br, "0123456789")
}
func getHeaders(n int) string {
var h []string
for i := 0; i < n; i++ {
h = append(h, fmt.Sprintf("Header_%d: Value_%d\r\n", i, i))
}
return strings.Join(h, "")
}
func TestResponseHeaderReadSuccess(t *testing.T) {
h := &ResponseHeader{}
// 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", "")
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", "")
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", "")
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", "")
// 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")
// 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")
// 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", "")
// 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")
// No space after colon
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\nContent-Length:34\nContent-Type: sss\n\naaaa",
200, 34, "sss", "aaaa")
// invalid case
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 400 OK\nconTEnt-leNGTH: 123\nConTENT-TYPE: ass\n\n",
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", "")
// 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", "")
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", "")
// 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", "")
// 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")
// 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")
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", "")
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", "")
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", "")
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")
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")
// no headers
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\n\r\naaaabbb",
200, -2, string(defaultContentType), "aaaabbb")
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")
if h.IsHTTP11() {
t.Fatalf("ancient protocol must be non-http/1.1")
}
if !h.ConnectionClose() {
t.Fatalf("expecting connection: close for ancient protocol")
}
// 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")
if h.IsHTTP11() {
t.Fatalf("ancient protocol must be non-http/1.1")
}
if h.ConnectionClose() {
t.Fatalf("expecting connection: keep-alive for ancient protocol")
}
}
func TestRequestHeaderReadSuccess(t *testing.T) {
h := &RequestHeader{}
// simple headers
testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\nHost: google.com\r\n\r\n",
0, "/foo/bar", "google.com", "", "", "")
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",
0, "/a/bar", "gole.com", "", "", "foobar")
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",
0, "/bar", "gole", "", "", "pppp")
if h.IsHTTP11() {
t.Fatalf("ancient http protocol cannot be http/1.1")
}
if !h.ConnectionClose() {
t.Fatalf("expecting connectionClose for ancient http protocol")
}
// 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",
0, "/aa", "bb", "", "", "xxx")
if h.IsHTTP11() {
t.Fatalf("ancient http protocol cannot be http/1.1")
}
if h.ConnectionClose() {
t.Fatalf("unexpected 'connection: close' 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.IsHTTP11() {
t.Fatalf("expecting http/1.1 protocol")
}
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",
0, "/foo/bar", "google.com", "", "", "")
// 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")
// 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",
0, "/a", "aaa", "", "", "xccv")
// no space after colon
testRequestHeaderReadSuccess(t, h, "GET /a HTTP/1.1\nHost:aaaxd\n\nsdfds",
0, "/a", "aaaxd", "", "", "sdfds")
// 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", "", "", "")
// get with non-zero content-length
testRequestHeaderReadSuccess(t, h, "GET /xxx HTTP/1.1\nHost: aaa.com\nContent-Length: 123\n\n",
0, "/xxx", "aaa.com", "", "", "")
// invalid case
testRequestHeaderReadSuccess(t, h, "GET /aaa HTTP/1.1\nhoST: bbb.com\n\naas",
0, "/aaa", "bbb.com", "", "", "aas")
// referer
testRequestHeaderReadSuccess(t, h, "GET /asdf HTTP/1.1\nHost: aaa.com\nReferer: bb.com\n\naaa",
0, "/asdf", "aaa.com", "bb.com", "", "aaa")
// duplicate host
testRequestHeaderReadSuccess(t, h, "GET /aa HTTP/1.1\r\nHost: aaaaaa.com\r\nHost: bb.com\r\n\r\n",
0, "/aa", "bb.com", "", "", "")
// 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", "")
// 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", "")
// 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",
0, "/aaa", "bbb.com", "", "aaab", "")
// non-post with content-length
testRequestHeaderReadSuccess(t, h, "HEAD / HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 123\r\n\r\n",
0, "/", "aaa.com", "", "", "")
// 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",
0, "/aa", "aa.com", "", "abd/test", "")
// 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",
0, "http://gooGle.com/foO/%20bar?xxx#aaa", "aa.cOM", "", "", "trail")
// no protocol in the first line
testRequestHeaderReadSuccess(t, h, "GET /foo/bar\r\nHost: google.com\r\n\r\nisdD",
0, "/foo/bar", "google.com", "", "", "isdD")
// 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",
0, "/aaa", "aaa.com", "", "", "sss")
// request uri with spaces
testRequestHeaderReadSuccess(t, h, "GET /foo/ bar baz HTTP/1.1\r\nHost: aa.com\r\n\r\nxxx",
0, "/foo/ bar baz", "aa.com", "", "", "xxx")
// no host
testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\nFOObar: assdfd\r\n\r\naaa",
0, "/foo/bar", "", "", "", "aaa")
// no host, no headers
testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\n\r\nfoobar",
0, "/foo/bar", "", "", "", "foobar")
// post with invalid content-length
testRequestHeaderReadSuccess(t, h, "POST /a HTTP/1.1\r\nHost: bb\r\nContent-Type: aa\r\nContent-Length: dff\r\n\r\nqwerty",
-2, "/a", "bb", "", "aa", "qwerty")
// 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")
// 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")
// 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")
// 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")
// 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")
}
func TestResponseHeaderReadError(t *testing.T) {
h := &ResponseHeader{}
// incorrect first line
testResponseHeaderReadError(t, h, "")
testResponseHeaderReadError(t, h, "fo")
testResponseHeaderReadError(t, h, "foobarbaz")
testResponseHeaderReadError(t, h, "HTTP/1.1")
testResponseHeaderReadError(t, h, "HTTP/1.1 ")
testResponseHeaderReadError(t, h, "HTTP/1.1 s")
// non-numeric status code
testResponseHeaderReadError(t, h, "HTTP/1.1 foobar OK\r\nContent-Length: 123\r\nContent-Type: text/html\r\n\r\n")
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")
// 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")
}
func TestRequestHeaderReadError(t *testing.T) {
h := &RequestHeader{}
// incorrect first line
testRequestHeaderReadError(t, h, "")
testRequestHeaderReadError(t, h, "fo")
testRequestHeaderReadError(t, h, "GET ")
testRequestHeaderReadError(t, h, "GET / HTTP/1.1\r")
// missing RequestURI
testRequestHeaderReadError(t, h, "GET HTTP/1.1\r\nHost: google.com\r\n\r\n")
}
func testResponseHeaderReadError(t *testing.T, h *ResponseHeader, headers string) {
r := bytes.NewBufferString(headers)
br := bufio.NewReader(r)
err := h.Read(br)
if err == nil {
t.Fatalf("Expecting error when reading response header %q", 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")
}
func testRequestHeaderReadError(t *testing.T, h *RequestHeader, headers string) {
r := bytes.NewBufferString(headers)
br := bufio.NewReader(r)
err := h.Read(br)
if err == nil {
t.Fatalf("Expecting error when reading request header %q", headers)
}
// make sure request header works after error
testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\nHost: aaaa\r\n\r\nxxx",
0, "/foo/bar", "aaaa", "", "", "xxx")
}
func testResponseHeaderReadSuccess(t *testing.T, h *ResponseHeader, headers string, expectedStatusCode, expectedContentLength int,
expectedContentType, expectedTrailer string) {
r := bytes.NewBufferString(headers)
br := bufio.NewReader(r)
err := h.Read(br)
if err != nil {
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) {
r := bytes.NewBufferString(headers)
br := bufio.NewReader(r)
err := h.Read(br)
if err != nil {
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) {
if h.StatusCode() != expectedStatusCode {
t.Fatalf("Unexpected status code %d. Expected %d", h.StatusCode(), expectedStatusCode)
}
if h.ContentLength() != expectedContentLength {
t.Fatalf("Unexpected content length %d. Expected %d", h.ContentLength(), expectedContentLength)
}
if string(h.Peek("Content-Type")) != expectedContentType {
t.Fatalf("Unexpected content type %q. Expected %q", h.Peek("Content-Type"), expectedContentType)
}
}
func verifyRequestHeader(t *testing.T, h *RequestHeader, expectedContentLength int,
expectedRequestURI, expectedHost, expectedReferer, expectedContentType string) {
if h.ContentLength() != expectedContentLength {
t.Fatalf("Unexpected Content-Length %d. Expected %d", h.ContentLength(), expectedContentLength)
}
if string(h.RequestURI()) != expectedRequestURI {
t.Fatalf("Unexpected RequestURI %q. Expected %q", h.RequestURI(), expectedRequestURI)
}
if string(h.Peek("Host")) != expectedHost {
t.Fatalf("Unexpected host %q. Expected %q", h.Peek("Host"), expectedHost)
}
if string(h.Peek("Referer")) != expectedReferer {
t.Fatalf("Unexpected referer %q. Expected %q", h.Peek("Referer"), expectedReferer)
}
if string(h.Peek("Content-Type")) != expectedContentType {
t.Fatalf("Unexpected content-type %q. Expected %q", h.Peek("Content-Type"), expectedContentType)
}
}
func verifyTrailer(t *testing.T, r *bufio.Reader, expectedTrailer string) {
trailer, err := ioutil.ReadAll(r)
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)
}
}