github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/http2/hpack/hpack_test.go (about) 1 // Copyright 2014 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 hpack 6 7 import ( 8 "bufio" 9 "bytes" 10 "encoding/hex" 11 "fmt" 12 "math/rand" 13 "reflect" 14 "regexp" 15 "strconv" 16 "strings" 17 "testing" 18 "time" 19 ) 20 21 func TestStaticTable(t *testing.T) { 22 fromSpec := ` 23 +-------+-----------------------------+---------------+ 24 | 1 | :authority | | 25 | 2 | :method | GET | 26 | 3 | :method | POST | 27 | 4 | :path | / | 28 | 5 | :path | /index.html | 29 | 6 | :scheme | http | 30 | 7 | :scheme | https | 31 | 8 | :status | 200 | 32 | 9 | :status | 204 | 33 | 10 | :status | 206 | 34 | 11 | :status | 304 | 35 | 12 | :status | 400 | 36 | 13 | :status | 404 | 37 | 14 | :status | 500 | 38 | 15 | accept-charset | | 39 | 16 | accept-encoding | gzip, deflate | 40 | 17 | accept-language | | 41 | 18 | accept-ranges | | 42 | 19 | accept | | 43 | 20 | access-control-allow-origin | | 44 | 21 | age | | 45 | 22 | allow | | 46 | 23 | authorization | | 47 | 24 | cache-control | | 48 | 25 | content-disposition | | 49 | 26 | content-encoding | | 50 | 27 | content-language | | 51 | 28 | content-length | | 52 | 29 | content-location | | 53 | 30 | content-range | | 54 | 31 | content-type | | 55 | 32 | cookie | | 56 | 33 | date | | 57 | 34 | etag | | 58 | 35 | expect | | 59 | 36 | expires | | 60 | 37 | from | | 61 | 38 | host | | 62 | 39 | if-match | | 63 | 40 | if-modified-since | | 64 | 41 | if-none-match | | 65 | 42 | if-range | | 66 | 43 | if-unmodified-since | | 67 | 44 | last-modified | | 68 | 45 | link | | 69 | 46 | location | | 70 | 47 | max-forwards | | 71 | 48 | proxy-authenticate | | 72 | 49 | proxy-authorization | | 73 | 50 | range | | 74 | 51 | referer | | 75 | 52 | refresh | | 76 | 53 | retry-after | | 77 | 54 | server | | 78 | 55 | set-cookie | | 79 | 56 | strict-transport-security | | 80 | 57 | transfer-encoding | | 81 | 58 | user-agent | | 82 | 59 | vary | | 83 | 60 | via | | 84 | 61 | www-authenticate | | 85 +-------+-----------------------------+---------------+ 86 ` 87 bs := bufio.NewScanner(strings.NewReader(fromSpec)) 88 re := regexp.MustCompile(`\| (\d+)\s+\| (\S+)\s*\| (\S(.*\S)?)?\s+\|`) 89 for bs.Scan() { 90 l := bs.Text() 91 if !strings.Contains(l, "|") { 92 continue 93 } 94 m := re.FindStringSubmatch(l) 95 if m == nil { 96 continue 97 } 98 i, err := strconv.Atoi(m[1]) 99 if err != nil { 100 t.Errorf("Bogus integer on line %q", l) 101 continue 102 } 103 if i < 1 || i > len(staticTable) { 104 t.Errorf("Bogus index %d on line %q", i, l) 105 continue 106 } 107 if got, want := staticTable[i-1].Name, m[2]; got != want { 108 t.Errorf("header index %d name = %q; want %q", i, got, want) 109 } 110 if got, want := staticTable[i-1].Value, m[3]; got != want { 111 t.Errorf("header index %d value = %q; want %q", i, got, want) 112 } 113 } 114 if err := bs.Err(); err != nil { 115 t.Error(err) 116 } 117 } 118 119 func (d *Decoder) mustAt(idx int) HeaderField { 120 if hf, ok := d.at(uint64(idx)); !ok { 121 panic(fmt.Sprintf("bogus index %d", idx)) 122 } else { 123 return hf 124 } 125 } 126 127 func TestDynamicTableAt(t *testing.T) { 128 d := NewDecoder(4096, nil) 129 at := d.mustAt 130 if got, want := at(2), (pair(":method", "GET")); got != want { 131 t.Errorf("at(2) = %v; want %v", got, want) 132 } 133 d.dynTab.add(pair("foo", "bar")) 134 d.dynTab.add(pair("blake", "miz")) 135 if got, want := at(len(staticTable)+1), (pair("blake", "miz")); got != want { 136 t.Errorf("at(dyn 1) = %v; want %v", got, want) 137 } 138 if got, want := at(len(staticTable)+2), (pair("foo", "bar")); got != want { 139 t.Errorf("at(dyn 2) = %v; want %v", got, want) 140 } 141 if got, want := at(3), (pair(":method", "POST")); got != want { 142 t.Errorf("at(3) = %v; want %v", got, want) 143 } 144 } 145 146 func TestDynamicTableSearch(t *testing.T) { 147 dt := dynamicTable{} 148 dt.setMaxSize(4096) 149 150 dt.add(pair("foo", "bar")) 151 dt.add(pair("blake", "miz")) 152 dt.add(pair(":method", "GET")) 153 154 tests := []struct { 155 hf HeaderField 156 wantI uint64 157 wantMatch bool 158 }{ 159 // Name and Value match 160 {pair("foo", "bar"), 3, true}, 161 {pair(":method", "GET"), 1, true}, 162 163 // Only name match because of Sensitive == true 164 {HeaderField{"blake", "miz", true}, 2, false}, 165 166 // Only Name matches 167 {pair("foo", "..."), 3, false}, 168 {pair("blake", "..."), 2, false}, 169 {pair(":method", "..."), 1, false}, 170 171 // None match 172 {pair("foo-", "bar"), 0, false}, 173 } 174 for _, tt := range tests { 175 if gotI, gotMatch := dt.search(tt.hf); gotI != tt.wantI || gotMatch != tt.wantMatch { 176 t.Errorf("d.search(%+v) = %v, %v; want %v, %v", tt.hf, gotI, gotMatch, tt.wantI, tt.wantMatch) 177 } 178 } 179 } 180 181 func TestDynamicTableSizeEvict(t *testing.T) { 182 d := NewDecoder(4096, nil) 183 if want := uint32(0); d.dynTab.size != want { 184 t.Fatalf("size = %d; want %d", d.dynTab.size, want) 185 } 186 add := d.dynTab.add 187 add(pair("blake", "eats pizza")) 188 if want := uint32(15 + 32); d.dynTab.size != want { 189 t.Fatalf("after pizza, size = %d; want %d", d.dynTab.size, want) 190 } 191 add(pair("foo", "bar")) 192 if want := uint32(15 + 32 + 6 + 32); d.dynTab.size != want { 193 t.Fatalf("after foo bar, size = %d; want %d", d.dynTab.size, want) 194 } 195 d.dynTab.setMaxSize(15 + 32 + 1 /* slop */) 196 if want := uint32(6 + 32); d.dynTab.size != want { 197 t.Fatalf("after setMaxSize, size = %d; want %d", d.dynTab.size, want) 198 } 199 if got, want := d.mustAt(len(staticTable)+1), (pair("foo", "bar")); got != want { 200 t.Errorf("at(dyn 1) = %v; want %v", got, want) 201 } 202 add(pair("long", strings.Repeat("x", 500))) 203 if want := uint32(0); d.dynTab.size != want { 204 t.Fatalf("after big one, size = %d; want %d", d.dynTab.size, want) 205 } 206 } 207 208 func TestDecoderDecode(t *testing.T) { 209 tests := []struct { 210 name string 211 in []byte 212 want []HeaderField 213 wantDynTab []HeaderField // newest entry first 214 }{ 215 // C.2.1 Literal Header Field with Indexing 216 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.1 217 {"C.2.1", dehex("400a 6375 7374 6f6d 2d6b 6579 0d63 7573 746f 6d2d 6865 6164 6572"), 218 []HeaderField{pair("custom-key", "custom-header")}, 219 []HeaderField{pair("custom-key", "custom-header")}, 220 }, 221 222 // C.2.2 Literal Header Field without Indexing 223 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.2 224 {"C.2.2", dehex("040c 2f73 616d 706c 652f 7061 7468"), 225 []HeaderField{pair(":path", "/sample/path")}, 226 []HeaderField{}}, 227 228 // C.2.3 Literal Header Field never Indexed 229 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.3 230 {"C.2.3", dehex("1008 7061 7373 776f 7264 0673 6563 7265 74"), 231 []HeaderField{{"password", "secret", true}}, 232 []HeaderField{}}, 233 234 // C.2.4 Indexed Header Field 235 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.4 236 {"C.2.4", []byte("\x82"), 237 []HeaderField{pair(":method", "GET")}, 238 []HeaderField{}}, 239 } 240 for _, tt := range tests { 241 d := NewDecoder(4096, nil) 242 hf, err := d.DecodeFull(tt.in) 243 if err != nil { 244 t.Errorf("%s: %v", tt.name, err) 245 continue 246 } 247 if !reflect.DeepEqual(hf, tt.want) { 248 t.Errorf("%s: Got %v; want %v", tt.name, hf, tt.want) 249 } 250 gotDynTab := d.dynTab.reverseCopy() 251 if !reflect.DeepEqual(gotDynTab, tt.wantDynTab) { 252 t.Errorf("%s: dynamic table after = %v; want %v", tt.name, gotDynTab, tt.wantDynTab) 253 } 254 } 255 } 256 257 func (dt *dynamicTable) reverseCopy() (hf []HeaderField) { 258 hf = make([]HeaderField, len(dt.ents)) 259 for i := range hf { 260 hf[i] = dt.ents[len(dt.ents)-1-i] 261 } 262 return 263 } 264 265 type encAndWant struct { 266 enc []byte 267 want []HeaderField 268 wantDynTab []HeaderField 269 wantDynSize uint32 270 } 271 272 // C.3 Request Examples without Huffman Coding 273 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.3 274 func TestDecodeC3_NoHuffman(t *testing.T) { 275 testDecodeSeries(t, 4096, []encAndWant{ 276 {dehex("8286 8441 0f77 7777 2e65 7861 6d70 6c65 2e63 6f6d"), 277 []HeaderField{ 278 pair(":method", "GET"), 279 pair(":scheme", "http"), 280 pair(":path", "/"), 281 pair(":authority", "www.example.com"), 282 }, 283 []HeaderField{ 284 pair(":authority", "www.example.com"), 285 }, 286 57, 287 }, 288 {dehex("8286 84be 5808 6e6f 2d63 6163 6865"), 289 []HeaderField{ 290 pair(":method", "GET"), 291 pair(":scheme", "http"), 292 pair(":path", "/"), 293 pair(":authority", "www.example.com"), 294 pair("cache-control", "no-cache"), 295 }, 296 []HeaderField{ 297 pair("cache-control", "no-cache"), 298 pair(":authority", "www.example.com"), 299 }, 300 110, 301 }, 302 {dehex("8287 85bf 400a 6375 7374 6f6d 2d6b 6579 0c63 7573 746f 6d2d 7661 6c75 65"), 303 []HeaderField{ 304 pair(":method", "GET"), 305 pair(":scheme", "https"), 306 pair(":path", "/index.html"), 307 pair(":authority", "www.example.com"), 308 pair("custom-key", "custom-value"), 309 }, 310 []HeaderField{ 311 pair("custom-key", "custom-value"), 312 pair("cache-control", "no-cache"), 313 pair(":authority", "www.example.com"), 314 }, 315 164, 316 }, 317 }) 318 } 319 320 // C.4 Request Examples with Huffman Coding 321 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.4 322 func TestDecodeC4_Huffman(t *testing.T) { 323 testDecodeSeries(t, 4096, []encAndWant{ 324 {dehex("8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff"), 325 []HeaderField{ 326 pair(":method", "GET"), 327 pair(":scheme", "http"), 328 pair(":path", "/"), 329 pair(":authority", "www.example.com"), 330 }, 331 []HeaderField{ 332 pair(":authority", "www.example.com"), 333 }, 334 57, 335 }, 336 {dehex("8286 84be 5886 a8eb 1064 9cbf"), 337 []HeaderField{ 338 pair(":method", "GET"), 339 pair(":scheme", "http"), 340 pair(":path", "/"), 341 pair(":authority", "www.example.com"), 342 pair("cache-control", "no-cache"), 343 }, 344 []HeaderField{ 345 pair("cache-control", "no-cache"), 346 pair(":authority", "www.example.com"), 347 }, 348 110, 349 }, 350 {dehex("8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925 a849 e95b b8e8 b4bf"), 351 []HeaderField{ 352 pair(":method", "GET"), 353 pair(":scheme", "https"), 354 pair(":path", "/index.html"), 355 pair(":authority", "www.example.com"), 356 pair("custom-key", "custom-value"), 357 }, 358 []HeaderField{ 359 pair("custom-key", "custom-value"), 360 pair("cache-control", "no-cache"), 361 pair(":authority", "www.example.com"), 362 }, 363 164, 364 }, 365 }) 366 } 367 368 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.5 369 // "This section shows several consecutive header lists, corresponding 370 // to HTTP responses, on the same connection. The HTTP/2 setting 371 // parameter SETTINGS_HEADER_TABLE_SIZE is set to the value of 256 372 // octets, causing some evictions to occur." 373 func TestDecodeC5_ResponsesNoHuff(t *testing.T) { 374 testDecodeSeries(t, 256, []encAndWant{ 375 {dehex(` 376 4803 3330 3258 0770 7269 7661 7465 611d 377 4d6f 6e2c 2032 3120 4f63 7420 3230 3133 378 2032 303a 3133 3a32 3120 474d 546e 1768 379 7474 7073 3a2f 2f77 7777 2e65 7861 6d70 380 6c65 2e63 6f6d 381 `), 382 []HeaderField{ 383 pair(":status", "302"), 384 pair("cache-control", "private"), 385 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 386 pair("location", "https://www.example.com"), 387 }, 388 []HeaderField{ 389 pair("location", "https://www.example.com"), 390 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 391 pair("cache-control", "private"), 392 pair(":status", "302"), 393 }, 394 222, 395 }, 396 {dehex("4803 3330 37c1 c0bf"), 397 []HeaderField{ 398 pair(":status", "307"), 399 pair("cache-control", "private"), 400 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 401 pair("location", "https://www.example.com"), 402 }, 403 []HeaderField{ 404 pair(":status", "307"), 405 pair("location", "https://www.example.com"), 406 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 407 pair("cache-control", "private"), 408 }, 409 222, 410 }, 411 {dehex(` 412 88c1 611d 4d6f 6e2c 2032 3120 4f63 7420 413 3230 3133 2032 303a 3133 3a32 3220 474d 414 54c0 5a04 677a 6970 7738 666f 6f3d 4153 415 444a 4b48 514b 425a 584f 5157 454f 5049 416 5541 5851 5745 4f49 553b 206d 6178 2d61 417 6765 3d33 3630 303b 2076 6572 7369 6f6e 418 3d31 419 `), 420 []HeaderField{ 421 pair(":status", "200"), 422 pair("cache-control", "private"), 423 pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"), 424 pair("location", "https://www.example.com"), 425 pair("content-encoding", "gzip"), 426 pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), 427 }, 428 []HeaderField{ 429 pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), 430 pair("content-encoding", "gzip"), 431 pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"), 432 }, 433 215, 434 }, 435 }) 436 } 437 438 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.6 439 // "This section shows the same examples as the previous section, but 440 // using Huffman encoding for the literal values. The HTTP/2 setting 441 // parameter SETTINGS_HEADER_TABLE_SIZE is set to the value of 256 442 // octets, causing some evictions to occur. The eviction mechanism 443 // uses the length of the decoded literal values, so the same 444 // evictions occurs as in the previous section." 445 func TestDecodeC6_ResponsesHuffman(t *testing.T) { 446 testDecodeSeries(t, 256, []encAndWant{ 447 {dehex(` 448 4882 6402 5885 aec3 771a 4b61 96d0 7abe 449 9410 54d4 44a8 2005 9504 0b81 66e0 82a6 450 2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8 451 e9ae 82ae 43d3 452 `), 453 []HeaderField{ 454 pair(":status", "302"), 455 pair("cache-control", "private"), 456 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 457 pair("location", "https://www.example.com"), 458 }, 459 []HeaderField{ 460 pair("location", "https://www.example.com"), 461 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 462 pair("cache-control", "private"), 463 pair(":status", "302"), 464 }, 465 222, 466 }, 467 {dehex("4883 640e ffc1 c0bf"), 468 []HeaderField{ 469 pair(":status", "307"), 470 pair("cache-control", "private"), 471 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 472 pair("location", "https://www.example.com"), 473 }, 474 []HeaderField{ 475 pair(":status", "307"), 476 pair("location", "https://www.example.com"), 477 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 478 pair("cache-control", "private"), 479 }, 480 222, 481 }, 482 {dehex(` 483 88c1 6196 d07a be94 1054 d444 a820 0595 484 040b 8166 e084 a62d 1bff c05a 839b d9ab 485 77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b 486 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 487 9587 3160 65c0 03ed 4ee5 b106 3d50 07 488 `), 489 []HeaderField{ 490 pair(":status", "200"), 491 pair("cache-control", "private"), 492 pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"), 493 pair("location", "https://www.example.com"), 494 pair("content-encoding", "gzip"), 495 pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), 496 }, 497 []HeaderField{ 498 pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), 499 pair("content-encoding", "gzip"), 500 pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"), 501 }, 502 215, 503 }, 504 }) 505 } 506 507 func testDecodeSeries(t *testing.T, size uint32, steps []encAndWant) { 508 d := NewDecoder(size, nil) 509 for i, step := range steps { 510 hf, err := d.DecodeFull(step.enc) 511 if err != nil { 512 t.Fatalf("Error at step index %d: %v", i, err) 513 } 514 if !reflect.DeepEqual(hf, step.want) { 515 t.Fatalf("At step index %d: Got headers %v; want %v", i, hf, step.want) 516 } 517 gotDynTab := d.dynTab.reverseCopy() 518 if !reflect.DeepEqual(gotDynTab, step.wantDynTab) { 519 t.Errorf("After step index %d, dynamic table = %v; want %v", i, gotDynTab, step.wantDynTab) 520 } 521 if d.dynTab.size != step.wantDynSize { 522 t.Errorf("After step index %d, dynamic table size = %v; want %v", i, d.dynTab.size, step.wantDynSize) 523 } 524 } 525 } 526 527 func TestHuffmanDecode(t *testing.T) { 528 tests := []struct { 529 inHex, want string 530 }{ 531 {"f1e3 c2e5 f23a 6ba0 ab90 f4ff", "www.example.com"}, 532 {"a8eb 1064 9cbf", "no-cache"}, 533 {"25a8 49e9 5ba9 7d7f", "custom-key"}, 534 {"25a8 49e9 5bb8 e8b4 bf", "custom-value"}, 535 {"6402", "302"}, 536 {"aec3 771a 4b", "private"}, 537 {"d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff", "Mon, 21 Oct 2013 20:13:21 GMT"}, 538 {"9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3", "https://www.example.com"}, 539 {"9bd9 ab", "gzip"}, 540 {"94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07", 541 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"}, 542 } 543 for i, tt := range tests { 544 var buf bytes.Buffer 545 in, err := hex.DecodeString(strings.Replace(tt.inHex, " ", "", -1)) 546 if err != nil { 547 t.Errorf("%d. hex input error: %v", i, err) 548 continue 549 } 550 if _, err := HuffmanDecode(&buf, in); err != nil { 551 t.Errorf("%d. decode error: %v", i, err) 552 continue 553 } 554 if got := buf.String(); tt.want != got { 555 t.Errorf("%d. decode = %q; want %q", i, got, tt.want) 556 } 557 } 558 } 559 560 func TestAppendHuffmanString(t *testing.T) { 561 tests := []struct { 562 in, want string 563 }{ 564 {"www.example.com", "f1e3 c2e5 f23a 6ba0 ab90 f4ff"}, 565 {"no-cache", "a8eb 1064 9cbf"}, 566 {"custom-key", "25a8 49e9 5ba9 7d7f"}, 567 {"custom-value", "25a8 49e9 5bb8 e8b4 bf"}, 568 {"302", "6402"}, 569 {"private", "aec3 771a 4b"}, 570 {"Mon, 21 Oct 2013 20:13:21 GMT", "d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff"}, 571 {"https://www.example.com", "9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3"}, 572 {"gzip", "9bd9 ab"}, 573 {"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", 574 "94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07"}, 575 } 576 for i, tt := range tests { 577 buf := []byte{} 578 want := strings.Replace(tt.want, " ", "", -1) 579 buf = AppendHuffmanString(buf, tt.in) 580 if got := hex.EncodeToString(buf); want != got { 581 t.Errorf("%d. encode = %q; want %q", i, got, want) 582 } 583 } 584 } 585 586 func TestHuffmanMaxStrLen(t *testing.T) { 587 const msg = "Some string" 588 huff := AppendHuffmanString(nil, msg) 589 590 testGood := func(max int) { 591 var out bytes.Buffer 592 if err := huffmanDecode(&out, max, huff); err != nil { 593 t.Errorf("For maxLen=%d, unexpected error: %v", max, err) 594 } 595 if out.String() != msg { 596 t.Errorf("For maxLen=%d, out = %q; want %q", max, out.String(), msg) 597 } 598 } 599 testGood(0) 600 testGood(len(msg)) 601 testGood(len(msg) + 1) 602 603 var out bytes.Buffer 604 if err := huffmanDecode(&out, len(msg)-1, huff); err != ErrStringLength { 605 t.Errorf("err = %v; want ErrStringLength", err) 606 } 607 } 608 609 func TestHuffmanRoundtripStress(t *testing.T) { 610 const Len = 50 // of uncompressed string 611 input := make([]byte, Len) 612 var output bytes.Buffer 613 var huff []byte 614 615 n := 5000 616 if testing.Short() { 617 n = 100 618 } 619 seed := time.Now().UnixNano() 620 t.Logf("Seed = %v", seed) 621 src := rand.New(rand.NewSource(seed)) 622 var encSize int64 623 for i := 0; i < n; i++ { 624 for l := range input { 625 input[l] = byte(src.Intn(256)) 626 } 627 huff = AppendHuffmanString(huff[:0], string(input)) 628 encSize += int64(len(huff)) 629 output.Reset() 630 if err := huffmanDecode(&output, 0, huff); err != nil { 631 t.Errorf("Failed to decode %q -> %q -> error %v", input, huff, err) 632 continue 633 } 634 if !bytes.Equal(output.Bytes(), input) { 635 t.Errorf("Roundtrip failure on %q -> %q -> %q", input, huff, output.Bytes()) 636 } 637 } 638 t.Logf("Compressed size of original: %0.02f%% (%v -> %v)", 100*(float64(encSize)/(Len*float64(n))), Len*n, encSize) 639 } 640 641 func TestHuffmanDecodeFuzz(t *testing.T) { 642 const Len = 50 // of compressed 643 var buf, zbuf bytes.Buffer 644 645 n := 5000 646 if testing.Short() { 647 n = 100 648 } 649 seed := time.Now().UnixNano() 650 t.Logf("Seed = %v", seed) 651 src := rand.New(rand.NewSource(seed)) 652 numFail := 0 653 for i := 0; i < n; i++ { 654 zbuf.Reset() 655 if i == 0 { 656 // Start with at least one invalid one. 657 zbuf.WriteString("00\x91\xff\xff\xff\xff\xc8") 658 } else { 659 for l := 0; l < Len; l++ { 660 zbuf.WriteByte(byte(src.Intn(256))) 661 } 662 } 663 664 buf.Reset() 665 if err := huffmanDecode(&buf, 0, zbuf.Bytes()); err != nil { 666 if err == ErrInvalidHuffman { 667 numFail++ 668 continue 669 } 670 t.Errorf("Failed to decode %q: %v", zbuf.Bytes(), err) 671 continue 672 } 673 } 674 t.Logf("%0.02f%% are invalid (%d / %d)", 100*float64(numFail)/float64(n), numFail, n) 675 if numFail < 1 { 676 t.Error("expected at least one invalid huffman encoding (test starts with one)") 677 } 678 } 679 680 func TestReadVarInt(t *testing.T) { 681 type res struct { 682 i uint64 683 consumed int 684 err error 685 } 686 tests := []struct { 687 n byte 688 p []byte 689 want res 690 }{ 691 // Fits in a byte: 692 {1, []byte{0}, res{0, 1, nil}}, 693 {2, []byte{2}, res{2, 1, nil}}, 694 {3, []byte{6}, res{6, 1, nil}}, 695 {4, []byte{14}, res{14, 1, nil}}, 696 {5, []byte{30}, res{30, 1, nil}}, 697 {6, []byte{62}, res{62, 1, nil}}, 698 {7, []byte{126}, res{126, 1, nil}}, 699 {8, []byte{254}, res{254, 1, nil}}, 700 701 // Doesn't fit in a byte: 702 {1, []byte{1}, res{0, 0, errNeedMore}}, 703 {2, []byte{3}, res{0, 0, errNeedMore}}, 704 {3, []byte{7}, res{0, 0, errNeedMore}}, 705 {4, []byte{15}, res{0, 0, errNeedMore}}, 706 {5, []byte{31}, res{0, 0, errNeedMore}}, 707 {6, []byte{63}, res{0, 0, errNeedMore}}, 708 {7, []byte{127}, res{0, 0, errNeedMore}}, 709 {8, []byte{255}, res{0, 0, errNeedMore}}, 710 711 // Ignoring top bits: 712 {5, []byte{255, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 111 713 {5, []byte{159, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 100 714 {5, []byte{191, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 101 715 716 // Extra byte: 717 {5, []byte{191, 154, 10, 2}, res{1337, 3, nil}}, // extra byte 718 719 // Short a byte: 720 {5, []byte{191, 154}, res{0, 0, errNeedMore}}, 721 722 // integer overflow: 723 {1, []byte{255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, res{0, 0, errVarintOverflow}}, 724 } 725 for _, tt := range tests { 726 i, remain, err := readVarInt(tt.n, tt.p) 727 consumed := len(tt.p) - len(remain) 728 got := res{i, consumed, err} 729 if got != tt.want { 730 t.Errorf("readVarInt(%d, %v ~ %x) = %+v; want %+v", tt.n, tt.p, tt.p, got, tt.want) 731 } 732 } 733 } 734 735 // Fuzz crash, originally reported at https://github.com/bradfitz/http2/issues/56 736 func TestHuffmanFuzzCrash(t *testing.T) { 737 got, err := HuffmanDecodeToString([]byte("00\x91\xff\xff\xff\xff\xc8")) 738 if got != "" { 739 t.Errorf("Got %q; want empty string", got) 740 } 741 if err != ErrInvalidHuffman { 742 t.Errorf("Err = %v; want ErrInvalidHuffman", err) 743 } 744 } 745 746 func dehex(s string) []byte { 747 s = strings.Replace(s, " ", "", -1) 748 s = strings.Replace(s, "\n", "", -1) 749 b, err := hex.DecodeString(s) 750 if err != nil { 751 panic(err) 752 } 753 return b 754 } 755 756 func TestEmitEnabled(t *testing.T) { 757 var buf bytes.Buffer 758 enc := NewEncoder(&buf) 759 enc.WriteField(HeaderField{Name: "foo", Value: "bar"}) 760 enc.WriteField(HeaderField{Name: "foo", Value: "bar"}) 761 762 numCallback := 0 763 var dec *Decoder 764 dec = NewDecoder(8<<20, func(HeaderField) { 765 numCallback++ 766 dec.SetEmitEnabled(false) 767 }) 768 if !dec.EmitEnabled() { 769 t.Errorf("initial emit enabled = false; want true") 770 } 771 if _, err := dec.Write(buf.Bytes()); err != nil { 772 t.Error(err) 773 } 774 if numCallback != 1 { 775 t.Errorf("num callbacks = %d; want 1", numCallback) 776 } 777 if dec.EmitEnabled() { 778 t.Errorf("emit enabled = true; want false") 779 } 780 } 781 782 func TestSaveBufLimit(t *testing.T) { 783 const maxStr = 1 << 10 784 var got []HeaderField 785 dec := NewDecoder(initialHeaderTableSize, func(hf HeaderField) { 786 got = append(got, hf) 787 }) 788 dec.SetMaxStringLength(maxStr) 789 var frag []byte 790 frag = append(frag[:0], encodeTypeByte(false, false)) 791 frag = appendVarInt(frag, 7, 3) 792 frag = append(frag, "foo"...) 793 frag = appendVarInt(frag, 7, 3) 794 frag = append(frag, "bar"...) 795 796 if _, err := dec.Write(frag); err != nil { 797 t.Fatal(err) 798 } 799 800 want := []HeaderField{{Name: "foo", Value: "bar"}} 801 if !reflect.DeepEqual(got, want) { 802 t.Errorf("After small writes, got %v; want %v", got, want) 803 } 804 805 frag = append(frag[:0], encodeTypeByte(false, false)) 806 frag = appendVarInt(frag, 7, maxStr*3) 807 frag = append(frag, make([]byte, maxStr*3)...) 808 809 _, err := dec.Write(frag) 810 if err != ErrStringLength { 811 t.Fatalf("Write error = %v; want ErrStringLength", err) 812 } 813 }