google.golang.org/grpc@v1.72.2/internal/transport/grpchttp2/http2bridge_test.go (about) 1 /* 2 * 3 * Copyright 2024 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package grpchttp2 20 21 import ( 22 "fmt" 23 "testing" 24 25 "github.com/google/go-cmp/cmp" 26 "golang.org/x/net/http2/hpack" 27 ) 28 29 // testConn is a test utility which provides an io.Writer and io.Reader 30 // interface and access to its internal buffers for testing. 31 type testConn struct { 32 wbuf []byte 33 rbuf []byte 34 } 35 36 func (c *testConn) Write(p []byte) (int, error) { 37 c.wbuf = append(c.wbuf, p...) 38 return len(p), nil 39 } 40 41 func (c *testConn) Read(p []byte) (int, error) { 42 n := copy(p, c.rbuf) 43 c.rbuf = c.rbuf[n:] 44 return n, nil 45 } 46 47 func appendUint32(b []byte, x uint32) []byte { 48 return append(b, byte(x>>24), byte(x>>16), byte(x>>8), byte(x)) 49 } 50 51 func readUint32(b []byte) uint32 { 52 return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) 53 } 54 55 func readUint24(b []byte) int { 56 return int(b[0])<<16 | int(b[1])<<8 | int(b[2]) 57 } 58 59 func readUint16(b []byte) uint16 { 60 return uint16(b[0])<<8 | uint16(b[1]) 61 } 62 63 // parseWrittenHeader takes a byte buffer representing a written frame header 64 // and returns its parsed values. 65 func parseWrittenHeader(buf []byte) *FrameHeader { 66 size := uint32(readUint24(buf[0:3])) 67 t := FrameType(buf[3]) 68 flags := Flag(buf[4]) 69 sID := readUint32(buf[5:]) 70 return &FrameHeader{Size: size, Type: t, Flags: flags, StreamID: sID} 71 } 72 73 // Tests and verifies that the framer correctly reads a Data Frame. 74 func (s) TestBridge_ReadFrame_Data(t *testing.T) { 75 c := &testConn{} 76 recvData := "test data" 77 // Writing a Data Frame to the reading buf with recvData as payload. 78 c.rbuf = append(c.rbuf, 0, 0, byte(len(recvData)), byte(FrameTypeData), byte(FlagDataEndStream)) 79 c.rbuf = appendUint32(c.rbuf, 1) 80 c.rbuf = append(c.rbuf, []byte(recvData)...) 81 82 f := NewFramerBridge(c, c, 0, nil) 83 fr, err := f.ReadFrame() 84 if err != nil { 85 t.Fatalf("ReadFrame(): %v", err) 86 } 87 88 wantHdr := &FrameHeader{ 89 Size: uint32(len(recvData)), 90 Type: FrameTypeData, 91 Flags: FlagDataEndStream, 92 StreamID: 1, 93 } 94 95 if diff := cmp.Diff(fr.Header(), wantHdr); diff != "" { 96 t.Errorf("ReadFrame() (-got, +want): %s", diff) 97 } 98 99 df := fr.(*DataFrame) 100 if string(df.Data) != recvData { 101 t.Errorf("ReadFrame(): Data: got %q, want %q", string(df.Data), recvData) 102 } 103 df.Free() 104 } 105 106 // Tests and verifies that the framer correctly reads a RSTStream Frame. 107 func (s) TestBridge_ReadFrame_RSTStream(t *testing.T) { 108 c := &testConn{} 109 // Writing a RSTStream Frame to the reading buf with ErrCodeProtocol as 110 // payload. 111 c.rbuf = append(c.rbuf, 0, 0, 4, byte(FrameTypeRSTStream), 0) 112 c.rbuf = appendUint32(c.rbuf, 1) 113 c.rbuf = appendUint32(c.rbuf, uint32(ErrCodeProtocol)) 114 115 f := NewFramerBridge(c, c, 0, nil) 116 fr, err := f.ReadFrame() 117 if err != nil { 118 t.Fatalf("ReadFrame(): %v", err) 119 } 120 121 wantHdr := &FrameHeader{ 122 Size: 4, 123 Type: FrameTypeRSTStream, 124 Flags: 0, 125 StreamID: 1, 126 } 127 if diff := cmp.Diff(fr.Header(), wantHdr); diff != "" { 128 t.Errorf("ReadFrame() (-got, +want): %s", diff) 129 } 130 rf := fr.(*RSTStreamFrame) 131 if rf.Code != ErrCodeProtocol { 132 t.Errorf("ReadFrame(): Code: got %#x, want %#x", rf.Code, ErrCodeProtocol) 133 } 134 } 135 136 // Tests and verifies that the framer correctly reads a Settings Frame. 137 func (s) TestBridge_ReadFrame_Settings(t *testing.T) { 138 c := &testConn{} 139 s := Setting{ID: SettingsHeaderTableSize, Value: 200} 140 // Writing a Settings Frame to the reading buf with s as payload. 141 c.rbuf = append(c.rbuf, 0, 0, 6, byte(FrameTypeSettings), 0) 142 c.rbuf = appendUint32(c.rbuf, 0) 143 c.rbuf = append(c.rbuf, byte(s.ID>>8), byte(s.ID)) 144 c.rbuf = appendUint32(c.rbuf, s.Value) 145 146 f := NewFramerBridge(c, c, 0, nil) 147 fr, err := f.ReadFrame() 148 if err != nil { 149 t.Fatalf("ReadFrame(): %v", err) 150 } 151 152 wantHdr := &FrameHeader{ 153 Size: 6, 154 Type: FrameTypeSettings, 155 Flags: 0, 156 StreamID: 0, 157 } 158 if diff := cmp.Diff(fr.Header(), wantHdr); diff != "" { 159 t.Errorf("ReadFrame() (-got, +want): %s", diff) 160 } 161 162 sf := fr.(*SettingsFrame) 163 if len(sf.Settings) != 1 { 164 t.Fatalf("ReadFrame(): Settings: got %d, want %d", len(sf.Settings), 1) 165 } 166 if sf.Settings[0] != s { 167 t.Errorf("ReadFrame(): Settings: got %v, want %v", sf.Settings[0], s) 168 } 169 } 170 171 // Tests and verifies that the framer correctly reads a Ping Frame. 172 func (s) TestBridge_ReadFrame_Ping(t *testing.T) { 173 c := &testConn{} 174 d := []byte{1, 2, 3, 4, 5, 6, 7, 8} 175 // Writing a Ping Frame to the reading buf with d as payload. 176 c.rbuf = append(c.rbuf, 0, 0, 8, byte(FrameTypePing), 0) 177 c.rbuf = appendUint32(c.rbuf, 0) 178 c.rbuf = append(c.rbuf, d...) 179 180 f := NewFramerBridge(c, c, 0, nil) 181 fr, err := f.ReadFrame() 182 if err != nil { 183 t.Fatalf("ReadFrame(): %v", err) 184 } 185 186 wantHdr := &FrameHeader{ 187 Size: 8, 188 Type: FrameTypePing, 189 Flags: 0, 190 StreamID: 0, 191 } 192 if diff := cmp.Diff(fr.Header(), wantHdr); diff != "" { 193 t.Errorf("ReadFrame() (-got, +want): %s", diff) 194 } 195 196 pf := fr.(*PingFrame) 197 for i := range pf.Data { 198 if pf.Data[i] != d[i] { 199 t.Errorf("ReadFrame(): Data[%d]: got %d, want %d", i, pf.Data[i], d[i]) 200 } 201 } 202 pf.Free() 203 } 204 205 // Tests and verifies that the framer correctly reads a GoAway Frame. 206 func (s) TestBridge_ReadFrame_GoAway(t *testing.T) { 207 c := &testConn{} 208 d := "debug_data" 209 // The length of data + 4 byte code + 4 byte streamID 210 ln := len(d) + 8 211 // Writing a GoAway Frame to the reading buf with d, ErrCodeFlowControl and 212 // streamID 2 as payload. 213 c.rbuf = append(c.rbuf, 0, 0, byte(ln), byte(FrameTypeGoAway), 0) 214 c.rbuf = appendUint32(c.rbuf, 0) 215 c.rbuf = appendUint32(c.rbuf, 2) 216 c.rbuf = appendUint32(c.rbuf, uint32(ErrCodeFlowControl)) 217 c.rbuf = append(c.rbuf, []byte(d)...) 218 f := NewFramerBridge(c, c, 0, nil) 219 220 fr, err := f.ReadFrame() 221 if err != nil { 222 t.Fatalf("ReadFrame(): %v", err) 223 } 224 225 wantHdr := &FrameHeader{ 226 Size: uint32(ln), 227 Type: FrameTypeGoAway, 228 Flags: 0, 229 StreamID: 0, 230 } 231 if diff := cmp.Diff(fr.Header(), wantHdr); diff != "" { 232 t.Errorf("ReadFrame() (-got, +want): %s", diff) 233 } 234 235 gf := fr.(*GoAwayFrame) 236 if gf.LastStreamID != 2 { 237 t.Errorf("ReadFrame(): LastStreamID: got %d, want %d", gf.LastStreamID, 2) 238 } 239 if gf.Code != ErrCodeFlowControl { 240 t.Errorf("ReadFrame(): Code: got %#x, want %#x", gf.Code, ErrCodeFlowControl) 241 } 242 if string(gf.DebugData) != d { 243 t.Errorf("ReadFrame(): DebugData: got %q, want %q", string(gf.DebugData), d) 244 } 245 gf.Free() 246 } 247 248 // Tests and verifies that the framer correctly reads a WindowUpdate Frame. 249 func (s) TestBridge_ReadFrame_WindowUpdate(t *testing.T) { 250 c := &testConn{} 251 // Writing a WindowUpdate Frame to the reading buf with 100 as payload. 252 c.rbuf = append(c.rbuf, 0, 0, 4, byte(FrameTypeWindowUpdate), 0) 253 c.rbuf = appendUint32(c.rbuf, 1) 254 c.rbuf = appendUint32(c.rbuf, 100) 255 256 f := NewFramerBridge(c, c, 0, nil) 257 fr, err := f.ReadFrame() 258 if err != nil { 259 t.Fatalf("ReadFrame(): %v", err) 260 } 261 262 wantHdr := &FrameHeader{ 263 Size: 4, 264 Type: FrameTypeWindowUpdate, 265 Flags: 0, 266 StreamID: 1, 267 } 268 if diff := cmp.Diff(fr.Header(), wantHdr); diff != "" { 269 t.Errorf("ReadFrame() (-got, +want): %s", diff) 270 } 271 272 wf := fr.(*WindowUpdateFrame) 273 if wf.Inc != 100 { 274 t.Errorf("ReadFrame(): Inc: got %d, want %d", wf.Inc, 1) 275 } 276 } 277 278 // Tests and verifies that the framer correctly merges Headers and Continuation 279 // Frames into a single MetaHeaders Frame. 280 func (s) TestBridge_ReadFrame_MetaHeaders(t *testing.T) { 281 fields := []hpack.HeaderField{ 282 {Name: "foo", Value: "bar"}, 283 {Name: "baz", Value: "qux"}, 284 } 285 286 c := &testConn{} 287 enc := hpack.NewEncoder(c) 288 for _, field := range fields { 289 enc.WriteField(field) 290 } 291 half1 := c.wbuf[0 : len(c.wbuf)/2] 292 half2 := c.wbuf[len(c.wbuf)/2:] 293 294 // Writing a Headers Frame with half of the encoded headers 295 c.rbuf = append(c.rbuf, 0, 0, byte(len(half1)), byte(FrameTypeHeaders), 0) 296 c.rbuf = appendUint32(c.rbuf, 1) 297 c.rbuf = append(c.rbuf, half1...) 298 299 // Writing a Continuation Frame with the other half of the encoded headers 300 // to test merging. 301 c.rbuf = append(c.rbuf, 0, 0, byte(len(half2)), byte(FrameTypeContinuation), byte(FlagContinuationEndHeaders)) 302 c.rbuf = appendUint32(c.rbuf, 1) 303 // Copy data written by the encoder into the reading buf 304 c.rbuf = append(c.rbuf, half2...) 305 306 f := NewFramerBridge(c, c, 0, nil) 307 fr, err := f.ReadFrame() 308 if err != nil { 309 t.Fatalf("ReadFrame(): %v", err) 310 } 311 312 mf, ok := fr.(*MetaHeadersFrame) 313 if !ok { 314 t.Errorf("ReadFrame(): Type: expected MetaHeadersFrame, got %T", fr) 315 } 316 if len(mf.Fields) != 2 { 317 t.Errorf("ReadFrame(): Fields: got %d, want %d", len(mf.Fields), 1) 318 } 319 for i, field := range fields { 320 if field.Name != mf.Fields[i].Name { 321 t.Errorf("ReadFrame(): Fields[%d].Name: got %q, want %q", i, mf.Fields[i].Name, field.Name) 322 } 323 if field.Value != mf.Fields[i].Value { 324 t.Errorf("ReadFrame(): Fields[%d].Value: got %q, want %q", i, mf.Fields[i].Value, field.Value) 325 } 326 } 327 } 328 329 // Tests and verifies that the framer correctly reads an unknown frame Frame. 330 func (s) TestBridge_ReadFrame_UnknownFrame(t *testing.T) { 331 c := &testConn{} 332 wantData := "test data" 333 334 // Writing an unknown frame to the reading buf 335 c.rbuf = append(c.rbuf, 0, 0, byte(len(wantData)), 0xa, 0) 336 c.rbuf = appendUint32(c.rbuf, 1) 337 c.rbuf = append(c.rbuf, []byte(wantData)...) 338 339 f := NewFramerBridge(c, c, 0, nil) 340 fr, err := f.ReadFrame() 341 if err != nil { 342 t.Errorf("ReadFrame(): %s", err) 343 } 344 345 wantHdr := &FrameHeader{ 346 Size: uint32(len(wantData)), 347 Type: 0xa, 348 Flags: 0, 349 StreamID: 1, 350 } 351 if diff := cmp.Diff(fr.Header(), wantHdr); diff != "" { 352 t.Errorf("ReadFrame() (-got, +want): %s", diff) 353 } 354 355 uf, ok := fr.(*UnknownFrame) 356 if !ok { 357 t.Errorf("ReadFrame(): Type: expected UnknownFrame, got %T", f) 358 } 359 if string(uf.Payload) != wantData { 360 t.Errorf("ReadFrame(): Payload: got %q, want %q", uf.Payload, wantData) 361 } 362 uf.Free() 363 } 364 365 // Tests and verifies that a Data Frame is correctly written. 366 func (s) TestBridge_WriteData_MultipleSlices(t *testing.T) { 367 c := &testConn{} 368 testBuf := [][]byte{[]byte("test"), []byte(" data")} 369 f := NewFramerBridge(c, c, 0, nil) 370 f.WriteData(1, false, testBuf...) 371 372 wantData := "test data" 373 wantHdr := &FrameHeader{ 374 Size: uint32(len(wantData)), 375 Type: FrameTypeData, 376 Flags: 0, 377 StreamID: 1, 378 } 379 gotHdr := parseWrittenHeader(c.wbuf[:9]) 380 if diff := cmp.Diff(gotHdr, wantHdr); diff != "" { 381 t.Errorf("WriteData() (-got, +want): %s", diff) 382 } 383 384 if string(c.wbuf[9:]) != wantData { 385 t.Errorf("WriteData(): Data: got %q, want %q", string(c.wbuf[9:]), wantData) 386 } 387 } 388 389 func (s) TestBridge_WriteData_OneSlice(t *testing.T) { 390 c := &testConn{} 391 testBuf := [][]byte{[]byte("test data")} 392 f := NewFramerBridge(c, c, 0, nil) 393 f.WriteData(1, false, testBuf...) 394 395 wantData := "test data" 396 wantHdr := &FrameHeader{ 397 Size: uint32(len(wantData)), 398 Type: FrameTypeData, 399 Flags: 0, 400 StreamID: 1, 401 } 402 gotHdr := parseWrittenHeader(c.wbuf[:9]) 403 if diff := cmp.Diff(gotHdr, wantHdr); diff != "" { 404 t.Errorf("WriteData() (-got, +want): %s", diff) 405 } 406 407 if string(c.wbuf[9:]) != wantData { 408 t.Errorf("WriteData(): Data: got %q, want %q", string(c.wbuf[9:]), wantData) 409 } 410 } 411 412 // Tests and verifies that a Headers Frame and all its flag permutations are 413 // correctly written. 414 func (s) TestBridge_WriteHeaders(t *testing.T) { 415 tests := []struct { 416 name string 417 endStream bool 418 endHeaders bool 419 }{ 420 {name: "no flags", endStream: false, endHeaders: false}, 421 {name: "endheaders", endStream: false, endHeaders: true}, 422 {name: "endstream and endheaders", endStream: true, endHeaders: true}, 423 {name: "endstream", endStream: true, endHeaders: false}, 424 } 425 wantData := "test data" 426 427 for _, test := range tests { 428 t.Run(fmt.Sprintf(test.name, test.endStream, test.endHeaders), func(t *testing.T) { 429 c := &testConn{} 430 f := NewFramerBridge(c, c, 0, nil) 431 432 f.WriteHeaders(1, test.endStream, test.endHeaders, []byte(wantData)) 433 434 var wantFlags Flag 435 if test.endStream { 436 wantFlags |= FlagHeadersEndStream 437 } 438 if test.endHeaders { 439 wantFlags |= FlagHeadersEndHeaders 440 } 441 wantHdr := &FrameHeader{ 442 Size: uint32(len(wantData)), 443 Type: FrameTypeHeaders, 444 Flags: wantFlags, 445 StreamID: 1, 446 } 447 gotHdr := parseWrittenHeader(c.wbuf[:9]) 448 if diff := cmp.Diff(gotHdr, wantHdr); diff != "" { 449 t.Errorf("WriteHeaders() (-got, +want): %s", diff) 450 } 451 452 if data := string(c.wbuf[9:]); data != wantData { 453 t.Errorf("WriteHeaders(): Data: got %q, want %q", data, wantData) 454 } 455 }) 456 457 } 458 } 459 460 // Tests and verifies that a RSTStream Frame is correctly written. 461 func (s) TestBridge_WriteRSTStream(t *testing.T) { 462 c := &testConn{} 463 f := NewFramerBridge(c, c, 0, nil) 464 f.WriteRSTStream(1, ErrCodeProtocol) 465 466 wantHdr := &FrameHeader{ 467 Size: 4, 468 Type: FrameTypeRSTStream, 469 Flags: 0, 470 StreamID: 1, 471 } 472 gotHdr := parseWrittenHeader(c.wbuf[:9]) 473 if diff := cmp.Diff(gotHdr, wantHdr); diff != "" { 474 t.Errorf("WriteRSTStream() (-got, +want): %s", diff) 475 } 476 477 if errCode := readUint32(c.wbuf[9:13]); errCode != uint32(ErrCodeProtocol) { 478 t.Errorf("WriteRSTStream(): SettingID: got %d, want %d", errCode, ErrCodeProtocol) 479 } 480 } 481 482 // Tests and verifies that a Settings Frame is correctly written. 483 func (s) TestBridge_WriteSettings(t *testing.T) { 484 c := &testConn{} 485 f := NewFramerBridge(c, c, 0, nil) 486 f.WriteSettings(Setting{ID: SettingsHeaderTableSize, Value: 200}) 487 488 wantHdr := &FrameHeader{ 489 Size: 6, 490 Type: FrameTypeSettings, 491 Flags: 0, 492 StreamID: 0, 493 } 494 gotHdr := parseWrittenHeader(c.wbuf[:9]) 495 if diff := cmp.Diff(gotHdr, wantHdr); diff != "" { 496 t.Errorf("WriteSettings() (-got, +want): %s", diff) 497 } 498 499 if settingID := readUint16(c.wbuf[9:11]); settingID != uint16(SettingsHeaderTableSize) { 500 t.Errorf("WriteSettings(): SettingID: got %d, want %d", settingID, SettingsHeaderTableSize) 501 } 502 if settingVal := readUint32(c.wbuf[11:15]); settingVal != 200 { 503 t.Errorf("WriteSettings(): SettingVal: got %d, want %d", settingVal, 200) 504 } 505 } 506 507 // Tests and verifies that a Settings Frame with the ack flag is correctly 508 // written. 509 func (s) TestBridge_WriteSettingsAck(t *testing.T) { 510 c := &testConn{} 511 f := NewFramerBridge(c, c, 0, nil) 512 f.WriteSettingsAck() 513 514 wantHdr := &FrameHeader{ 515 Size: 0, 516 Type: FrameTypeSettings, 517 Flags: FlagSettingsAck, 518 StreamID: 0, 519 } 520 gotHdr := parseWrittenHeader(c.wbuf[:9]) 521 if diff := cmp.Diff(gotHdr, wantHdr); diff != "" { 522 t.Errorf("WriteSettingsAck() (-got, +want): %s", diff) 523 } 524 525 } 526 527 // Tests and verifies that a Ping Frame is correctly written with its flag 528 // permutations. 529 func (s) TestBridge_WritePing(t *testing.T) { 530 wantData := [8]byte{1, 2, 3, 4, 5, 6, 7, 8} 531 acks := []bool{true, false} 532 533 for _, ack := range acks { 534 t.Run(fmt.Sprintf("ack=%v", ack), func(t *testing.T) { 535 c := &testConn{} 536 f := NewFramerBridge(c, c, 0, nil) 537 538 f.WritePing(ack, wantData) 539 var wantFlags Flag 540 if ack { 541 wantFlags |= FlagPingAck 542 } 543 wantHdr := &FrameHeader{ 544 Size: 8, 545 Type: FrameTypePing, 546 Flags: wantFlags, 547 StreamID: 0, 548 } 549 gotHdr := parseWrittenHeader(c.wbuf[:9]) 550 if diff := cmp.Diff(gotHdr, wantHdr); diff != "" { 551 t.Errorf("WritePing() (-got, +want): %s", diff) 552 } 553 554 data := c.wbuf[9:] 555 for i := range data { 556 if data[i] != wantData[i] { 557 t.Errorf("WritePing(): Data[%d]: got %d, want %d", i, data[i], wantData[i]) 558 } 559 } 560 c.wbuf = c.wbuf[:0] 561 }) 562 } 563 } 564 565 // Tests and verifies that a GoAway Frame is correctly written. 566 func (s) TestBridge_WriteGoAway(t *testing.T) { 567 c := &testConn{} 568 f := NewFramerBridge(c, c, 0, nil) 569 f.WriteGoAway(2, ErrCodeFlowControl, []byte("debug_data")) 570 571 wantHdr := &FrameHeader{ 572 Size: 18, 573 Type: FrameTypeGoAway, 574 Flags: 0, 575 StreamID: 0, 576 } 577 gotHdr := parseWrittenHeader(c.wbuf[:9]) 578 if diff := cmp.Diff(gotHdr, wantHdr); diff != "" { 579 t.Errorf("WriteGoAway() (-got, +want): %s", diff) 580 } 581 582 if lastStream := readUint32(c.wbuf[9:13]); lastStream != 2 { 583 t.Errorf("WriteGoAway(): LastStreamID: got %d, want %d", lastStream, 2) 584 } 585 if code := ErrCode(readUint32(c.wbuf[13:17])); code != ErrCodeFlowControl { 586 t.Errorf("WriteGoAway(): Code: got %d, want %d", code, ErrCodeFlowControl) 587 } 588 if data := string(c.wbuf[17:]); data != "debug_data" { 589 t.Errorf("WriteGoAway(): Data: got %q, want %q", data, "debug_data") 590 } 591 } 592 593 // Tests and verifies that a WindowUpdate Frame is correctly written. 594 func (s) TestBridge_WriteWindowUpdate(t *testing.T) { 595 c := &testConn{} 596 f := NewFramerBridge(c, c, 0, nil) 597 f.WriteWindowUpdate(1, 2) 598 599 wantHdr := &FrameHeader{ 600 Size: 4, 601 Type: FrameTypeWindowUpdate, 602 Flags: 0, 603 StreamID: 1, 604 } 605 gotHdr := parseWrittenHeader(c.wbuf[:9]) 606 if diff := cmp.Diff(gotHdr, wantHdr); diff != "" { 607 t.Errorf("WriteWindowUpdate() (-got, +want): %s", diff) 608 } 609 610 if inc := readUint32(c.wbuf[9:13]); inc != 2 { 611 t.Errorf("WriteWindowUpdate(): Inc: got %d, want %d", inc, 2) 612 } 613 } 614 615 // Tests and verifies that a Continuation Frame is correctly written with its 616 // flag permutations. 617 func (s) TestBridge_WriteContinuation(t *testing.T) { 618 wantData := "hdr block" 619 endHeaders := []struct { 620 name string 621 endHeaders bool 622 }{ 623 {name: "no flags", endHeaders: false}, 624 {name: "endheaders", endHeaders: true}, 625 } 626 627 for _, test := range endHeaders { 628 t.Run(test.name, func(t *testing.T) { 629 c := &testConn{} 630 f := NewFramerBridge(c, c, 0, nil) 631 f.WriteContinuation(1, test.endHeaders, []byte("hdr block")) 632 var wantFlags Flag 633 if test.endHeaders { 634 wantFlags |= FlagContinuationEndHeaders 635 } 636 wantHdr := &FrameHeader{ 637 Size: uint32(len(wantData)), 638 Type: FrameTypeContinuation, 639 Flags: wantFlags, 640 StreamID: 1, 641 } 642 gotHdr := parseWrittenHeader(c.wbuf[:9]) 643 if diff := cmp.Diff(gotHdr, wantHdr); diff != "" { 644 t.Errorf("WriteContinuation() (-got, +want): %s", diff) 645 } 646 647 if data := string(c.wbuf[9:]); data != wantData { 648 t.Errorf("WriteContinuation(): Data: got %q, want %q", data, wantData) 649 } 650 c.wbuf = c.wbuf[:0] 651 }) 652 653 } 654 }