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