github.com/AESNooper/go/src@v0.0.0-20220218095104-b56a4ab1bbbb/net/http/header_test.go (about) 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 "internal/race" 10 "reflect" 11 "runtime" 12 "testing" 13 "time" 14 ) 15 16 var headerWriteTests = []struct { 17 h Header 18 exclude map[string]bool 19 expected string 20 }{ 21 {Header{}, nil, ""}, 22 { 23 Header{ 24 "Content-Type": {"text/html; charset=UTF-8"}, 25 "Content-Length": {"0"}, 26 }, 27 nil, 28 "Content-Length: 0\r\nContent-Type: text/html; charset=UTF-8\r\n", 29 }, 30 { 31 Header{ 32 "Content-Length": {"0", "1", "2"}, 33 }, 34 nil, 35 "Content-Length: 0\r\nContent-Length: 1\r\nContent-Length: 2\r\n", 36 }, 37 { 38 Header{ 39 "Expires": {"-1"}, 40 "Content-Length": {"0"}, 41 "Content-Encoding": {"gzip"}, 42 }, 43 map[string]bool{"Content-Length": true}, 44 "Content-Encoding: gzip\r\nExpires: -1\r\n", 45 }, 46 { 47 Header{ 48 "Expires": {"-1"}, 49 "Content-Length": {"0", "1", "2"}, 50 "Content-Encoding": {"gzip"}, 51 }, 52 map[string]bool{"Content-Length": true}, 53 "Content-Encoding: gzip\r\nExpires: -1\r\n", 54 }, 55 { 56 Header{ 57 "Expires": {"-1"}, 58 "Content-Length": {"0"}, 59 "Content-Encoding": {"gzip"}, 60 }, 61 map[string]bool{"Content-Length": true, "Expires": true, "Content-Encoding": true}, 62 "", 63 }, 64 { 65 Header{ 66 "Nil": nil, 67 "Empty": {}, 68 "Blank": {""}, 69 "Double-Blank": {"", ""}, 70 }, 71 nil, 72 "Blank: \r\nDouble-Blank: \r\nDouble-Blank: \r\n", 73 }, 74 // Tests header sorting when over the insertion sort threshold side: 75 { 76 Header{ 77 "k1": {"1a", "1b"}, 78 "k2": {"2a", "2b"}, 79 "k3": {"3a", "3b"}, 80 "k4": {"4a", "4b"}, 81 "k5": {"5a", "5b"}, 82 "k6": {"6a", "6b"}, 83 "k7": {"7a", "7b"}, 84 "k8": {"8a", "8b"}, 85 "k9": {"9a", "9b"}, 86 }, 87 map[string]bool{"k5": true}, 88 "k1: 1a\r\nk1: 1b\r\nk2: 2a\r\nk2: 2b\r\nk3: 3a\r\nk3: 3b\r\n" + 89 "k4: 4a\r\nk4: 4b\r\nk6: 6a\r\nk6: 6b\r\n" + 90 "k7: 7a\r\nk7: 7b\r\nk8: 8a\r\nk8: 8b\r\nk9: 9a\r\nk9: 9b\r\n", 91 }, 92 // Tests invalid characters in headers. 93 { 94 Header{ 95 "Content-Type": {"text/html; charset=UTF-8"}, 96 "NewlineInValue": {"1\r\nBar: 2"}, 97 "NewlineInKey\r\n": {"1"}, 98 "Colon:InKey": {"1"}, 99 "Evil: 1\r\nSmuggledValue": {"1"}, 100 }, 101 nil, 102 "Content-Type: text/html; charset=UTF-8\r\n" + 103 "NewlineInValue: 1 Bar: 2\r\n", 104 }, 105 } 106 107 func TestHeaderWrite(t *testing.T) { 108 var buf bytes.Buffer 109 for i, test := range headerWriteTests { 110 test.h.WriteSubset(&buf, test.exclude) 111 if buf.String() != test.expected { 112 t.Errorf("#%d:\n got: %q\nwant: %q", i, buf.String(), test.expected) 113 } 114 buf.Reset() 115 } 116 } 117 118 var parseTimeTests = []struct { 119 h Header 120 err bool 121 }{ 122 {Header{"Date": {""}}, true}, 123 {Header{"Date": {"invalid"}}, true}, 124 {Header{"Date": {"1994-11-06T08:49:37Z00:00"}}, true}, 125 {Header{"Date": {"Sun, 06 Nov 1994 08:49:37 GMT"}}, false}, 126 {Header{"Date": {"Sunday, 06-Nov-94 08:49:37 GMT"}}, false}, 127 {Header{"Date": {"Sun Nov 6 08:49:37 1994"}}, false}, 128 } 129 130 func TestParseTime(t *testing.T) { 131 expect := time.Date(1994, 11, 6, 8, 49, 37, 0, time.UTC) 132 for i, test := range parseTimeTests { 133 d, err := ParseTime(test.h.Get("Date")) 134 if err != nil { 135 if !test.err { 136 t.Errorf("#%d:\n got err: %v", i, err) 137 } 138 continue 139 } 140 if test.err { 141 t.Errorf("#%d:\n should err", i) 142 continue 143 } 144 if !expect.Equal(d) { 145 t.Errorf("#%d:\n got: %v\nwant: %v", i, d, expect) 146 } 147 } 148 } 149 150 type hasTokenTest struct { 151 header string 152 token string 153 want bool 154 } 155 156 var hasTokenTests = []hasTokenTest{ 157 {"", "", false}, 158 {"", "foo", false}, 159 {"foo", "foo", true}, 160 {"foo ", "foo", true}, 161 {" foo", "foo", true}, 162 {" foo ", "foo", true}, 163 {"foo,bar", "foo", true}, 164 {"bar,foo", "foo", true}, 165 {"bar, foo", "foo", true}, 166 {"bar,foo, baz", "foo", true}, 167 {"bar, foo,baz", "foo", true}, 168 {"bar,foo, baz", "foo", true}, 169 {"bar, foo, baz", "foo", true}, 170 {"FOO", "foo", true}, 171 {"FOO ", "foo", true}, 172 {" FOO", "foo", true}, 173 {" FOO ", "foo", true}, 174 {"FOO,BAR", "foo", true}, 175 {"BAR,FOO", "foo", true}, 176 {"BAR, FOO", "foo", true}, 177 {"BAR,FOO, baz", "foo", true}, 178 {"BAR, FOO,BAZ", "foo", true}, 179 {"BAR,FOO, BAZ", "foo", true}, 180 {"BAR, FOO, BAZ", "foo", true}, 181 {"foobar", "foo", false}, 182 {"barfoo ", "foo", false}, 183 } 184 185 func TestHasToken(t *testing.T) { 186 for _, tt := range hasTokenTests { 187 if hasToken(tt.header, tt.token) != tt.want { 188 t.Errorf("hasToken(%q, %q) = %v; want %v", tt.header, tt.token, !tt.want, tt.want) 189 } 190 } 191 } 192 193 func TestNilHeaderClone(t *testing.T) { 194 t1 := Header(nil) 195 t2 := t1.Clone() 196 if t2 != nil { 197 t.Errorf("cloned header does not match original: got: %+v; want: %+v", t2, nil) 198 } 199 } 200 201 var testHeader = Header{ 202 "Content-Length": {"123"}, 203 "Content-Type": {"text/plain"}, 204 "Date": {"some date at some time Z"}, 205 "Server": {DefaultUserAgent}, 206 } 207 208 var buf bytes.Buffer 209 210 func BenchmarkHeaderWriteSubset(b *testing.B) { 211 b.ReportAllocs() 212 for i := 0; i < b.N; i++ { 213 buf.Reset() 214 testHeader.WriteSubset(&buf, nil) 215 } 216 } 217 218 func TestHeaderWriteSubsetAllocs(t *testing.T) { 219 if testing.Short() { 220 t.Skip("skipping alloc test in short mode") 221 } 222 if race.Enabled { 223 t.Skip("skipping test under race detector") 224 } 225 if runtime.GOMAXPROCS(0) > 1 { 226 t.Skip("skipping; GOMAXPROCS>1") 227 } 228 n := testing.AllocsPerRun(100, func() { 229 buf.Reset() 230 testHeader.WriteSubset(&buf, nil) 231 }) 232 if n > 0 { 233 t.Errorf("allocs = %g; want 0", n) 234 } 235 } 236 237 // Issue 34878: test that every call to 238 // cloneOrMakeHeader never returns a nil Header. 239 func TestCloneOrMakeHeader(t *testing.T) { 240 tests := []struct { 241 name string 242 in, want Header 243 }{ 244 {"nil", nil, Header{}}, 245 {"empty", Header{}, Header{}}, 246 { 247 name: "non-empty", 248 in: Header{"foo": {"bar"}}, 249 want: Header{"foo": {"bar"}}, 250 }, 251 } 252 253 for _, tt := range tests { 254 t.Run(tt.name, func(t *testing.T) { 255 got := cloneOrMakeHeader(tt.in) 256 if got == nil { 257 t.Fatal("unexpected nil Header") 258 } 259 if !reflect.DeepEqual(got, tt.want) { 260 t.Fatalf("Got: %#v\nWant: %#v", got, tt.want) 261 } 262 got.Add("A", "B") 263 got.Get("A") 264 }) 265 } 266 }