github.com/useflyent/fhttp@v0.0.0-20211004035111-333f430cfbbf/http2/frame_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 http2 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "reflect" 12 "strings" 13 "testing" 14 "unsafe" 15 16 "github.com/useflyent/fhttp/http2/hpack" 17 ) 18 19 func testFramer() (*Framer, *bytes.Buffer) { 20 buf := new(bytes.Buffer) 21 return NewFramer(buf, buf), buf 22 } 23 24 func TestFrameSizes(t *testing.T) { 25 // Catch people rearranging the FrameHeader fields. 26 if got, want := int(unsafe.Sizeof(FrameHeader{})), 12; got != want { 27 t.Errorf("FrameHeader size = %d; want %d", got, want) 28 } 29 } 30 31 func TestFrameTypeString(t *testing.T) { 32 tests := []struct { 33 ft FrameType 34 want string 35 }{ 36 {FrameData, "DATA"}, 37 {FramePing, "PING"}, 38 {FrameGoAway, "GOAWAY"}, 39 {0xf, "UNKNOWN_FRAME_TYPE_15"}, 40 } 41 42 for i, tt := range tests { 43 got := tt.ft.String() 44 if got != tt.want { 45 t.Errorf("%d. String(FrameType %d) = %q; want %q", i, int(tt.ft), got, tt.want) 46 } 47 } 48 } 49 50 func TestWriteRST(t *testing.T) { 51 fr, buf := testFramer() 52 var streamID uint32 = 1<<24 + 2<<16 + 3<<8 + 4 53 var errCode uint32 = 7<<24 + 6<<16 + 5<<8 + 4 54 fr.WriteRSTStream(streamID, ErrCode(errCode)) 55 const wantEnc = "\x00\x00\x04\x03\x00\x01\x02\x03\x04\x07\x06\x05\x04" 56 if buf.String() != wantEnc { 57 t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc) 58 } 59 f, err := fr.ReadFrame() 60 if err != nil { 61 t.Fatal(err) 62 } 63 want := &RSTStreamFrame{ 64 FrameHeader: FrameHeader{ 65 valid: true, 66 Type: 0x3, 67 Flags: 0x0, 68 Length: 0x4, 69 StreamID: 0x1020304, 70 }, 71 ErrCode: 0x7060504, 72 } 73 if !reflect.DeepEqual(f, want) { 74 t.Errorf("parsed back %#v; want %#v", f, want) 75 } 76 } 77 78 func TestWriteData(t *testing.T) { 79 fr, buf := testFramer() 80 var streamID uint32 = 1<<24 + 2<<16 + 3<<8 + 4 81 data := []byte("ABC") 82 fr.WriteData(streamID, true, data) 83 const wantEnc = "\x00\x00\x03\x00\x01\x01\x02\x03\x04ABC" 84 if buf.String() != wantEnc { 85 t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc) 86 } 87 f, err := fr.ReadFrame() 88 if err != nil { 89 t.Fatal(err) 90 } 91 df, ok := f.(*DataFrame) 92 if !ok { 93 t.Fatalf("got %T; want *DataFrame", f) 94 } 95 if !bytes.Equal(df.Data(), data) { 96 t.Errorf("got %q; want %q", df.Data(), data) 97 } 98 if f.Header().Flags&1 == 0 { 99 t.Errorf("didn't see END_STREAM flag") 100 } 101 } 102 103 func TestWriteDataPadded(t *testing.T) { 104 tests := [...]struct { 105 streamID uint32 106 endStream bool 107 data []byte 108 pad []byte 109 wantHeader FrameHeader 110 }{ 111 // Unpadded: 112 0: { 113 streamID: 1, 114 endStream: true, 115 data: []byte("foo"), 116 pad: nil, 117 wantHeader: FrameHeader{ 118 Type: FrameData, 119 Flags: FlagDataEndStream, 120 Length: 3, 121 StreamID: 1, 122 }, 123 }, 124 125 // Padded bit set, but no padding: 126 1: { 127 streamID: 1, 128 endStream: true, 129 data: []byte("foo"), 130 pad: []byte{}, 131 wantHeader: FrameHeader{ 132 Type: FrameData, 133 Flags: FlagDataEndStream | FlagDataPadded, 134 Length: 4, 135 StreamID: 1, 136 }, 137 }, 138 139 // Padded bit set, with padding: 140 2: { 141 streamID: 1, 142 endStream: false, 143 data: []byte("foo"), 144 pad: []byte{0, 0, 0}, 145 wantHeader: FrameHeader{ 146 Type: FrameData, 147 Flags: FlagDataPadded, 148 Length: 7, 149 StreamID: 1, 150 }, 151 }, 152 } 153 for i, tt := range tests { 154 fr, _ := testFramer() 155 fr.WriteDataPadded(tt.streamID, tt.endStream, tt.data, tt.pad) 156 f, err := fr.ReadFrame() 157 if err != nil { 158 t.Errorf("%d. ReadFrame: %v", i, err) 159 continue 160 } 161 got := f.Header() 162 tt.wantHeader.valid = true 163 if !got.Equal(tt.wantHeader) { 164 t.Errorf("%d. read %+v; want %+v", i, got, tt.wantHeader) 165 continue 166 } 167 df := f.(*DataFrame) 168 if !bytes.Equal(df.Data(), tt.data) { 169 t.Errorf("%d. got %q; want %q", i, df.Data(), tt.data) 170 } 171 } 172 } 173 174 func (fh FrameHeader) Equal(b FrameHeader) bool { 175 return fh.valid == b.valid && 176 fh.Type == b.Type && 177 fh.Flags == b.Flags && 178 fh.Length == b.Length && 179 fh.StreamID == b.StreamID 180 } 181 182 func TestWriteHeaders(t *testing.T) { 183 tests := []struct { 184 name string 185 p HeadersFrameParam 186 wantEnc string 187 wantFrame *HeadersFrame 188 }{ 189 { 190 "basic", 191 HeadersFrameParam{ 192 StreamID: 42, 193 BlockFragment: []byte("abc"), 194 Priority: PriorityParam{}, 195 }, 196 "\x00\x00\x03\x01\x00\x00\x00\x00*abc", 197 &HeadersFrame{ 198 FrameHeader: FrameHeader{ 199 valid: true, 200 StreamID: 42, 201 Type: FrameHeaders, 202 Length: uint32(len("abc")), 203 }, 204 Priority: PriorityParam{}, 205 headerFragBuf: []byte("abc"), 206 }, 207 }, 208 { 209 "basic + end flags", 210 HeadersFrameParam{ 211 StreamID: 42, 212 BlockFragment: []byte("abc"), 213 EndStream: true, 214 EndHeaders: true, 215 Priority: PriorityParam{}, 216 }, 217 "\x00\x00\x03\x01\x05\x00\x00\x00*abc", 218 &HeadersFrame{ 219 FrameHeader: FrameHeader{ 220 valid: true, 221 StreamID: 42, 222 Type: FrameHeaders, 223 Flags: FlagHeadersEndStream | FlagHeadersEndHeaders, 224 Length: uint32(len("abc")), 225 }, 226 Priority: PriorityParam{}, 227 headerFragBuf: []byte("abc"), 228 }, 229 }, 230 { 231 "with padding", 232 HeadersFrameParam{ 233 StreamID: 42, 234 BlockFragment: []byte("abc"), 235 EndStream: true, 236 EndHeaders: true, 237 PadLength: 5, 238 Priority: PriorityParam{}, 239 }, 240 "\x00\x00\t\x01\r\x00\x00\x00*\x05abc\x00\x00\x00\x00\x00", 241 &HeadersFrame{ 242 FrameHeader: FrameHeader{ 243 valid: true, 244 StreamID: 42, 245 Type: FrameHeaders, 246 Flags: FlagHeadersEndStream | FlagHeadersEndHeaders | FlagHeadersPadded, 247 Length: uint32(1 + len("abc") + 5), // pad length + contents + padding 248 }, 249 Priority: PriorityParam{}, 250 headerFragBuf: []byte("abc"), 251 }, 252 }, 253 { 254 "with priority", 255 HeadersFrameParam{ 256 StreamID: 42, 257 BlockFragment: []byte("abc"), 258 EndStream: true, 259 EndHeaders: true, 260 PadLength: 2, 261 Priority: PriorityParam{ 262 StreamDep: 15, 263 Exclusive: true, 264 Weight: 127, 265 }, 266 }, 267 "\x00\x00\v\x01-\x00\x00\x00*\x02\x80\x00\x00\x0f\u007fabc\x00\x00", 268 &HeadersFrame{ 269 FrameHeader: FrameHeader{ 270 valid: true, 271 StreamID: 42, 272 Type: FrameHeaders, 273 Flags: FlagHeadersEndStream | FlagHeadersEndHeaders | FlagHeadersPadded | FlagHeadersPriority, 274 Length: uint32(1 + 5 + len("abc") + 2), // pad length + priority + contents + padding 275 }, 276 Priority: PriorityParam{ 277 StreamDep: 15, 278 Exclusive: true, 279 Weight: 127, 280 }, 281 headerFragBuf: []byte("abc"), 282 }, 283 }, 284 { 285 "with priority stream dep zero", // golang.org/issue/15444 286 HeadersFrameParam{ 287 StreamID: 42, 288 BlockFragment: []byte("abc"), 289 EndStream: true, 290 EndHeaders: true, 291 PadLength: 2, 292 Priority: PriorityParam{ 293 StreamDep: 0, 294 Exclusive: true, 295 Weight: 127, 296 }, 297 }, 298 "\x00\x00\v\x01-\x00\x00\x00*\x02\x80\x00\x00\x00\u007fabc\x00\x00", 299 &HeadersFrame{ 300 FrameHeader: FrameHeader{ 301 valid: true, 302 StreamID: 42, 303 Type: FrameHeaders, 304 Flags: FlagHeadersEndStream | FlagHeadersEndHeaders | FlagHeadersPadded | FlagHeadersPriority, 305 Length: uint32(1 + 5 + len("abc") + 2), // pad length + priority + contents + padding 306 }, 307 Priority: PriorityParam{ 308 StreamDep: 0, 309 Exclusive: true, 310 Weight: 127, 311 }, 312 headerFragBuf: []byte("abc"), 313 }, 314 }, 315 } 316 for _, tt := range tests { 317 fr, buf := testFramer() 318 if err := fr.WriteHeaders(tt.p); err != nil { 319 t.Errorf("test %q: %v", tt.name, err) 320 continue 321 } 322 if buf.String() != tt.wantEnc { 323 t.Errorf("test %q: encoded %q; want %q", tt.name, buf.Bytes(), tt.wantEnc) 324 } 325 f, err := fr.ReadFrame() 326 if err != nil { 327 t.Errorf("test %q: failed to read the frame back: %v", tt.name, err) 328 continue 329 } 330 if !reflect.DeepEqual(f, tt.wantFrame) { 331 t.Errorf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tt.name, f, tt.wantFrame) 332 } 333 } 334 } 335 336 func TestWriteInvalidStreamDep(t *testing.T) { 337 fr, _ := testFramer() 338 err := fr.WriteHeaders(HeadersFrameParam{ 339 StreamID: 42, 340 Priority: PriorityParam{ 341 StreamDep: 1 << 31, 342 }, 343 }) 344 if err != errDepStreamID { 345 t.Errorf("header error = %v; want %q", err, errDepStreamID) 346 } 347 348 err = fr.WritePriority(2, PriorityParam{StreamDep: 1 << 31}) 349 if err != errDepStreamID { 350 t.Errorf("priority error = %v; want %q", err, errDepStreamID) 351 } 352 } 353 354 func TestWriteContinuation(t *testing.T) { 355 const streamID = 42 356 tests := []struct { 357 name string 358 end bool 359 frag []byte 360 361 wantFrame *ContinuationFrame 362 }{ 363 { 364 "not end", 365 false, 366 []byte("abc"), 367 &ContinuationFrame{ 368 FrameHeader: FrameHeader{ 369 valid: true, 370 StreamID: streamID, 371 Type: FrameContinuation, 372 Length: uint32(len("abc")), 373 }, 374 headerFragBuf: []byte("abc"), 375 }, 376 }, 377 { 378 "end", 379 true, 380 []byte("def"), 381 &ContinuationFrame{ 382 FrameHeader: FrameHeader{ 383 valid: true, 384 StreamID: streamID, 385 Type: FrameContinuation, 386 Flags: FlagContinuationEndHeaders, 387 Length: uint32(len("def")), 388 }, 389 headerFragBuf: []byte("def"), 390 }, 391 }, 392 } 393 for _, tt := range tests { 394 fr, _ := testFramer() 395 if err := fr.WriteContinuation(streamID, tt.end, tt.frag); err != nil { 396 t.Errorf("test %q: %v", tt.name, err) 397 continue 398 } 399 fr.AllowIllegalReads = true 400 f, err := fr.ReadFrame() 401 if err != nil { 402 t.Errorf("test %q: failed to read the frame back: %v", tt.name, err) 403 continue 404 } 405 if !reflect.DeepEqual(f, tt.wantFrame) { 406 t.Errorf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tt.name, f, tt.wantFrame) 407 } 408 } 409 } 410 411 func TestWritePriority(t *testing.T) { 412 const streamID = 42 413 tests := []struct { 414 name string 415 priority PriorityParam 416 wantFrame *PriorityFrame 417 }{ 418 { 419 "not exclusive", 420 PriorityParam{ 421 StreamDep: 2, 422 Exclusive: false, 423 Weight: 127, 424 }, 425 &PriorityFrame{ 426 FrameHeader{ 427 valid: true, 428 StreamID: streamID, 429 Type: FramePriority, 430 Length: 5, 431 }, 432 PriorityParam{ 433 StreamDep: 2, 434 Exclusive: false, 435 Weight: 127, 436 }, 437 }, 438 }, 439 440 { 441 "exclusive", 442 PriorityParam{ 443 StreamDep: 3, 444 Exclusive: true, 445 Weight: 77, 446 }, 447 &PriorityFrame{ 448 FrameHeader{ 449 valid: true, 450 StreamID: streamID, 451 Type: FramePriority, 452 Length: 5, 453 }, 454 PriorityParam{ 455 StreamDep: 3, 456 Exclusive: true, 457 Weight: 77, 458 }, 459 }, 460 }, 461 } 462 for _, tt := range tests { 463 fr, _ := testFramer() 464 if err := fr.WritePriority(streamID, tt.priority); err != nil { 465 t.Errorf("test %q: %v", tt.name, err) 466 continue 467 } 468 f, err := fr.ReadFrame() 469 if err != nil { 470 t.Errorf("test %q: failed to read the frame back: %v", tt.name, err) 471 continue 472 } 473 if !reflect.DeepEqual(f, tt.wantFrame) { 474 t.Errorf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tt.name, f, tt.wantFrame) 475 } 476 } 477 } 478 479 func TestWriteSettings(t *testing.T) { 480 fr, buf := testFramer() 481 settings := []Setting{{1, 2}, {3, 4}} 482 fr.WriteSettings(settings...) 483 const wantEnc = "\x00\x00\f\x04\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x03\x00\x00\x00\x04" 484 if buf.String() != wantEnc { 485 t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc) 486 } 487 f, err := fr.ReadFrame() 488 if err != nil { 489 t.Fatal(err) 490 } 491 sf, ok := f.(*SettingsFrame) 492 if !ok { 493 t.Fatalf("Got a %T; want a SettingsFrame", f) 494 } 495 var got []Setting 496 sf.ForeachSetting(func(s Setting) error { 497 got = append(got, s) 498 valBack, ok := sf.Value(s.ID) 499 if !ok || valBack != s.Val { 500 t.Errorf("Value(%d) = %v, %v; want %v, true", s.ID, valBack, ok, s.Val) 501 } 502 return nil 503 }) 504 if !reflect.DeepEqual(settings, got) { 505 t.Errorf("Read settings %+v != written settings %+v", got, settings) 506 } 507 } 508 509 func TestWriteSettingsAck(t *testing.T) { 510 fr, buf := testFramer() 511 fr.WriteSettingsAck() 512 const wantEnc = "\x00\x00\x00\x04\x01\x00\x00\x00\x00" 513 if buf.String() != wantEnc { 514 t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc) 515 } 516 } 517 518 func TestWriteWindowUpdate(t *testing.T) { 519 fr, buf := testFramer() 520 const streamID = 1<<24 + 2<<16 + 3<<8 + 4 521 const incr = 7<<24 + 6<<16 + 5<<8 + 4 522 if err := fr.WriteWindowUpdate(streamID, incr); err != nil { 523 t.Fatal(err) 524 } 525 const wantEnc = "\x00\x00\x04\x08\x00\x01\x02\x03\x04\x07\x06\x05\x04" 526 if buf.String() != wantEnc { 527 t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc) 528 } 529 f, err := fr.ReadFrame() 530 if err != nil { 531 t.Fatal(err) 532 } 533 want := &WindowUpdateFrame{ 534 FrameHeader: FrameHeader{ 535 valid: true, 536 Type: 0x8, 537 Flags: 0x0, 538 Length: 0x4, 539 StreamID: 0x1020304, 540 }, 541 Increment: 0x7060504, 542 } 543 if !reflect.DeepEqual(f, want) { 544 t.Errorf("parsed back %#v; want %#v", f, want) 545 } 546 } 547 548 func TestWritePing(t *testing.T) { testWritePing(t, false) } 549 func TestWritePingAck(t *testing.T) { testWritePing(t, true) } 550 551 func testWritePing(t *testing.T, ack bool) { 552 fr, buf := testFramer() 553 if err := fr.WritePing(ack, [8]byte{1, 2, 3, 4, 5, 6, 7, 8}); err != nil { 554 t.Fatal(err) 555 } 556 var wantFlags Flags 557 if ack { 558 wantFlags = FlagPingAck 559 } 560 var wantEnc = "\x00\x00\x08\x06" + string(wantFlags) + "\x00\x00\x00\x00" + "\x01\x02\x03\x04\x05\x06\x07\x08" 561 if buf.String() != wantEnc { 562 t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc) 563 } 564 565 f, err := fr.ReadFrame() 566 if err != nil { 567 t.Fatal(err) 568 } 569 want := &PingFrame{ 570 FrameHeader: FrameHeader{ 571 valid: true, 572 Type: 0x6, 573 Flags: wantFlags, 574 Length: 0x8, 575 StreamID: 0, 576 }, 577 Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}, 578 } 579 if !reflect.DeepEqual(f, want) { 580 t.Errorf("parsed back %#v; want %#v", f, want) 581 } 582 } 583 584 func TestReadFrameHeader(t *testing.T) { 585 tests := []struct { 586 in string 587 want FrameHeader 588 }{ 589 {in: "\x00\x00\x00" + "\x00" + "\x00" + "\x00\x00\x00\x00", want: FrameHeader{}}, 590 {in: "\x01\x02\x03" + "\x04" + "\x05" + "\x06\x07\x08\x09", want: FrameHeader{ 591 Length: 66051, Type: 4, Flags: 5, StreamID: 101124105, 592 }}, 593 // Ignore high bit: 594 {in: "\xff\xff\xff" + "\xff" + "\xff" + "\xff\xff\xff\xff", want: FrameHeader{ 595 Length: 16777215, Type: 255, Flags: 255, StreamID: 2147483647}}, 596 {in: "\xff\xff\xff" + "\xff" + "\xff" + "\x7f\xff\xff\xff", want: FrameHeader{ 597 Length: 16777215, Type: 255, Flags: 255, StreamID: 2147483647}}, 598 } 599 for i, tt := range tests { 600 got, err := readFrameHeader(make([]byte, 9), strings.NewReader(tt.in)) 601 if err != nil { 602 t.Errorf("%d. readFrameHeader(%q) = %v", i, tt.in, err) 603 continue 604 } 605 tt.want.valid = true 606 if !got.Equal(tt.want) { 607 t.Errorf("%d. readFrameHeader(%q) = %+v; want %+v", i, tt.in, got, tt.want) 608 } 609 } 610 } 611 612 func TestReadWriteFrameHeader(t *testing.T) { 613 tests := []struct { 614 len uint32 615 typ FrameType 616 flags Flags 617 streamID uint32 618 }{ 619 {len: 0, typ: 255, flags: 1, streamID: 0}, 620 {len: 0, typ: 255, flags: 1, streamID: 1}, 621 {len: 0, typ: 255, flags: 1, streamID: 255}, 622 {len: 0, typ: 255, flags: 1, streamID: 256}, 623 {len: 0, typ: 255, flags: 1, streamID: 65535}, 624 {len: 0, typ: 255, flags: 1, streamID: 65536}, 625 626 {len: 0, typ: 1, flags: 255, streamID: 1}, 627 {len: 255, typ: 1, flags: 255, streamID: 1}, 628 {len: 256, typ: 1, flags: 255, streamID: 1}, 629 {len: 65535, typ: 1, flags: 255, streamID: 1}, 630 {len: 65536, typ: 1, flags: 255, streamID: 1}, 631 {len: 16777215, typ: 1, flags: 255, streamID: 1}, 632 } 633 for _, tt := range tests { 634 fr, buf := testFramer() 635 fr.startWrite(tt.typ, tt.flags, tt.streamID) 636 fr.writeBytes(make([]byte, tt.len)) 637 fr.endWrite() 638 fh, err := ReadFrameHeader(buf) 639 if err != nil { 640 t.Errorf("ReadFrameHeader(%+v) = %v", tt, err) 641 continue 642 } 643 if fh.Type != tt.typ || fh.Flags != tt.flags || fh.Length != tt.len || fh.StreamID != tt.streamID { 644 t.Errorf("ReadFrameHeader(%+v) = %+v; mismatch", tt, fh) 645 } 646 } 647 648 } 649 650 func TestWriteTooLargeFrame(t *testing.T) { 651 fr, _ := testFramer() 652 fr.startWrite(0, 1, 1) 653 fr.writeBytes(make([]byte, 1<<24)) 654 err := fr.endWrite() 655 if err != ErrFrameTooLarge { 656 t.Errorf("endWrite = %v; want errFrameTooLarge", err) 657 } 658 } 659 660 func TestWriteGoAway(t *testing.T) { 661 const debug = "foo" 662 fr, buf := testFramer() 663 if err := fr.WriteGoAway(0x01020304, 0x05060708, []byte(debug)); err != nil { 664 t.Fatal(err) 665 } 666 const wantEnc = "\x00\x00\v\a\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08" + debug 667 if buf.String() != wantEnc { 668 t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc) 669 } 670 f, err := fr.ReadFrame() 671 if err != nil { 672 t.Fatal(err) 673 } 674 want := &GoAwayFrame{ 675 FrameHeader: FrameHeader{ 676 valid: true, 677 Type: 0x7, 678 Flags: 0, 679 Length: uint32(4 + 4 + len(debug)), 680 StreamID: 0, 681 }, 682 LastStreamID: 0x01020304, 683 ErrCode: 0x05060708, 684 debugData: []byte(debug), 685 } 686 if !reflect.DeepEqual(f, want) { 687 t.Fatalf("parsed back:\n%#v\nwant:\n%#v", f, want) 688 } 689 if got := string(f.(*GoAwayFrame).DebugData()); got != debug { 690 t.Errorf("debug data = %q; want %q", got, debug) 691 } 692 } 693 694 func TestWritePushPromise(t *testing.T) { 695 pp := PushPromiseParam{ 696 StreamID: 42, 697 PromiseID: 42, 698 BlockFragment: []byte("abc"), 699 } 700 fr, buf := testFramer() 701 if err := fr.WritePushPromise(pp); err != nil { 702 t.Fatal(err) 703 } 704 const wantEnc = "\x00\x00\x07\x05\x00\x00\x00\x00*\x00\x00\x00*abc" 705 if buf.String() != wantEnc { 706 t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc) 707 } 708 f, err := fr.ReadFrame() 709 if err != nil { 710 t.Fatal(err) 711 } 712 _, ok := f.(*PushPromiseFrame) 713 if !ok { 714 t.Fatalf("got %T; want *PushPromiseFrame", f) 715 } 716 want := &PushPromiseFrame{ 717 FrameHeader: FrameHeader{ 718 valid: true, 719 Type: 0x5, 720 Flags: 0x0, 721 Length: 0x7, 722 StreamID: 42, 723 }, 724 PromiseID: 42, 725 headerFragBuf: []byte("abc"), 726 } 727 if !reflect.DeepEqual(f, want) { 728 t.Fatalf("parsed back:\n%#v\nwant:\n%#v", f, want) 729 } 730 } 731 732 // test checkFrameOrder and that HEADERS/PUSH_PROMISE and CONTINUATION frames can't be intermingled. 733 func TestReadFrameOrder(t *testing.T) { 734 head := func(f *Framer, id uint32, end bool) { 735 f.WriteHeaders(HeadersFrameParam{ 736 StreamID: id, 737 BlockFragment: []byte("foo"), // unused, but non-empty 738 EndHeaders: end, 739 }) 740 } 741 push := func(f *Framer, id uint32, end bool) { 742 f.WritePushPromise(PushPromiseParam{ 743 StreamID: id, 744 BlockFragment: []byte("foo"), 745 EndHeaders: end, 746 }) 747 } 748 cont := func(f *Framer, id uint32, end bool) { 749 f.WriteContinuation(id, end, []byte("foo")) 750 } 751 752 tests := [...]struct { 753 name string 754 w func(*Framer) 755 atLeast int 756 wantErr string 757 }{ 758 0: { 759 w: func(f *Framer) { 760 head(f, 1, true) 761 }, 762 }, 763 1: { 764 w: func(f *Framer) { 765 push(f, 1, true) 766 }, 767 }, 768 2: { 769 w: func(f *Framer) { 770 head(f, 1, true) 771 head(f, 2, true) 772 }, 773 }, 774 3: { 775 w: func(f *Framer) { 776 push(f, 1, true) 777 push(f, 2, true) 778 }, 779 }, 780 4: { 781 wantErr: "got HEADERS for stream 2; expected CONTINUATION following HEADERS for stream 1", 782 w: func(f *Framer) { 783 head(f, 1, false) 784 head(f, 2, true) 785 }, 786 }, 787 5: { 788 wantErr: "got PUSH_PROMISE for stream 2; expected CONTINUATION following PUSH_PROMISE for stream 1", 789 w: func(f *Framer) { 790 push(f, 1, false) 791 push(f, 2, true) 792 }, 793 }, 794 6: { 795 wantErr: "got DATA for stream 1; expected CONTINUATION following HEADERS for stream 1", 796 w: func(f *Framer) { 797 head(f, 1, false) 798 }, 799 }, 800 7: { 801 wantErr: "got DATA for stream 1; expected CONTINUATION following PUSH_PROMISE for stream 1", 802 w: func(f *Framer) { 803 push(f, 1, false) 804 }, 805 }, 806 8: { 807 w: func(f *Framer) { 808 head(f, 1, false) 809 cont(f, 1, true) 810 head(f, 2, true) 811 }, 812 }, 813 9: { 814 w: func(f *Framer) { 815 push(f, 1, false) 816 cont(f, 1, true) 817 push(f, 2, true) 818 }, 819 }, 820 10: { 821 wantErr: "got CONTINUATION for stream 2; expected stream 1", 822 w: func(f *Framer) { 823 head(f, 1, false) 824 cont(f, 2, true) 825 head(f, 2, true) 826 }, 827 }, 828 11: { 829 wantErr: "got CONTINUATION for stream 2; expected stream 1", 830 w: func(f *Framer) { 831 push(f, 1, false) 832 cont(f, 2, true) 833 push(f, 2, true) 834 }, 835 }, 836 12: { 837 wantErr: "unexpected CONTINUATION for stream 1", 838 w: func(f *Framer) { 839 cont(f, 1, true) 840 }, 841 }, 842 13: { 843 wantErr: "unexpected CONTINUATION for stream 1", 844 w: func(f *Framer) { 845 cont(f, 1, false) 846 }, 847 }, 848 14: { 849 wantErr: "HEADERS frame with stream ID 0", 850 w: func(f *Framer) { 851 head(f, 0, true) 852 }, 853 }, 854 15: { 855 wantErr: "PUSH_PROMISE frame with stream ID 0", 856 w: func(f *Framer) { 857 push(f, 0, true) 858 }, 859 }, 860 16: { 861 wantErr: "CONTINUATION frame with stream ID 0", 862 w: func(f *Framer) { 863 cont(f, 0, true) 864 }, 865 }, 866 17: { 867 wantErr: "unexpected CONTINUATION for stream 1", 868 atLeast: 5, 869 w: func(f *Framer) { 870 head(f, 1, false) 871 cont(f, 1, false) 872 cont(f, 1, false) 873 cont(f, 1, false) 874 cont(f, 1, true) 875 cont(f, 1, false) 876 }, 877 }, 878 18: { 879 wantErr: "unexpected CONTINUATION for stream 1", 880 atLeast: 5, 881 w: func(f *Framer) { 882 push(f, 1, false) 883 cont(f, 1, false) 884 cont(f, 1, false) 885 cont(f, 1, false) 886 cont(f, 1, true) 887 cont(f, 1, false) 888 }, 889 }, 890 } 891 892 for i, tt := range tests { 893 buf := new(bytes.Buffer) 894 f := NewFramer(buf, buf) 895 f.AllowIllegalWrites = true 896 tt.w(f) 897 f.WriteData(1, true, nil) // to test transition away from last step 898 899 var err error 900 n := 0 901 var log bytes.Buffer 902 for { 903 var got Frame 904 got, err = f.ReadFrame() 905 fmt.Fprintf(&log, " read %v, %v\n", got, err) 906 if err != nil { 907 break 908 } 909 n++ 910 } 911 if err == io.EOF { 912 err = nil 913 } 914 ok := tt.wantErr == "" 915 if ok && err != nil { 916 t.Errorf("%d. after %d good frames, ReadFrame = %v; want success\n%s", i, n, err, log.Bytes()) 917 continue 918 } 919 if !ok && err != ConnectionError(ErrCodeProtocol) { 920 t.Errorf("%d. after %d good frames, ReadFrame = %v; want ConnectionError(ErrCodeProtocol)\n%s", i, n, err, log.Bytes()) 921 continue 922 } 923 if !((f.errDetail == nil && tt.wantErr == "") || (fmt.Sprint(f.errDetail) == tt.wantErr)) { 924 t.Errorf("%d. framer error = %q; want %q\n%s", i, f.errDetail, tt.wantErr, log.Bytes()) 925 } 926 if n < tt.atLeast { 927 t.Errorf("%d. framer only read %d frames; want at least %d\n%s", i, n, tt.atLeast, log.Bytes()) 928 } 929 } 930 } 931 932 func testMetaFrame(t *testing.T, 933 write func(f *Framer, frags ...[]byte), 934 want func(flags Flags, length uint32, pairs ...string) Frame, 935 headersCmp func(t *testing.T, testName string, gotMetaFrame, wantMetaFrame interface{}), 936 lengthIncrementBeyondMetaHeaderFrame uint32) { 937 938 truncated := func(f Frame) Frame { 939 switch f := f.(type) { 940 case *MetaHeadersFrame: 941 f.Truncated = true 942 case *MetaPushPromiseFrame: 943 f.Truncated = true 944 default: 945 panic("not a meta frame") 946 } 947 return f 948 949 } 950 const flagEndHeaders Flags = 0x4 // same for all end headers flag 951 const noFlags Flags = 0 952 953 oneKBString := strings.Repeat("a", 1<<10) 954 955 tests := [...]struct { 956 name string 957 w func(*Framer) 958 want interface{} // meta frame or error 959 wantErrReason string 960 maxHeaderListSize uint32 961 }{ 962 0: { 963 name: "single_headers", 964 w: func(f *Framer) { 965 var he hpackEncoder 966 all := he.encodeHeaderRaw(t, ":method", "GET", ":path", "/") 967 write(f, all) 968 }, 969 want: want(flagEndHeaders, 2+lengthIncrementBeyondMetaHeaderFrame, ":method", "GET", ":path", "/"), 970 }, 971 1: { 972 name: "with_continuation", 973 w: func(f *Framer) { 974 var he hpackEncoder 975 all := he.encodeHeaderRaw(t, ":method", "GET", ":path", "/", "foo", "bar") 976 write(f, all[:1], all[1:]) 977 }, 978 want: want(noFlags, 1+lengthIncrementBeyondMetaHeaderFrame, ":method", "GET", ":path", "/", "foo", "bar"), 979 }, 980 2: { 981 name: "with_two_continuation", 982 w: func(f *Framer) { 983 var he hpackEncoder 984 all := he.encodeHeaderRaw(t, ":method", "GET", ":path", "/", "foo", "bar") 985 write(f, all[:2], all[2:4], all[4:]) 986 }, 987 want: want(noFlags, 2+lengthIncrementBeyondMetaHeaderFrame, ":method", "GET", ":path", "/", "foo", "bar"), 988 }, 989 3: { 990 name: "big_string_okay", 991 w: func(f *Framer) { 992 var he hpackEncoder 993 all := he.encodeHeaderRaw(t, ":method", "GET", ":path", "/", "foo", oneKBString) 994 write(f, all[:2], all[2:]) 995 }, 996 want: want(noFlags, 2+lengthIncrementBeyondMetaHeaderFrame, ":method", "GET", ":path", "/", "foo", oneKBString), 997 }, 998 4: { 999 name: "big_string_error", 1000 w: func(f *Framer) { 1001 var he hpackEncoder 1002 all := he.encodeHeaderRaw(t, ":method", "GET", ":path", "/", "foo", oneKBString) 1003 write(f, all[:2], all[2:]) 1004 }, 1005 maxHeaderListSize: (1 << 10) / 2, 1006 want: ConnectionError(ErrCodeCompression), 1007 }, 1008 5: { 1009 name: "max_header_list_truncated", 1010 w: func(f *Framer) { 1011 var he hpackEncoder 1012 var pairs = []string{":method", "GET", ":path", "/"} 1013 for i := 0; i < 100; i++ { 1014 pairs = append(pairs, "foo", "bar") 1015 } 1016 all := he.encodeHeaderRaw(t, pairs...) 1017 write(f, all[:2], all[2:]) 1018 }, 1019 maxHeaderListSize: (1 << 10) / 2, 1020 want: truncated(want(noFlags, 2+lengthIncrementBeyondMetaHeaderFrame, 1021 ":method", "GET", 1022 ":path", "/", 1023 "foo", "bar", 1024 "foo", "bar", 1025 "foo", "bar", 1026 "foo", "bar", 1027 "foo", "bar", 1028 "foo", "bar", 1029 "foo", "bar", 1030 "foo", "bar", 1031 "foo", "bar", 1032 "foo", "bar", 1033 "foo", "bar", // 11 1034 )), 1035 }, 1036 6: { 1037 name: "pseudo_order", 1038 w: func(f *Framer) { 1039 write(f, encodeHeaderRaw(t, 1040 ":method", "GET", 1041 "foo", "bar", 1042 ":path", "/", // bogus 1043 )) 1044 }, 1045 want: streamError(1, ErrCodeProtocol), 1046 wantErrReason: "pseudo header field after regular", 1047 }, 1048 7: { 1049 name: "pseudo_unknown", 1050 w: func(f *Framer) { 1051 write(f, encodeHeaderRaw(t, 1052 ":unknown", "foo", // bogus 1053 "foo", "bar", 1054 )) 1055 }, 1056 want: streamError(1, ErrCodeProtocol), 1057 wantErrReason: "invalid pseudo-header \":unknown\"", 1058 }, 1059 8: { 1060 name: "pseudo_mix_request_response", 1061 w: func(f *Framer) { 1062 write(f, encodeHeaderRaw(t, 1063 ":method", "GET", 1064 ":status", "100", 1065 )) 1066 }, 1067 want: streamError(1, ErrCodeProtocol), 1068 wantErrReason: "mix of request and response pseudo headers", 1069 }, 1070 9: { 1071 name: "pseudo_dup", 1072 w: func(f *Framer) { 1073 write(f, encodeHeaderRaw(t, 1074 ":method", "GET", 1075 ":method", "POST", 1076 )) 1077 }, 1078 want: streamError(1, ErrCodeProtocol), 1079 wantErrReason: "duplicate pseudo-header \":method\"", 1080 }, 1081 10: { 1082 name: "trailer_okay_no_pseudo", 1083 w: func(f *Framer) { write(f, encodeHeaderRaw(t, "foo", "bar")) }, 1084 want: want(flagEndHeaders, 8+lengthIncrementBeyondMetaHeaderFrame, "foo", "bar"), 1085 }, 1086 11: { 1087 name: "invalid_field_name", 1088 w: func(f *Framer) { write(f, encodeHeaderRaw(t, "CapitalBad", "x")) }, 1089 want: streamError(1, ErrCodeProtocol), 1090 wantErrReason: "invalid header field name \"CapitalBad\"", 1091 }, 1092 12: { 1093 name: "invalid_field_value", 1094 w: func(f *Framer) { write(f, encodeHeaderRaw(t, "key", "bad_null\x00")) }, 1095 want: streamError(1, ErrCodeProtocol), 1096 wantErrReason: "invalid header field value \"bad_null\\x00\"", 1097 }, 1098 } 1099 for i, tt := range tests { 1100 buf := new(bytes.Buffer) 1101 f := NewFramer(buf, buf) 1102 f.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil) 1103 f.MaxHeaderListSize = tt.maxHeaderListSize 1104 tt.w(f) 1105 1106 name := tt.name 1107 if name == "" { 1108 name = fmt.Sprintf("test index %d", i) 1109 } 1110 1111 var got interface{} 1112 var err error 1113 got, err = f.ReadFrame() 1114 if err != nil { 1115 got = err 1116 1117 // Ignore the StreamError.Cause field, if it matches the wantErrReason. 1118 // The test table above predates the Cause field. 1119 if se, ok := err.(StreamError); ok && se.Cause != nil && se.Cause.Error() == tt.wantErrReason { 1120 se.Cause = nil 1121 got = se 1122 } 1123 } 1124 if !reflect.DeepEqual(got, tt.want) { 1125 headersCmp(t, name, got, want) 1126 str := func(v interface{}) string { 1127 if _, ok := v.(error); ok { 1128 return fmt.Sprintf("error %v", v) 1129 } else { 1130 return fmt.Sprintf("value %#v", v) 1131 } 1132 } 1133 t.Errorf("%s:\n got: %v\nwant: %s", name, str(got), str(tt.want)) 1134 } 1135 if tt.wantErrReason != "" && tt.wantErrReason != fmt.Sprint(f.errDetail) { 1136 t.Errorf("%s: got error reason %q; want %q", name, f.errDetail, tt.wantErrReason) 1137 } 1138 } 1139 } 1140 1141 func TestMetaHeaderFrame(t *testing.T) { 1142 write := func(f *Framer, frags ...[]byte) { 1143 for i, frag := range frags { 1144 end := (i == len(frags)-1) 1145 if i == 0 { 1146 f.WriteHeaders(HeadersFrameParam{ 1147 StreamID: 1, 1148 BlockFragment: frag, 1149 EndHeaders: end, 1150 }) 1151 } else { 1152 f.WriteContinuation(1, end, frag) 1153 } 1154 } 1155 } 1156 want := func(flags Flags, length uint32, pairs ...string) Frame { 1157 mh := &MetaHeadersFrame{ 1158 HeadersFrame: &HeadersFrame{ 1159 FrameHeader: FrameHeader{ 1160 Type: FrameHeaders, 1161 Flags: flags, 1162 Length: length, 1163 StreamID: 1, 1164 }, 1165 }, 1166 Fields: []hpack.HeaderField(nil), 1167 } 1168 for len(pairs) > 0 { 1169 mh.Fields = append(mh.Fields, hpack.HeaderField{ 1170 Name: pairs[0], 1171 Value: pairs[1], 1172 }) 1173 pairs = pairs[2:] 1174 } 1175 return mh 1176 } 1177 headersCmp := func(t *testing.T, testName string, got, want interface{}) { 1178 if mhg, ok := got.(*MetaHeadersFrame); ok { 1179 if mhw, ok := want.(*MetaHeadersFrame); ok { 1180 hg := mhg.HeadersFrame 1181 hw := mhw.HeadersFrame 1182 if hg != nil && hw != nil && !reflect.DeepEqual(*hg, *hw) { 1183 t.Errorf("%s: headers differ:\n got: %+v\nwant: %+v\n", testName, *hg, *hw) 1184 } 1185 } 1186 } 1187 } 1188 testMetaFrame(t, write, want, headersCmp, 0) 1189 } 1190 func TestMetaPushPromiseFrame(t *testing.T) { 1191 write := func(f *Framer, frags ...[]byte) { 1192 for i, frag := range frags { 1193 end := (i == len(frags)-1) 1194 if i == 0 { 1195 err := f.WritePushPromise(PushPromiseParam{ 1196 StreamID: 1, 1197 PromiseID: 2, 1198 BlockFragment: frag, 1199 EndHeaders: end, 1200 }) 1201 if err != nil { 1202 t.Error(err) 1203 } 1204 } else { 1205 f.WriteContinuation(1, end, frag) 1206 } 1207 } 1208 } 1209 want := func(flags Flags, length uint32, pairs ...string) Frame { 1210 mh := &MetaPushPromiseFrame{ 1211 PushPromiseFrame: &PushPromiseFrame{ 1212 FrameHeader: FrameHeader{ 1213 Type: FramePushPromise, 1214 Flags: flags, 1215 Length: length, 1216 StreamID: 1, 1217 }, 1218 PromiseID: 2, 1219 }, 1220 Fields: []hpack.HeaderField(nil), 1221 } 1222 for len(pairs) > 0 { 1223 mh.Fields = append(mh.Fields, hpack.HeaderField{ 1224 Name: pairs[0], 1225 Value: pairs[1], 1226 }) 1227 pairs = pairs[2:] 1228 } 1229 return mh 1230 } 1231 headersCmp := func(t *testing.T, testName string, got, want interface{}) { 1232 if mhg, ok := got.(*MetaPushPromiseFrame); ok { 1233 if mhw, ok := want.(*MetaPushPromiseFrame); ok { 1234 hg := mhg.PushPromiseFrame 1235 hw := mhw.PushPromiseFrame 1236 if hg != nil && hw != nil && !reflect.DeepEqual(*hg, *hw) { 1237 t.Errorf("%s: headers differ:\n got: %+v\nwant: %+v\n", testName, *hg, *hw) 1238 } 1239 } 1240 } 1241 } 1242 testMetaFrame(t, write, want, headersCmp, 4) 1243 } 1244 1245 func TestSetReuseFrames(t *testing.T) { 1246 fr, buf := testFramer() 1247 fr.SetReuseFrames() 1248 1249 // Check that DataFrames are reused. Note that 1250 // SetReuseFrames only currently implements reuse of DataFrames. 1251 firstDf := readAndVerifyDataFrame("ABC", 3, fr, buf, t) 1252 1253 for i := 0; i < 10; i++ { 1254 df := readAndVerifyDataFrame("XYZ", 3, fr, buf, t) 1255 if df != firstDf { 1256 t.Errorf("Expected Framer to return references to the same DataFrame. Have %v and %v", &df, &firstDf) 1257 } 1258 } 1259 1260 for i := 0; i < 10; i++ { 1261 df := readAndVerifyDataFrame("", 0, fr, buf, t) 1262 if df != firstDf { 1263 t.Errorf("Expected Framer to return references to the same DataFrame. Have %v and %v", &df, &firstDf) 1264 } 1265 } 1266 1267 for i := 0; i < 10; i++ { 1268 df := readAndVerifyDataFrame("HHH", 3, fr, buf, t) 1269 if df != firstDf { 1270 t.Errorf("Expected Framer to return references to the same DataFrame. Have %v and %v", &df, &firstDf) 1271 } 1272 } 1273 } 1274 1275 func TestSetReuseFramesMoreThanOnce(t *testing.T) { 1276 fr, buf := testFramer() 1277 fr.SetReuseFrames() 1278 1279 firstDf := readAndVerifyDataFrame("ABC", 3, fr, buf, t) 1280 fr.SetReuseFrames() 1281 1282 for i := 0; i < 10; i++ { 1283 df := readAndVerifyDataFrame("XYZ", 3, fr, buf, t) 1284 // SetReuseFrames should be idempotent 1285 fr.SetReuseFrames() 1286 if df != firstDf { 1287 t.Errorf("Expected Framer to return references to the same DataFrame. Have %v and %v", &df, &firstDf) 1288 } 1289 } 1290 } 1291 1292 func TestNoSetReuseFrames(t *testing.T) { 1293 fr, buf := testFramer() 1294 const numNewDataFrames = 10 1295 dfSoFar := make([]interface{}, numNewDataFrames) 1296 1297 // Check that DataFrames are not reused if SetReuseFrames wasn't called. 1298 // SetReuseFrames only currently implements reuse of DataFrames. 1299 for i := 0; i < numNewDataFrames; i++ { 1300 df := readAndVerifyDataFrame("XYZ", 3, fr, buf, t) 1301 for _, item := range dfSoFar { 1302 if df == item { 1303 t.Errorf("Expected Framer to return new DataFrames since SetNoReuseFrames not set.") 1304 } 1305 } 1306 dfSoFar[i] = df 1307 } 1308 } 1309 1310 func readAndVerifyDataFrame(data string, length byte, fr *Framer, buf *bytes.Buffer, t *testing.T) *DataFrame { 1311 var streamID uint32 = 1<<24 + 2<<16 + 3<<8 + 4 1312 fr.WriteData(streamID, true, []byte(data)) 1313 wantEnc := "\x00\x00" + string(length) + "\x00\x01\x01\x02\x03\x04" + data 1314 if buf.String() != wantEnc { 1315 t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc) 1316 } 1317 f, err := fr.ReadFrame() 1318 if err != nil { 1319 t.Fatal(err) 1320 } 1321 df, ok := f.(*DataFrame) 1322 if !ok { 1323 t.Fatalf("got %T; want *DataFrame", f) 1324 } 1325 if !bytes.Equal(df.Data(), []byte(data)) { 1326 t.Errorf("got %q; want %q", df.Data(), []byte(data)) 1327 } 1328 if f.Header().Flags&1 == 0 { 1329 t.Errorf("didn't see END_STREAM flag") 1330 } 1331 return df 1332 } 1333 1334 func encodeHeaderRaw(t *testing.T, pairs ...string) []byte { 1335 var he hpackEncoder 1336 return he.encodeHeaderRaw(t, pairs...) 1337 } 1338 1339 func TestSettingsDuplicates(t *testing.T) { 1340 tests := []struct { 1341 settings []Setting 1342 want bool 1343 }{ 1344 {nil, false}, 1345 {[]Setting{{ID: 1}}, false}, 1346 {[]Setting{{ID: 1}, {ID: 2}}, false}, 1347 {[]Setting{{ID: 1}, {ID: 2}}, false}, 1348 {[]Setting{{ID: 1}, {ID: 2}, {ID: 3}}, false}, 1349 {[]Setting{{ID: 1}, {ID: 2}, {ID: 3}}, false}, 1350 {[]Setting{{ID: 1}, {ID: 2}, {ID: 3}, {ID: 4}}, false}, 1351 1352 {[]Setting{{ID: 1}, {ID: 2}, {ID: 3}, {ID: 2}}, true}, 1353 {[]Setting{{ID: 4}, {ID: 2}, {ID: 3}, {ID: 4}}, true}, 1354 1355 {[]Setting{ 1356 {ID: 1}, {ID: 2}, {ID: 3}, {ID: 4}, 1357 {ID: 5}, {ID: 6}, {ID: 7}, {ID: 8}, 1358 {ID: 9}, {ID: 10}, {ID: 11}, {ID: 12}, 1359 }, false}, 1360 1361 {[]Setting{ 1362 {ID: 1}, {ID: 2}, {ID: 3}, {ID: 4}, 1363 {ID: 5}, {ID: 6}, {ID: 7}, {ID: 8}, 1364 {ID: 9}, {ID: 10}, {ID: 11}, {ID: 11}, 1365 }, true}, 1366 } 1367 for i, tt := range tests { 1368 fr, _ := testFramer() 1369 fr.WriteSettings(tt.settings...) 1370 f, err := fr.ReadFrame() 1371 if err != nil { 1372 t.Fatalf("%d. ReadFrame: %v", i, err) 1373 } 1374 sf := f.(*SettingsFrame) 1375 got := sf.HasDuplicates() 1376 if got != tt.want { 1377 t.Errorf("%d. HasDuplicates = %v; want %v", i, got, tt.want) 1378 } 1379 } 1380 1381 }