...

Source file src/golang.org/x/net/internal/quic/conn_loss_test.go

Documentation: golang.org/x/net/internal/quic

     1  // Copyright 2023 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  //go:build go1.21
     6  
     7  package quic
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"fmt"
    13  	"testing"
    14  )
    15  
    16  // Frames may be retransmitted either when the packet containing the frame is lost, or on PTO.
    17  // lostFrameTest runs a test in both configurations.
    18  func lostFrameTest(t *testing.T, f func(t *testing.T, pto bool)) {
    19  	t.Run("lost", func(t *testing.T) {
    20  		f(t, false)
    21  	})
    22  	t.Run("pto", func(t *testing.T) {
    23  		f(t, true)
    24  	})
    25  }
    26  
    27  // triggerLossOrPTO causes the conn to declare the last sent packet lost,
    28  // or advances to the PTO timer.
    29  func (tc *testConn) triggerLossOrPTO(ptype packetType, pto bool) {
    30  	tc.t.Helper()
    31  	if pto {
    32  		if !tc.conn.loss.ptoTimerArmed {
    33  			tc.t.Fatalf("PTO timer not armed, expected it to be")
    34  		}
    35  		if *testVV {
    36  			tc.t.Logf("advancing to PTO timer")
    37  		}
    38  		tc.advanceTo(tc.conn.loss.timer)
    39  		return
    40  	}
    41  	if *testVV {
    42  		*testVV = false
    43  		defer func() {
    44  			tc.t.Logf("cause conn to declare last packet lost")
    45  			*testVV = true
    46  		}()
    47  	}
    48  	defer func(ignoreFrames map[byte]bool) {
    49  		tc.ignoreFrames = ignoreFrames
    50  	}(tc.ignoreFrames)
    51  	tc.ignoreFrames = map[byte]bool{
    52  		frameTypeAck:     true,
    53  		frameTypePadding: true,
    54  	}
    55  	// Send three packets containing PINGs, and then respond with an ACK for the
    56  	// last one. This puts the last packet before the PINGs outside the packet
    57  	// reordering threshold, and it will be declared lost.
    58  	const lossThreshold = 3
    59  	var num packetNumber
    60  	for i := 0; i < lossThreshold; i++ {
    61  		tc.conn.ping(spaceForPacketType(ptype))
    62  		d := tc.readDatagram()
    63  		if d == nil {
    64  			tc.t.Fatalf("conn is idle; want PING frame")
    65  		}
    66  		if d.packets[0].ptype != ptype {
    67  			tc.t.Fatalf("conn sent %v packet; want %v", d.packets[0].ptype, ptype)
    68  		}
    69  		num = d.packets[0].num
    70  	}
    71  	tc.writeFrames(ptype, debugFrameAck{
    72  		ranges: []i64range[packetNumber]{
    73  			{num, num + 1},
    74  		},
    75  	})
    76  }
    77  
    78  func TestLostResetStreamFrame(t *testing.T) {
    79  	// "Cancellation of stream transmission, as carried in a RESET_STREAM frame,
    80  	// is sent until acknowledged or until all stream data is acknowledged by the peer [...]"
    81  	// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.3-3.4
    82  	lostFrameTest(t, func(t *testing.T, pto bool) {
    83  		tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, permissiveTransportParameters)
    84  		tc.ignoreFrame(frameTypeAck)
    85  
    86  		s.Reset(1)
    87  		tc.wantFrame("reset stream",
    88  			packetType1RTT, debugFrameResetStream{
    89  				id:   s.id,
    90  				code: 1,
    91  			})
    92  
    93  		tc.triggerLossOrPTO(packetType1RTT, pto)
    94  		tc.wantFrame("resent RESET_STREAM frame",
    95  			packetType1RTT, debugFrameResetStream{
    96  				id:   s.id,
    97  				code: 1,
    98  			})
    99  	})
   100  }
   101  
   102  func TestLostStopSendingFrame(t *testing.T) {
   103  	// "[...] a request to cancel stream transmission, as encoded in a STOP_SENDING frame,
   104  	// is sent until the receiving part of the stream enters either a "Data Recvd" or
   105  	// "Reset Recvd" state [...]"
   106  	// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.3-3.5
   107  	//
   108  	// Technically, we can stop sending a STOP_SENDING frame if the peer sends
   109  	// us all the data for the stream or resets it. We don't bother tracking this,
   110  	// however, so we'll keep sending the frame until it is acked. This is harmless.
   111  	lostFrameTest(t, func(t *testing.T, pto bool) {
   112  		tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, permissiveTransportParameters)
   113  		tc.ignoreFrame(frameTypeAck)
   114  
   115  		s.CloseRead()
   116  		tc.wantFrame("stream is read-closed",
   117  			packetType1RTT, debugFrameStopSending{
   118  				id: s.id,
   119  			})
   120  
   121  		tc.triggerLossOrPTO(packetType1RTT, pto)
   122  		tc.wantFrame("resent STOP_SENDING frame",
   123  			packetType1RTT, debugFrameStopSending{
   124  				id: s.id,
   125  			})
   126  	})
   127  }
   128  
   129  func TestLostCryptoFrame(t *testing.T) {
   130  	// "Data sent in CRYPTO frames is retransmitted [...] until all data has been acknowledged."
   131  	// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.3-3.1
   132  	lostFrameTest(t, func(t *testing.T, pto bool) {
   133  		tc := newTestConn(t, clientSide)
   134  		tc.ignoreFrame(frameTypeAck)
   135  
   136  		tc.wantFrame("client sends Initial CRYPTO frame",
   137  			packetTypeInitial, debugFrameCrypto{
   138  				data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
   139  			})
   140  		tc.triggerLossOrPTO(packetTypeInitial, pto)
   141  		tc.wantFrame("client resends Initial CRYPTO frame",
   142  			packetTypeInitial, debugFrameCrypto{
   143  				data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
   144  			})
   145  
   146  		tc.writeFrames(packetTypeInitial,
   147  			debugFrameCrypto{
   148  				data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
   149  			})
   150  		tc.writeFrames(packetTypeHandshake,
   151  			debugFrameCrypto{
   152  				data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
   153  			})
   154  
   155  		tc.wantFrame("client sends Handshake CRYPTO frame",
   156  			packetTypeHandshake, debugFrameCrypto{
   157  				data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake],
   158  			})
   159  		tc.wantFrame("client provides server with an additional connection ID",
   160  			packetType1RTT, debugFrameNewConnectionID{
   161  				seq:    1,
   162  				connID: testLocalConnID(1),
   163  				token:  testLocalStatelessResetToken(1),
   164  			})
   165  		tc.triggerLossOrPTO(packetTypeHandshake, pto)
   166  		tc.wantFrame("client resends Handshake CRYPTO frame",
   167  			packetTypeHandshake, debugFrameCrypto{
   168  				data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake],
   169  			})
   170  	})
   171  }
   172  
   173  func TestLostStreamFrameEmpty(t *testing.T) {
   174  	// A STREAM frame opening a stream, but containing no stream data, should
   175  	// be retransmitted if lost.
   176  	lostFrameTest(t, func(t *testing.T, pto bool) {
   177  		ctx := canceledContext()
   178  		tc := newTestConn(t, clientSide, permissiveTransportParameters)
   179  		tc.handshake()
   180  		tc.ignoreFrame(frameTypeAck)
   181  
   182  		c, err := tc.conn.NewStream(ctx)
   183  		if err != nil {
   184  			t.Fatalf("NewStream: %v", err)
   185  		}
   186  		c.Flush() // open the stream
   187  		tc.wantFrame("created bidirectional stream 0",
   188  			packetType1RTT, debugFrameStream{
   189  				id:   newStreamID(clientSide, bidiStream, 0),
   190  				data: []byte{},
   191  			})
   192  
   193  		tc.triggerLossOrPTO(packetType1RTT, pto)
   194  		tc.wantFrame("resent stream frame",
   195  			packetType1RTT, debugFrameStream{
   196  				id:   newStreamID(clientSide, bidiStream, 0),
   197  				data: []byte{},
   198  			})
   199  	})
   200  }
   201  
   202  func TestLostStreamWithData(t *testing.T) {
   203  	// "Application data sent in STREAM frames is retransmitted in new STREAM
   204  	// frames unless the endpoint has sent a RESET_STREAM for that stream."
   205  	// https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.2
   206  	//
   207  	// TODO: Lost stream frame after RESET_STREAM
   208  	lostFrameTest(t, func(t *testing.T, pto bool) {
   209  		data := []byte{0, 1, 2, 3, 4, 5, 6, 7}
   210  		tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, func(p *transportParameters) {
   211  			p.initialMaxStreamsUni = 1
   212  			p.initialMaxData = 1 << 20
   213  			p.initialMaxStreamDataUni = 1 << 20
   214  		})
   215  		s.Write(data[:4])
   216  		s.Flush()
   217  		tc.wantFrame("send [0,4)",
   218  			packetType1RTT, debugFrameStream{
   219  				id:   s.id,
   220  				off:  0,
   221  				data: data[:4],
   222  			})
   223  		s.Write(data[4:8])
   224  		s.Flush()
   225  		tc.wantFrame("send [4,8)",
   226  			packetType1RTT, debugFrameStream{
   227  				id:   s.id,
   228  				off:  4,
   229  				data: data[4:8],
   230  			})
   231  		s.CloseWrite()
   232  		tc.wantFrame("send FIN",
   233  			packetType1RTT, debugFrameStream{
   234  				id:   s.id,
   235  				off:  8,
   236  				fin:  true,
   237  				data: []byte{},
   238  			})
   239  
   240  		tc.triggerLossOrPTO(packetType1RTT, pto)
   241  		tc.wantFrame("resend data",
   242  			packetType1RTT, debugFrameStream{
   243  				id:   s.id,
   244  				off:  0,
   245  				fin:  true,
   246  				data: data[:8],
   247  			})
   248  	})
   249  }
   250  
   251  func TestLostStreamPartialLoss(t *testing.T) {
   252  	// Conn sends four STREAM packets.
   253  	// ACKs are received for the packets containing bytes 0 and 2.
   254  	// The remaining packets are declared lost.
   255  	// The Conn resends only the lost data.
   256  	//
   257  	// This test doesn't have a PTO mode, because the ACK for the packet containing byte 2
   258  	// starts the loss timer for the packet containing byte 1, and the PTO timer is not
   259  	// armed when the loss timer is.
   260  	data := []byte{0, 1, 2, 3}
   261  	tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, func(p *transportParameters) {
   262  		p.initialMaxStreamsUni = 1
   263  		p.initialMaxData = 1 << 20
   264  		p.initialMaxStreamDataUni = 1 << 20
   265  	})
   266  	for i := range data {
   267  		s.Write(data[i : i+1])
   268  		s.Flush()
   269  		tc.wantFrame(fmt.Sprintf("send STREAM frame with byte %v", i),
   270  			packetType1RTT, debugFrameStream{
   271  				id:   s.id,
   272  				off:  int64(i),
   273  				data: data[i : i+1],
   274  			})
   275  		if i%2 == 0 {
   276  			tc.writeAckForLatest()
   277  		}
   278  	}
   279  	const pto = false
   280  	tc.triggerLossOrPTO(packetType1RTT, pto)
   281  	tc.wantFrame("resend byte 1",
   282  		packetType1RTT, debugFrameStream{
   283  			id:   s.id,
   284  			off:  1,
   285  			data: data[1:2],
   286  		})
   287  	tc.wantFrame("resend byte 3",
   288  		packetType1RTT, debugFrameStream{
   289  			id:   s.id,
   290  			off:  3,
   291  			data: data[3:4],
   292  		})
   293  	tc.wantIdle("no more frames sent after packet loss")
   294  }
   295  
   296  func TestLostMaxDataFrame(t *testing.T) {
   297  	// "An updated value is sent in a MAX_DATA frame if the packet
   298  	// containing the most recently sent MAX_DATA frame is declared lost [...]"
   299  	// https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.7
   300  	lostFrameTest(t, func(t *testing.T, pto bool) {
   301  		const maxWindowSize = 32
   302  		buf := make([]byte, maxWindowSize)
   303  		tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, func(c *Config) {
   304  			c.MaxConnReadBufferSize = 32
   305  		})
   306  
   307  		// We send MAX_DATA = 63.
   308  		tc.writeFrames(packetType1RTT, debugFrameStream{
   309  			id:   s.id,
   310  			off:  0,
   311  			data: make([]byte, maxWindowSize),
   312  		})
   313  		if n, err := s.Read(buf[:maxWindowSize-1]); err != nil || n != maxWindowSize-1 {
   314  			t.Fatalf("Read() = %v, %v; want %v, nil", n, err, maxWindowSize-1)
   315  		}
   316  		tc.wantFrame("conn window is extended after reading data",
   317  			packetType1RTT, debugFrameMaxData{
   318  				max: (maxWindowSize * 2) - 1,
   319  			})
   320  
   321  		// MAX_DATA = 64, which is only one more byte, so we don't send the frame.
   322  		if n, err := s.Read(buf); err != nil || n != 1 {
   323  			t.Fatalf("Read() = %v, %v; want %v, nil", n, err, 1)
   324  		}
   325  		tc.wantIdle("read doesn't extend window enough to send another MAX_DATA")
   326  
   327  		// The MAX_DATA = 63 packet was lost, so we send 64.
   328  		tc.triggerLossOrPTO(packetType1RTT, pto)
   329  		tc.wantFrame("resent MAX_DATA includes most current value",
   330  			packetType1RTT, debugFrameMaxData{
   331  				max: maxWindowSize * 2,
   332  			})
   333  	})
   334  }
   335  
   336  func TestLostMaxStreamDataFrame(t *testing.T) {
   337  	// "[...] an updated value is sent when the packet containing
   338  	// the most recent MAX_STREAM_DATA frame for a stream is lost"
   339  	// https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.8
   340  	lostFrameTest(t, func(t *testing.T, pto bool) {
   341  		const maxWindowSize = 32
   342  		buf := make([]byte, maxWindowSize)
   343  		tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, func(c *Config) {
   344  			c.MaxStreamReadBufferSize = maxWindowSize
   345  		})
   346  
   347  		// We send MAX_STREAM_DATA = 63.
   348  		tc.writeFrames(packetType1RTT, debugFrameStream{
   349  			id:   s.id,
   350  			off:  0,
   351  			data: make([]byte, maxWindowSize),
   352  		})
   353  		if n, err := s.Read(buf[:maxWindowSize-1]); err != nil || n != maxWindowSize-1 {
   354  			t.Fatalf("Read() = %v, %v; want %v, nil", n, err, maxWindowSize-1)
   355  		}
   356  		tc.wantFrame("stream window is extended after reading data",
   357  			packetType1RTT, debugFrameMaxStreamData{
   358  				id:  s.id,
   359  				max: (maxWindowSize * 2) - 1,
   360  			})
   361  
   362  		// MAX_STREAM_DATA = 64, which is only one more byte, so we don't send the frame.
   363  		if n, err := s.Read(buf); err != nil || n != 1 {
   364  			t.Fatalf("Read() = %v, %v; want %v, nil", n, err, 1)
   365  		}
   366  		tc.wantIdle("read doesn't extend window enough to send another MAX_STREAM_DATA")
   367  
   368  		// The MAX_STREAM_DATA = 63 packet was lost, so we send 64.
   369  		tc.triggerLossOrPTO(packetType1RTT, pto)
   370  		tc.wantFrame("resent MAX_STREAM_DATA includes most current value",
   371  			packetType1RTT, debugFrameMaxStreamData{
   372  				id:  s.id,
   373  				max: maxWindowSize * 2,
   374  			})
   375  	})
   376  }
   377  
   378  func TestLostMaxStreamDataFrameAfterStreamFinReceived(t *testing.T) {
   379  	// "An endpoint SHOULD stop sending MAX_STREAM_DATA frames when
   380  	// the receiving part of the stream enters a "Size Known" or "Reset Recvd" state."
   381  	// https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.8
   382  	lostFrameTest(t, func(t *testing.T, pto bool) {
   383  		const maxWindowSize = 10
   384  		buf := make([]byte, maxWindowSize)
   385  		tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, func(c *Config) {
   386  			c.MaxStreamReadBufferSize = maxWindowSize
   387  		})
   388  
   389  		tc.writeFrames(packetType1RTT, debugFrameStream{
   390  			id:   s.id,
   391  			off:  0,
   392  			data: make([]byte, maxWindowSize),
   393  		})
   394  		if n, err := s.Read(buf); err != nil || n != maxWindowSize {
   395  			t.Fatalf("Read() = %v, %v; want %v, nil", n, err, maxWindowSize)
   396  		}
   397  		tc.wantFrame("stream window is extended after reading data",
   398  			packetType1RTT, debugFrameMaxStreamData{
   399  				id:  s.id,
   400  				max: 2 * maxWindowSize,
   401  			})
   402  
   403  		tc.writeFrames(packetType1RTT, debugFrameStream{
   404  			id:  s.id,
   405  			off: maxWindowSize,
   406  			fin: true,
   407  		})
   408  
   409  		tc.ignoreFrame(frameTypePing)
   410  		tc.triggerLossOrPTO(packetType1RTT, pto)
   411  		tc.wantIdle("lost MAX_STREAM_DATA not resent for stream in 'size known'")
   412  	})
   413  }
   414  
   415  func TestLostMaxStreamsFrameMostRecent(t *testing.T) {
   416  	// "[...] an updated value is sent when a packet containing the
   417  	// most recent MAX_STREAMS for a stream type frame is declared lost [...]"
   418  	// https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.9
   419  	testStreamTypes(t, "", func(t *testing.T, styp streamType) {
   420  		lostFrameTest(t, func(t *testing.T, pto bool) {
   421  			ctx := canceledContext()
   422  			tc := newTestConn(t, serverSide, func(c *Config) {
   423  				c.MaxUniRemoteStreams = 1
   424  				c.MaxBidiRemoteStreams = 1
   425  			})
   426  			tc.handshake()
   427  			tc.ignoreFrame(frameTypeAck)
   428  			tc.writeFrames(packetType1RTT, debugFrameStream{
   429  				id:  newStreamID(clientSide, styp, 0),
   430  				fin: true,
   431  			})
   432  			s, err := tc.conn.AcceptStream(ctx)
   433  			if err != nil {
   434  				t.Fatalf("AcceptStream() = %v", err)
   435  			}
   436  			s.CloseContext(ctx)
   437  			if styp == bidiStream {
   438  				tc.wantFrame("stream is closed",
   439  					packetType1RTT, debugFrameStream{
   440  						id:   s.id,
   441  						data: []byte{},
   442  						fin:  true,
   443  					})
   444  				tc.writeAckForAll()
   445  			}
   446  			tc.wantFrame("closing stream updates peer's MAX_STREAMS",
   447  				packetType1RTT, debugFrameMaxStreams{
   448  					streamType: styp,
   449  					max:        2,
   450  				})
   451  
   452  			tc.triggerLossOrPTO(packetType1RTT, pto)
   453  			tc.wantFrame("lost MAX_STREAMS is resent",
   454  				packetType1RTT, debugFrameMaxStreams{
   455  					streamType: styp,
   456  					max:        2,
   457  				})
   458  		})
   459  	})
   460  }
   461  
   462  func TestLostMaxStreamsFrameNotMostRecent(t *testing.T) {
   463  	// Send two MAX_STREAMS frames, lose the first one.
   464  	//
   465  	// No PTO mode for this test: The ack that causes the first frame
   466  	// to be lost arms the loss timer for the second, so the PTO timer is not armed.
   467  	const pto = false
   468  	ctx := canceledContext()
   469  	tc := newTestConn(t, serverSide, func(c *Config) {
   470  		c.MaxUniRemoteStreams = 2
   471  	})
   472  	tc.handshake()
   473  	tc.ignoreFrame(frameTypeAck)
   474  	for i := int64(0); i < 2; i++ {
   475  		tc.writeFrames(packetType1RTT, debugFrameStream{
   476  			id:  newStreamID(clientSide, uniStream, i),
   477  			fin: true,
   478  		})
   479  		s, err := tc.conn.AcceptStream(ctx)
   480  		if err != nil {
   481  			t.Fatalf("AcceptStream() = %v", err)
   482  		}
   483  		if err := s.CloseContext(ctx); err != nil {
   484  			t.Fatalf("stream.Close() = %v", err)
   485  		}
   486  		tc.wantFrame("closing stream updates peer's MAX_STREAMS",
   487  			packetType1RTT, debugFrameMaxStreams{
   488  				streamType: uniStream,
   489  				max:        3 + i,
   490  			})
   491  	}
   492  
   493  	// The second MAX_STREAMS frame is acked.
   494  	tc.writeAckForLatest()
   495  
   496  	// The first MAX_STREAMS frame is lost.
   497  	tc.conn.ping(appDataSpace)
   498  	tc.wantFrame("connection should send a PING frame",
   499  		packetType1RTT, debugFramePing{})
   500  	tc.triggerLossOrPTO(packetType1RTT, pto)
   501  	tc.wantIdle("superseded MAX_DATA is not resent on loss")
   502  }
   503  
   504  func TestLostStreamDataBlockedFrame(t *testing.T) {
   505  	// "A new [STREAM_DATA_BLOCKED] frame is sent if a packet containing
   506  	// the most recent frame for a scope is lost [...]"
   507  	// https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.10
   508  	lostFrameTest(t, func(t *testing.T, pto bool) {
   509  		tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, func(p *transportParameters) {
   510  			p.initialMaxStreamsUni = 1
   511  			p.initialMaxData = 1 << 20
   512  		})
   513  
   514  		w := runAsync(tc, func(ctx context.Context) (int, error) {
   515  			return s.WriteContext(ctx, []byte{0, 1, 2, 3})
   516  		})
   517  		defer w.cancel()
   518  		tc.wantFrame("write is blocked by flow control",
   519  			packetType1RTT, debugFrameStreamDataBlocked{
   520  				id:  s.id,
   521  				max: 0,
   522  			})
   523  
   524  		tc.writeFrames(packetType1RTT, debugFrameMaxStreamData{
   525  			id:  s.id,
   526  			max: 1,
   527  		})
   528  		tc.wantFrame("write makes some progress, but is still blocked by flow control",
   529  			packetType1RTT, debugFrameStreamDataBlocked{
   530  				id:  s.id,
   531  				max: 1,
   532  			})
   533  		tc.wantFrame("write consuming available window",
   534  			packetType1RTT, debugFrameStream{
   535  				id:   s.id,
   536  				off:  0,
   537  				data: []byte{0},
   538  			})
   539  
   540  		tc.triggerLossOrPTO(packetType1RTT, pto)
   541  		tc.wantFrame("STREAM_DATA_BLOCKED is resent",
   542  			packetType1RTT, debugFrameStreamDataBlocked{
   543  				id:  s.id,
   544  				max: 1,
   545  			})
   546  		tc.wantFrame("STREAM is resent as well",
   547  			packetType1RTT, debugFrameStream{
   548  				id:   s.id,
   549  				off:  0,
   550  				data: []byte{0},
   551  			})
   552  	})
   553  }
   554  
   555  func TestLostStreamDataBlockedFrameAfterStreamUnblocked(t *testing.T) {
   556  	// "A new [STREAM_DATA_BLOCKED] frame is sent [...] only while
   557  	// the endpoint is blocked on the corresponding limit."
   558  	// https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.10
   559  	lostFrameTest(t, func(t *testing.T, pto bool) {
   560  		tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, func(p *transportParameters) {
   561  			p.initialMaxStreamsUni = 1
   562  			p.initialMaxData = 1 << 20
   563  		})
   564  
   565  		data := []byte{0, 1, 2, 3}
   566  		w := runAsync(tc, func(ctx context.Context) (int, error) {
   567  			return s.WriteContext(ctx, data)
   568  		})
   569  		defer w.cancel()
   570  		tc.wantFrame("write is blocked by flow control",
   571  			packetType1RTT, debugFrameStreamDataBlocked{
   572  				id:  s.id,
   573  				max: 0,
   574  			})
   575  
   576  		tc.writeFrames(packetType1RTT, debugFrameMaxStreamData{
   577  			id:  s.id,
   578  			max: 10,
   579  		})
   580  		tc.wantFrame("write completes after flow control available",
   581  			packetType1RTT, debugFrameStream{
   582  				id:   s.id,
   583  				off:  0,
   584  				data: data,
   585  			})
   586  
   587  		tc.triggerLossOrPTO(packetType1RTT, pto)
   588  		tc.wantFrame("STREAM data is resent",
   589  			packetType1RTT, debugFrameStream{
   590  				id:   s.id,
   591  				off:  0,
   592  				data: data,
   593  			})
   594  		tc.wantIdle("STREAM_DATA_BLOCKED is not resent, since the stream is not blocked")
   595  	})
   596  }
   597  
   598  func TestLostNewConnectionIDFrame(t *testing.T) {
   599  	// "New connection IDs are [...] retransmitted if the packet containing them is lost."
   600  	// https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.13
   601  	lostFrameTest(t, func(t *testing.T, pto bool) {
   602  		tc := newTestConn(t, serverSide)
   603  		tc.handshake()
   604  		tc.ignoreFrame(frameTypeAck)
   605  
   606  		tc.writeFrames(packetType1RTT,
   607  			debugFrameRetireConnectionID{
   608  				seq: 1,
   609  			})
   610  		tc.wantFrame("provide a new connection ID after peer retires old one",
   611  			packetType1RTT, debugFrameNewConnectionID{
   612  				seq:    2,
   613  				connID: testLocalConnID(2),
   614  				token:  testLocalStatelessResetToken(2),
   615  			})
   616  
   617  		tc.triggerLossOrPTO(packetType1RTT, pto)
   618  		tc.wantFrame("resend new connection ID",
   619  			packetType1RTT, debugFrameNewConnectionID{
   620  				seq:    2,
   621  				connID: testLocalConnID(2),
   622  				token:  testLocalStatelessResetToken(2),
   623  			})
   624  	})
   625  }
   626  
   627  func TestLostRetireConnectionIDFrame(t *testing.T) {
   628  	// "[...] retired connection IDs are [...] retransmitted
   629  	// if the packet containing them is lost."
   630  	// https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.13
   631  	lostFrameTest(t, func(t *testing.T, pto bool) {
   632  		tc := newTestConn(t, clientSide)
   633  		tc.handshake()
   634  		tc.ignoreFrame(frameTypeAck)
   635  
   636  		tc.writeFrames(packetType1RTT,
   637  			debugFrameNewConnectionID{
   638  				seq:           2,
   639  				retirePriorTo: 1,
   640  				connID:        testPeerConnID(2),
   641  			})
   642  		tc.wantFrame("peer requested connection id be retired",
   643  			packetType1RTT, debugFrameRetireConnectionID{
   644  				seq: 0,
   645  			})
   646  
   647  		tc.triggerLossOrPTO(packetType1RTT, pto)
   648  		tc.wantFrame("resend RETIRE_CONNECTION_ID",
   649  			packetType1RTT, debugFrameRetireConnectionID{
   650  				seq: 0,
   651  			})
   652  	})
   653  }
   654  
   655  func TestLostHandshakeDoneFrame(t *testing.T) {
   656  	// "The HANDSHAKE_DONE frame MUST be retransmitted until it is acknowledged."
   657  	// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.3-3.16
   658  	lostFrameTest(t, func(t *testing.T, pto bool) {
   659  		tc := newTestConn(t, serverSide)
   660  		tc.ignoreFrame(frameTypeAck)
   661  
   662  		tc.writeFrames(packetTypeInitial,
   663  			debugFrameCrypto{
   664  				data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
   665  			})
   666  		tc.wantFrame("server sends Initial CRYPTO frame",
   667  			packetTypeInitial, debugFrameCrypto{
   668  				data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
   669  			})
   670  		tc.wantFrame("server sends Handshake CRYPTO frame",
   671  			packetTypeHandshake, debugFrameCrypto{
   672  				data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake],
   673  			})
   674  		tc.wantFrame("server provides an additional connection ID",
   675  			packetType1RTT, debugFrameNewConnectionID{
   676  				seq:    1,
   677  				connID: testLocalConnID(1),
   678  				token:  testLocalStatelessResetToken(1),
   679  			})
   680  		tc.writeFrames(packetTypeHandshake,
   681  			debugFrameCrypto{
   682  				data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
   683  			})
   684  
   685  		tc.wantFrame("server sends HANDSHAKE_DONE after handshake completes",
   686  			packetType1RTT, debugFrameHandshakeDone{})
   687  
   688  		tc.triggerLossOrPTO(packetType1RTT, pto)
   689  		tc.wantFrame("server resends HANDSHAKE_DONE",
   690  			packetType1RTT, debugFrameHandshakeDone{})
   691  	})
   692  }
   693  

View as plain text