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  }