Home | History | Annotate | Download | only in http
      1 // Copyright 2011 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package http
      6 
      7 import (
      8 	"bytes"
      9 	"runtime"
     10 	"testing"
     11 	"time"
     12 )
     13 
     14 var headerWriteTests = []struct {
     15 	h        Header
     16 	exclude  map[string]bool
     17 	expected string
     18 }{
     19 	{Header{}, nil, ""},
     20 	{
     21 		Header{
     22 			"Content-Type":   {"text/html; charset=UTF-8"},
     23 			"Content-Length": {"0"},
     24 		},
     25 		nil,
     26 		"Content-Length: 0\r\nContent-Type: text/html; charset=UTF-8\r\n",
     27 	},
     28 	{
     29 		Header{
     30 			"Content-Length": {"0", "1", "2"},
     31 		},
     32 		nil,
     33 		"Content-Length: 0\r\nContent-Length: 1\r\nContent-Length: 2\r\n",
     34 	},
     35 	{
     36 		Header{
     37 			"Expires":          {"-1"},
     38 			"Content-Length":   {"0"},
     39 			"Content-Encoding": {"gzip"},
     40 		},
     41 		map[string]bool{"Content-Length": true},
     42 		"Content-Encoding: gzip\r\nExpires: -1\r\n",
     43 	},
     44 	{
     45 		Header{
     46 			"Expires":          {"-1"},
     47 			"Content-Length":   {"0", "1", "2"},
     48 			"Content-Encoding": {"gzip"},
     49 		},
     50 		map[string]bool{"Content-Length": true},
     51 		"Content-Encoding: gzip\r\nExpires: -1\r\n",
     52 	},
     53 	{
     54 		Header{
     55 			"Expires":          {"-1"},
     56 			"Content-Length":   {"0"},
     57 			"Content-Encoding": {"gzip"},
     58 		},
     59 		map[string]bool{"Content-Length": true, "Expires": true, "Content-Encoding": true},
     60 		"",
     61 	},
     62 	{
     63 		Header{
     64 			"Nil":          nil,
     65 			"Empty":        {},
     66 			"Blank":        {""},
     67 			"Double-Blank": {"", ""},
     68 		},
     69 		nil,
     70 		"Blank: \r\nDouble-Blank: \r\nDouble-Blank: \r\n",
     71 	},
     72 	// Tests header sorting when over the insertion sort threshold side:
     73 	{
     74 		Header{
     75 			"k1": {"1a", "1b"},
     76 			"k2": {"2a", "2b"},
     77 			"k3": {"3a", "3b"},
     78 			"k4": {"4a", "4b"},
     79 			"k5": {"5a", "5b"},
     80 			"k6": {"6a", "6b"},
     81 			"k7": {"7a", "7b"},
     82 			"k8": {"8a", "8b"},
     83 			"k9": {"9a", "9b"},
     84 		},
     85 		map[string]bool{"k5": true},
     86 		"k1: 1a\r\nk1: 1b\r\nk2: 2a\r\nk2: 2b\r\nk3: 3a\r\nk3: 3b\r\n" +
     87 			"k4: 4a\r\nk4: 4b\r\nk6: 6a\r\nk6: 6b\r\n" +
     88 			"k7: 7a\r\nk7: 7b\r\nk8: 8a\r\nk8: 8b\r\nk9: 9a\r\nk9: 9b\r\n",
     89 	},
     90 }
     91 
     92 func TestHeaderWrite(t *testing.T) {
     93 	var buf bytes.Buffer
     94 	for i, test := range headerWriteTests {
     95 		test.h.WriteSubset(&buf, test.exclude)
     96 		if buf.String() != test.expected {
     97 			t.Errorf("#%d:\n got: %q\nwant: %q", i, buf.String(), test.expected)
     98 		}
     99 		buf.Reset()
    100 	}
    101 }
    102 
    103 var parseTimeTests = []struct {
    104 	h   Header
    105 	err bool
    106 }{
    107 	{Header{"Date": {""}}, true},
    108 	{Header{"Date": {"invalid"}}, true},
    109 	{Header{"Date": {"1994-11-06T08:49:37Z00:00"}}, true},
    110 	{Header{"Date": {"Sun, 06 Nov 1994 08:49:37 GMT"}}, false},
    111 	{Header{"Date": {"Sunday, 06-Nov-94 08:49:37 GMT"}}, false},
    112 	{Header{"Date": {"Sun Nov  6 08:49:37 1994"}}, false},
    113 }
    114 
    115 func TestParseTime(t *testing.T) {
    116 	expect := time.Date(1994, 11, 6, 8, 49, 37, 0, time.UTC)
    117 	for i, test := range parseTimeTests {
    118 		d, err := ParseTime(test.h.Get("Date"))
    119 		if err != nil {
    120 			if !test.err {
    121 				t.Errorf("#%d:\n got err: %v", i, err)
    122 			}
    123 			continue
    124 		}
    125 		if test.err {
    126 			t.Errorf("#%d:\n  should err", i)
    127 			continue
    128 		}
    129 		if !expect.Equal(d) {
    130 			t.Errorf("#%d:\n got: %v\nwant: %v", i, d, expect)
    131 		}
    132 	}
    133 }
    134 
    135 type hasTokenTest struct {
    136 	header string
    137 	token  string
    138 	want   bool
    139 }
    140 
    141 var hasTokenTests = []hasTokenTest{
    142 	{"", "", false},
    143 	{"", "foo", false},
    144 	{"foo", "foo", true},
    145 	{"foo ", "foo", true},
    146 	{" foo", "foo", true},
    147 	{" foo ", "foo", true},
    148 	{"foo,bar", "foo", true},
    149 	{"bar,foo", "foo", true},
    150 	{"bar, foo", "foo", true},
    151 	{"bar,foo, baz", "foo", true},
    152 	{"bar, foo,baz", "foo", true},
    153 	{"bar,foo, baz", "foo", true},
    154 	{"bar, foo, baz", "foo", true},
    155 	{"FOO", "foo", true},
    156 	{"FOO ", "foo", true},
    157 	{" FOO", "foo", true},
    158 	{" FOO ", "foo", true},
    159 	{"FOO,BAR", "foo", true},
    160 	{"BAR,FOO", "foo", true},
    161 	{"BAR, FOO", "foo", true},
    162 	{"BAR,FOO, baz", "foo", true},
    163 	{"BAR, FOO,BAZ", "foo", true},
    164 	{"BAR,FOO, BAZ", "foo", true},
    165 	{"BAR, FOO, BAZ", "foo", true},
    166 	{"foobar", "foo", false},
    167 	{"barfoo ", "foo", false},
    168 }
    169 
    170 func TestHasToken(t *testing.T) {
    171 	for _, tt := range hasTokenTests {
    172 		if hasToken(tt.header, tt.token) != tt.want {
    173 			t.Errorf("hasToken(%q, %q) = %v; want %v", tt.header, tt.token, !tt.want, tt.want)
    174 		}
    175 	}
    176 }
    177 
    178 var testHeader = Header{
    179 	"Content-Length": {"123"},
    180 	"Content-Type":   {"text/plain"},
    181 	"Date":           {"some date at some time Z"},
    182 	"Server":         {DefaultUserAgent},
    183 }
    184 
    185 var buf bytes.Buffer
    186 
    187 func BenchmarkHeaderWriteSubset(b *testing.B) {
    188 	b.ReportAllocs()
    189 	for i := 0; i < b.N; i++ {
    190 		buf.Reset()
    191 		testHeader.WriteSubset(&buf, nil)
    192 	}
    193 }
    194 
    195 func TestHeaderWriteSubsetAllocs(t *testing.T) {
    196 	if testing.Short() {
    197 		t.Skip("skipping alloc test in short mode")
    198 	}
    199 	if raceEnabled {
    200 		t.Skip("skipping test under race detector")
    201 	}
    202 	if runtime.GOMAXPROCS(0) > 1 {
    203 		t.Skip("skipping; GOMAXPROCS>1")
    204 	}
    205 	n := testing.AllocsPerRun(100, func() {
    206 		buf.Reset()
    207 		testHeader.WriteSubset(&buf, nil)
    208 	})
    209 	if n > 0 {
    210 		t.Errorf("allocs = %g; want 0", n)
    211 	}
    212 }
    213