| | |
| | |
| | |
| |
|
| | package http |
| |
|
| | import ( |
| | "bytes" |
| | "internal/race" |
| | "reflect" |
| | "runtime" |
| | "strings" |
| | "testing" |
| | "time" |
| | ) |
| |
|
| | var headerWriteTests = []struct { |
| | h Header |
| | exclude map[string]bool |
| | expected string |
| | }{ |
| | {Header{}, nil, ""}, |
| | { |
| | Header{ |
| | "Content-Type": {"text/html; charset=UTF-8"}, |
| | "Content-Length": {"0"}, |
| | }, |
| | nil, |
| | "Content-Length: 0\r\nContent-Type: text/html; charset=UTF-8\r\n", |
| | }, |
| | { |
| | Header{ |
| | "Content-Length": {"0", "1", "2"}, |
| | }, |
| | nil, |
| | "Content-Length: 0\r\nContent-Length: 1\r\nContent-Length: 2\r\n", |
| | }, |
| | { |
| | Header{ |
| | "Expires": {"-1"}, |
| | "Content-Length": {"0"}, |
| | "Content-Encoding": {"gzip"}, |
| | }, |
| | map[string]bool{"Content-Length": true}, |
| | "Content-Encoding: gzip\r\nExpires: -1\r\n", |
| | }, |
| | { |
| | Header{ |
| | "Expires": {"-1"}, |
| | "Content-Length": {"0", "1", "2"}, |
| | "Content-Encoding": {"gzip"}, |
| | }, |
| | map[string]bool{"Content-Length": true}, |
| | "Content-Encoding: gzip\r\nExpires: -1\r\n", |
| | }, |
| | { |
| | Header{ |
| | "Expires": {"-1"}, |
| | "Content-Length": {"0"}, |
| | "Content-Encoding": {"gzip"}, |
| | }, |
| | map[string]bool{"Content-Length": true, "Expires": true, "Content-Encoding": true}, |
| | "", |
| | }, |
| | { |
| | Header{ |
| | "Nil": nil, |
| | "Empty": {}, |
| | "Blank": {""}, |
| | "Double-Blank": {"", ""}, |
| | }, |
| | nil, |
| | "Blank: \r\nDouble-Blank: \r\nDouble-Blank: \r\n", |
| | }, |
| | |
| | { |
| | Header{ |
| | "k1": {"1a", "1b"}, |
| | "k2": {"2a", "2b"}, |
| | "k3": {"3a", "3b"}, |
| | "k4": {"4a", "4b"}, |
| | "k5": {"5a", "5b"}, |
| | "k6": {"6a", "6b"}, |
| | "k7": {"7a", "7b"}, |
| | "k8": {"8a", "8b"}, |
| | "k9": {"9a", "9b"}, |
| | }, |
| | map[string]bool{"k5": true}, |
| | "k1: 1a\r\nk1: 1b\r\nk2: 2a\r\nk2: 2b\r\nk3: 3a\r\nk3: 3b\r\n" + |
| | "k4: 4a\r\nk4: 4b\r\nk6: 6a\r\nk6: 6b\r\n" + |
| | "k7: 7a\r\nk7: 7b\r\nk8: 8a\r\nk8: 8b\r\nk9: 9a\r\nk9: 9b\r\n", |
| | }, |
| | |
| | { |
| | Header{ |
| | "Content-Type": {"text/html; charset=UTF-8"}, |
| | "NewlineInValue": {"1\r\nBar: 2"}, |
| | "NewlineInKey\r\n": {"1"}, |
| | "Colon:InKey": {"1"}, |
| | "Evil: 1\r\nSmuggledValue": {"1"}, |
| | }, |
| | nil, |
| | "Content-Type: text/html; charset=UTF-8\r\n" + |
| | "NewlineInValue: 1 Bar: 2\r\n", |
| | }, |
| | } |
| |
|
| | func TestHeaderWrite(t *testing.T) { |
| | var buf strings.Builder |
| | for i, test := range headerWriteTests { |
| | test.h.WriteSubset(&buf, test.exclude) |
| | if buf.String() != test.expected { |
| | t.Errorf("#%d:\n got: %q\nwant: %q", i, buf.String(), test.expected) |
| | } |
| | buf.Reset() |
| | } |
| | } |
| |
|
| | var parseTimeTests = []struct { |
| | h Header |
| | err bool |
| | }{ |
| | {Header{"Date": {""}}, true}, |
| | {Header{"Date": {"invalid"}}, true}, |
| | {Header{"Date": {"1994-11-06T08:49:37Z00:00"}}, true}, |
| | {Header{"Date": {"Sun, 06 Nov 1994 08:49:37 GMT"}}, false}, |
| | {Header{"Date": {"Sunday, 06-Nov-94 08:49:37 GMT"}}, false}, |
| | {Header{"Date": {"Sun Nov 6 08:49:37 1994"}}, false}, |
| | } |
| |
|
| | func TestParseTime(t *testing.T) { |
| | expect := time.Date(1994, 11, 6, 8, 49, 37, 0, time.UTC) |
| | for i, test := range parseTimeTests { |
| | d, err := ParseTime(test.h.Get("Date")) |
| | if err != nil { |
| | if !test.err { |
| | t.Errorf("#%d:\n got err: %v", i, err) |
| | } |
| | continue |
| | } |
| | if test.err { |
| | t.Errorf("#%d:\n should err", i) |
| | continue |
| | } |
| | if !expect.Equal(d) { |
| | t.Errorf("#%d:\n got: %v\nwant: %v", i, d, expect) |
| | } |
| | } |
| | } |
| |
|
| | type hasTokenTest struct { |
| | header string |
| | token string |
| | want bool |
| | } |
| |
|
| | var hasTokenTests = []hasTokenTest{ |
| | {"", "", false}, |
| | {"", "foo", false}, |
| | {"foo", "foo", true}, |
| | {"foo ", "foo", true}, |
| | {" foo", "foo", true}, |
| | {" foo ", "foo", true}, |
| | {"foo,bar", "foo", true}, |
| | {"bar,foo", "foo", true}, |
| | {"bar, foo", "foo", true}, |
| | {"bar,foo, baz", "foo", true}, |
| | {"bar, foo,baz", "foo", true}, |
| | {"bar,foo, baz", "foo", true}, |
| | {"bar, foo, baz", "foo", true}, |
| | {"FOO", "foo", true}, |
| | {"FOO ", "foo", true}, |
| | {" FOO", "foo", true}, |
| | {" FOO ", "foo", true}, |
| | {"FOO,BAR", "foo", true}, |
| | {"BAR,FOO", "foo", true}, |
| | {"BAR, FOO", "foo", true}, |
| | {"BAR,FOO, baz", "foo", true}, |
| | {"BAR, FOO,BAZ", "foo", true}, |
| | {"BAR,FOO, BAZ", "foo", true}, |
| | {"BAR, FOO, BAZ", "foo", true}, |
| | {"foobar", "foo", false}, |
| | {"barfoo ", "foo", false}, |
| | } |
| |
|
| | func TestHasToken(t *testing.T) { |
| | for _, tt := range hasTokenTests { |
| | if hasToken(tt.header, tt.token) != tt.want { |
| | t.Errorf("hasToken(%q, %q) = %v; want %v", tt.header, tt.token, !tt.want, tt.want) |
| | } |
| | } |
| | } |
| |
|
| | func TestNilHeaderClone(t *testing.T) { |
| | t1 := Header(nil) |
| | t2 := t1.Clone() |
| | if t2 != nil { |
| | t.Errorf("cloned header does not match original: got: %+v; want: %+v", t2, nil) |
| | } |
| | } |
| |
|
| | var testHeader = Header{ |
| | "Content-Length": {"123"}, |
| | "Content-Type": {"text/plain"}, |
| | "Date": {"some date at some time Z"}, |
| | "Server": {DefaultUserAgent}, |
| | } |
| |
|
| | var buf bytes.Buffer |
| |
|
| | func BenchmarkHeaderWriteSubset(b *testing.B) { |
| | b.ReportAllocs() |
| | for i := 0; i < b.N; i++ { |
| | buf.Reset() |
| | testHeader.WriteSubset(&buf, nil) |
| | } |
| | } |
| |
|
| | func TestHeaderWriteSubsetAllocs(t *testing.T) { |
| | if testing.Short() { |
| | t.Skip("skipping alloc test in short mode") |
| | } |
| | if race.Enabled { |
| | t.Skip("skipping test under race detector") |
| | } |
| | if runtime.GOMAXPROCS(0) > 1 { |
| | t.Skip("skipping; GOMAXPROCS>1") |
| | } |
| | n := testing.AllocsPerRun(100, func() { |
| | buf.Reset() |
| | testHeader.WriteSubset(&buf, nil) |
| | }) |
| | if n > 0 { |
| | t.Errorf("allocs = %g; want 0", n) |
| | } |
| | } |
| |
|
| | |
| | |
| | func TestCloneOrMakeHeader(t *testing.T) { |
| | tests := []struct { |
| | name string |
| | in, want Header |
| | }{ |
| | {"nil", nil, Header{}}, |
| | {"empty", Header{}, Header{}}, |
| | { |
| | name: "non-empty", |
| | in: Header{"foo": {"bar"}}, |
| | want: Header{"foo": {"bar"}}, |
| | }, |
| | { |
| | name: "nil value", |
| | in: Header{"foo": nil}, |
| | want: Header{"foo": nil}, |
| | }, |
| | } |
| |
|
| | for _, tt := range tests { |
| | t.Run(tt.name, func(t *testing.T) { |
| | got := cloneOrMakeHeader(tt.in) |
| | if got == nil { |
| | t.Fatal("unexpected nil Header") |
| | } |
| | if !reflect.DeepEqual(got, tt.want) { |
| | t.Fatalf("Got: %#v\nWant: %#v", got, tt.want) |
| | } |
| | got.Add("A", "B") |
| | got.Get("A") |
| | }) |
| | } |
| | } |
| |
|