github.com/tumi8/quic-go@v0.37.4-tum/integrationtests/self/zero_rtt_oldgo_test.go (about)

     1  //go:build !go1.21
     2  
     3  package self_test
     4  
     5  import (
     6  	"context"
     7  	"crypto/tls"
     8  	"fmt"
     9  	"io"
    10  	mrand "math/rand"
    11  	"net"
    12  	"sync"
    13  	"sync/atomic"
    14  	"time"
    15  
    16  	"github.com/tumi8/quic-go"
    17  	quicproxy "github.com/tumi8/quic-go/integrationtests/tools/proxy"
    18  	"github.com/tumi8/quic-go/noninternal/protocol"
    19  	"github.com/tumi8/quic-go/noninternal/wire"
    20  	"github.com/tumi8/quic-go/logging"
    21  
    22  	. "github.com/onsi/ginkgo/v2"
    23  	. "github.com/onsi/gomega"
    24  )
    25  
    26  var _ = Describe("0-RTT", func() {
    27  	rtt := scaleDuration(5 * time.Millisecond)
    28  
    29  	runCountingProxy := func(serverPort int) (*quicproxy.QuicProxy, *uint32) {
    30  		var num0RTTPackets uint32 // to be used as an atomic
    31  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
    32  			RemoteAddr: fmt.Sprintf("localhost:%d", serverPort),
    33  			DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration {
    34  				for len(data) > 0 {
    35  					if !wire.IsLongHeaderPacket(data[0]) {
    36  						break
    37  					}
    38  					hdr, _, rest, err := wire.ParsePacket(data)
    39  					Expect(err).ToNot(HaveOccurred())
    40  					if hdr.Type == protocol.PacketType0RTT {
    41  						atomic.AddUint32(&num0RTTPackets, 1)
    42  						break
    43  					}
    44  					data = rest
    45  				}
    46  				return rtt / 2
    47  			},
    48  		})
    49  		Expect(err).ToNot(HaveOccurred())
    50  
    51  		return proxy, &num0RTTPackets
    52  	}
    53  
    54  	dialAndReceiveSessionTicket := func(serverConf *quic.Config) (*tls.Config, *tls.Config) {
    55  		tlsConf := getTLSConfig()
    56  		if serverConf == nil {
    57  			serverConf = getQuicConfig(nil)
    58  		}
    59  		serverConf.Allow0RTT = true
    60  		ln, err := quic.ListenAddrEarly(
    61  			"localhost:0",
    62  			tlsConf,
    63  			serverConf,
    64  		)
    65  		Expect(err).ToNot(HaveOccurred())
    66  		defer ln.Close()
    67  
    68  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
    69  			RemoteAddr:  fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port),
    70  			DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { return rtt / 2 },
    71  		})
    72  		Expect(err).ToNot(HaveOccurred())
    73  		defer proxy.Close()
    74  
    75  		// dial the first connection in order to receive a session ticket
    76  		done := make(chan struct{})
    77  		go func() {
    78  			defer GinkgoRecover()
    79  			defer close(done)
    80  			conn, err := ln.Accept(context.Background())
    81  			Expect(err).ToNot(HaveOccurred())
    82  			<-conn.Context().Done()
    83  		}()
    84  
    85  		clientConf := getTLSClientConfig()
    86  		gets := make(chan string, 100)
    87  		puts := make(chan string, 100)
    88  		clientConf.ClientSessionCache = newClientSessionCache(tls.NewLRUClientSessionCache(100), gets, puts)
    89  		conn, err := quic.DialAddr(
    90  			context.Background(),
    91  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
    92  			clientConf,
    93  			getQuicConfig(nil),
    94  		)
    95  		Expect(err).ToNot(HaveOccurred())
    96  		Eventually(puts).Should(Receive())
    97  		// received the session ticket. We're done here.
    98  		Expect(conn.CloseWithError(0, "")).To(Succeed())
    99  		Eventually(done).Should(BeClosed())
   100  		return tlsConf, clientConf
   101  	}
   102  
   103  	transfer0RTTData := func(
   104  		ln *quic.EarlyListener,
   105  		proxyPort int,
   106  		connIDLen int,
   107  		clientTLSConf *tls.Config,
   108  		clientConf *quic.Config,
   109  		testdata []byte, // data to transfer
   110  	) {
   111  		// accept the second connection, and receive the data sent in 0-RTT
   112  		done := make(chan struct{})
   113  		go func() {
   114  			defer GinkgoRecover()
   115  			conn, err := ln.Accept(context.Background())
   116  			Expect(err).ToNot(HaveOccurred())
   117  			str, err := conn.AcceptStream(context.Background())
   118  			Expect(err).ToNot(HaveOccurred())
   119  			data, err := io.ReadAll(str)
   120  			Expect(err).ToNot(HaveOccurred())
   121  			Expect(data).To(Equal(testdata))
   122  			Expect(str.Close()).To(Succeed())
   123  			Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
   124  			<-conn.Context().Done()
   125  			close(done)
   126  		}()
   127  
   128  		if clientConf == nil {
   129  			clientConf = getQuicConfig(nil)
   130  		}
   131  		var conn quic.EarlyConnection
   132  		if connIDLen == 0 {
   133  			var err error
   134  			conn, err = quic.DialAddrEarly(
   135  				context.Background(),
   136  				fmt.Sprintf("localhost:%d", proxyPort),
   137  				clientTLSConf,
   138  				clientConf,
   139  			)
   140  			Expect(err).ToNot(HaveOccurred())
   141  		} else {
   142  			addr, err := net.ResolveUDPAddr("udp", "localhost:0")
   143  			Expect(err).ToNot(HaveOccurred())
   144  			udpConn, err := net.ListenUDP("udp", addr)
   145  			Expect(err).ToNot(HaveOccurred())
   146  			defer udpConn.Close()
   147  			tr := &quic.Transport{
   148  				Conn:               udpConn,
   149  				ConnectionIDLength: connIDLen,
   150  			}
   151  			defer tr.Close()
   152  			conn, err = tr.DialEarly(
   153  				context.Background(),
   154  				&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: proxyPort},
   155  				clientTLSConf,
   156  				clientConf,
   157  			)
   158  			Expect(err).ToNot(HaveOccurred())
   159  		}
   160  		defer conn.CloseWithError(0, "")
   161  		str, err := conn.OpenStream()
   162  		Expect(err).ToNot(HaveOccurred())
   163  		_, err = str.Write(testdata)
   164  		Expect(err).ToNot(HaveOccurred())
   165  		Expect(str.Close()).To(Succeed())
   166  		<-conn.HandshakeComplete()
   167  		Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
   168  		io.ReadAll(str) // wait for the EOF from the server to arrive before closing the conn
   169  		conn.CloseWithError(0, "")
   170  		Eventually(done).Should(BeClosed())
   171  		Eventually(conn.Context().Done()).Should(BeClosed())
   172  	}
   173  
   174  	check0RTTRejected := func(
   175  		ln *quic.EarlyListener,
   176  		proxyPort int,
   177  		clientConf *tls.Config,
   178  	) {
   179  		conn, err := quic.DialAddrEarly(
   180  			context.Background(),
   181  			fmt.Sprintf("localhost:%d", proxyPort),
   182  			clientConf,
   183  			getQuicConfig(nil),
   184  		)
   185  		Expect(err).ToNot(HaveOccurred())
   186  		str, err := conn.OpenUniStream()
   187  		Expect(err).ToNot(HaveOccurred())
   188  		_, err = str.Write(make([]byte, 3000))
   189  		Expect(err).ToNot(HaveOccurred())
   190  		Expect(str.Close()).To(Succeed())
   191  		Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
   192  
   193  		// make sure the server doesn't process the data
   194  		ctx, cancel := context.WithTimeout(context.Background(), scaleDuration(50*time.Millisecond))
   195  		defer cancel()
   196  		serverConn, err := ln.Accept(ctx)
   197  		Expect(err).ToNot(HaveOccurred())
   198  		Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse())
   199  		_, err = serverConn.AcceptUniStream(ctx)
   200  		Expect(err).To(Equal(context.DeadlineExceeded))
   201  		Expect(serverConn.CloseWithError(0, "")).To(Succeed())
   202  		Eventually(conn.Context().Done()).Should(BeClosed())
   203  	}
   204  
   205  	// can be used to extract 0-RTT from a packetTracer
   206  	get0RTTPackets := func(packets []packet) []protocol.PacketNumber {
   207  		var zeroRTTPackets []protocol.PacketNumber
   208  		for _, p := range packets {
   209  			if p.hdr.Type == protocol.PacketType0RTT {
   210  				zeroRTTPackets = append(zeroRTTPackets, p.hdr.PacketNumber)
   211  			}
   212  		}
   213  		return zeroRTTPackets
   214  	}
   215  
   216  	for _, l := range []int{0, 15} {
   217  		connIDLen := l
   218  
   219  		It(fmt.Sprintf("transfers 0-RTT data, with %d byte connection IDs", connIDLen), func() {
   220  			tlsConf, clientTLSConf := dialAndReceiveSessionTicket(nil)
   221  
   222  			tracer := newPacketTracer()
   223  			ln, err := quic.ListenAddrEarly(
   224  				"localhost:0",
   225  				tlsConf,
   226  				getQuicConfig(&quic.Config{
   227  					Allow0RTT: true,
   228  					Tracer:    newTracer(tracer),
   229  				}),
   230  			)
   231  			Expect(err).ToNot(HaveOccurred())
   232  			defer ln.Close()
   233  
   234  			proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   235  			defer proxy.Close()
   236  
   237  			transfer0RTTData(
   238  				ln,
   239  				proxy.LocalPort(),
   240  				connIDLen,
   241  				clientTLSConf,
   242  				getQuicConfig(nil),
   243  				PRData,
   244  			)
   245  
   246  			var numNewConnIDs int
   247  			for _, p := range tracer.getRcvdLongHeaderPackets() {
   248  				for _, f := range p.frames {
   249  					if _, ok := f.(*logging.NewConnectionIDFrame); ok {
   250  						numNewConnIDs++
   251  					}
   252  				}
   253  			}
   254  			if connIDLen == 0 {
   255  				Expect(numNewConnIDs).To(BeZero())
   256  			} else {
   257  				Expect(numNewConnIDs).ToNot(BeZero())
   258  			}
   259  
   260  			num0RTT := atomic.LoadUint32(num0RTTPackets)
   261  			fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   262  			Expect(num0RTT).ToNot(BeZero())
   263  			zeroRTTPackets := get0RTTPackets(tracer.getRcvdLongHeaderPackets())
   264  			Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10))
   265  			Expect(zeroRTTPackets).To(ContainElement(protocol.PacketNumber(0)))
   266  		})
   267  	}
   268  
   269  	// Test that data intended to be sent with 1-RTT protection is not sent in 0-RTT packets.
   270  	It("waits for a connection until the handshake is done", func() {
   271  		tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
   272  
   273  		zeroRTTData := GeneratePRData(5 << 10)
   274  		oneRTTData := PRData
   275  
   276  		tracer := newPacketTracer()
   277  		ln, err := quic.ListenAddrEarly(
   278  			"localhost:0",
   279  			tlsConf,
   280  			getQuicConfig(&quic.Config{
   281  				Allow0RTT: true,
   282  				Tracer:    newTracer(tracer),
   283  			}),
   284  		)
   285  		Expect(err).ToNot(HaveOccurred())
   286  		defer ln.Close()
   287  
   288  		// now accept the second connection, and receive the 0-RTT data
   289  		go func() {
   290  			defer GinkgoRecover()
   291  			conn, err := ln.Accept(context.Background())
   292  			Expect(err).ToNot(HaveOccurred())
   293  			str, err := conn.AcceptUniStream(context.Background())
   294  			Expect(err).ToNot(HaveOccurred())
   295  			data, err := io.ReadAll(str)
   296  			Expect(err).ToNot(HaveOccurred())
   297  			Expect(data).To(Equal(zeroRTTData))
   298  			str, err = conn.AcceptUniStream(context.Background())
   299  			Expect(err).ToNot(HaveOccurred())
   300  			data, err = io.ReadAll(str)
   301  			Expect(err).ToNot(HaveOccurred())
   302  			Expect(data).To(Equal(oneRTTData))
   303  			Expect(conn.CloseWithError(0, "")).To(Succeed())
   304  		}()
   305  
   306  		proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   307  		defer proxy.Close()
   308  
   309  		conn, err := quic.DialAddrEarly(
   310  			context.Background(),
   311  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   312  			clientConf,
   313  			getQuicConfig(nil),
   314  		)
   315  		Expect(err).ToNot(HaveOccurred())
   316  		firstStr, err := conn.OpenUniStream()
   317  		Expect(err).ToNot(HaveOccurred())
   318  		_, err = firstStr.Write(zeroRTTData)
   319  		Expect(err).ToNot(HaveOccurred())
   320  		Expect(firstStr.Close()).To(Succeed())
   321  
   322  		// wait for the handshake to complete
   323  		Eventually(conn.HandshakeComplete()).Should(BeClosed())
   324  		str, err := conn.OpenUniStream()
   325  		Expect(err).ToNot(HaveOccurred())
   326  		_, err = str.Write(PRData)
   327  		Expect(err).ToNot(HaveOccurred())
   328  		Expect(str.Close()).To(Succeed())
   329  		<-conn.Context().Done()
   330  
   331  		// check that 0-RTT packets only contain STREAM frames for the first stream
   332  		var num0RTT int
   333  		for _, p := range tracer.getRcvdLongHeaderPackets() {
   334  			if p.hdr.Header.Type != protocol.PacketType0RTT {
   335  				continue
   336  			}
   337  			for _, f := range p.frames {
   338  				sf, ok := f.(*logging.StreamFrame)
   339  				if !ok {
   340  					continue
   341  				}
   342  				num0RTT++
   343  				Expect(sf.StreamID).To(Equal(firstStr.StreamID()))
   344  			}
   345  		}
   346  		fmt.Fprintf(GinkgoWriter, "received %d STREAM frames in 0-RTT packets\n", num0RTT)
   347  		Expect(num0RTT).ToNot(BeZero())
   348  	})
   349  
   350  	It("transfers 0-RTT data, when 0-RTT packets are lost", func() {
   351  		var (
   352  			num0RTTPackets uint32 // to be used as an atomic
   353  			num0RTTDropped uint32
   354  		)
   355  
   356  		tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
   357  
   358  		tracer := newPacketTracer()
   359  		ln, err := quic.ListenAddrEarly(
   360  			"localhost:0",
   361  			tlsConf,
   362  			getQuicConfig(&quic.Config{
   363  				Allow0RTT: true,
   364  				Tracer:    newTracer(tracer),
   365  			}),
   366  		)
   367  		Expect(err).ToNot(HaveOccurred())
   368  		defer ln.Close()
   369  
   370  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
   371  			RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port),
   372  			DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration {
   373  				if wire.IsLongHeaderPacket(data[0]) {
   374  					hdr, _, _, err := wire.ParsePacket(data)
   375  					Expect(err).ToNot(HaveOccurred())
   376  					if hdr.Type == protocol.PacketType0RTT {
   377  						atomic.AddUint32(&num0RTTPackets, 1)
   378  					}
   379  				}
   380  				return rtt / 2
   381  			},
   382  			DropPacket: func(_ quicproxy.Direction, data []byte) bool {
   383  				if !wire.IsLongHeaderPacket(data[0]) {
   384  					return false
   385  				}
   386  				hdr, _, _, err := wire.ParsePacket(data)
   387  				Expect(err).ToNot(HaveOccurred())
   388  				if hdr.Type == protocol.PacketType0RTT {
   389  					// drop 25% of the 0-RTT packets
   390  					drop := mrand.Intn(4) == 0
   391  					if drop {
   392  						atomic.AddUint32(&num0RTTDropped, 1)
   393  					}
   394  					return drop
   395  				}
   396  				return false
   397  			},
   398  		})
   399  		Expect(err).ToNot(HaveOccurred())
   400  		defer proxy.Close()
   401  
   402  		transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData)
   403  
   404  		num0RTT := atomic.LoadUint32(&num0RTTPackets)
   405  		numDropped := atomic.LoadUint32(&num0RTTDropped)
   406  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets. Dropped %d of those.", num0RTT, numDropped)
   407  		Expect(numDropped).ToNot(BeZero())
   408  		Expect(num0RTT).ToNot(BeZero())
   409  		Expect(get0RTTPackets(tracer.getRcvdLongHeaderPackets())).ToNot(BeEmpty())
   410  	})
   411  
   412  	It("retransmits all 0-RTT data when the server performs a Retry", func() {
   413  		var mutex sync.Mutex
   414  		var firstConnID, secondConnID *protocol.ConnectionID
   415  		var firstCounter, secondCounter protocol.ByteCount
   416  
   417  		tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
   418  
   419  		countZeroRTTBytes := func(data []byte) (n protocol.ByteCount) {
   420  			for len(data) > 0 {
   421  				hdr, _, rest, err := wire.ParsePacket(data)
   422  				if err != nil {
   423  					return
   424  				}
   425  				data = rest
   426  				if hdr.Type == protocol.PacketType0RTT {
   427  					n += hdr.Length - 16 /* AEAD tag */
   428  				}
   429  			}
   430  			return
   431  		}
   432  
   433  		tracer := newPacketTracer()
   434  		ln, err := quic.ListenAddrEarly(
   435  			"localhost:0",
   436  			tlsConf,
   437  			getQuicConfig(&quic.Config{
   438  				RequireAddressValidation: func(net.Addr) bool { return true },
   439  				Allow0RTT:                true,
   440  				Tracer:                   newTracer(tracer),
   441  			}),
   442  		)
   443  		Expect(err).ToNot(HaveOccurred())
   444  		defer ln.Close()
   445  
   446  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
   447  			RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port),
   448  			DelayPacket: func(dir quicproxy.Direction, data []byte) time.Duration {
   449  				connID, err := wire.ParseConnectionID(data, 0)
   450  				Expect(err).ToNot(HaveOccurred())
   451  
   452  				mutex.Lock()
   453  				defer mutex.Unlock()
   454  
   455  				if zeroRTTBytes := countZeroRTTBytes(data); zeroRTTBytes > 0 {
   456  					if firstConnID == nil {
   457  						firstConnID = &connID
   458  						firstCounter += zeroRTTBytes
   459  					} else if firstConnID != nil && *firstConnID == connID {
   460  						Expect(secondConnID).To(BeNil())
   461  						firstCounter += zeroRTTBytes
   462  					} else if secondConnID == nil {
   463  						secondConnID = &connID
   464  						secondCounter += zeroRTTBytes
   465  					} else if secondConnID != nil && *secondConnID == connID {
   466  						secondCounter += zeroRTTBytes
   467  					} else {
   468  						Fail("received 3 connection IDs on 0-RTT packets")
   469  					}
   470  				}
   471  				return rtt / 2
   472  			},
   473  		})
   474  		Expect(err).ToNot(HaveOccurred())
   475  		defer proxy.Close()
   476  
   477  		transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, GeneratePRData(5000)) // ~5 packets
   478  
   479  		mutex.Lock()
   480  		defer mutex.Unlock()
   481  		Expect(firstCounter).To(BeNumerically("~", 5000+100 /* framing overhead */, 100)) // the FIN bit might be sent extra
   482  		Expect(secondCounter).To(BeNumerically("~", firstCounter, 20))
   483  		zeroRTTPackets := get0RTTPackets(tracer.getRcvdLongHeaderPackets())
   484  		Expect(len(zeroRTTPackets)).To(BeNumerically(">=", 5))
   485  		Expect(zeroRTTPackets[0]).To(BeNumerically(">=", protocol.PacketNumber(5)))
   486  	})
   487  
   488  	It("doesn't reject 0-RTT when the server's transport stream limit increased", func() {
   489  		const maxStreams = 1
   490  		tlsConf, clientConf := dialAndReceiveSessionTicket(getQuicConfig(&quic.Config{
   491  			MaxIncomingUniStreams: maxStreams,
   492  		}))
   493  
   494  		tracer := newPacketTracer()
   495  		ln, err := quic.ListenAddrEarly(
   496  			"localhost:0",
   497  			tlsConf,
   498  			getQuicConfig(&quic.Config{
   499  				MaxIncomingUniStreams: maxStreams + 1,
   500  				Allow0RTT:             true,
   501  				Tracer:                newTracer(tracer),
   502  			}),
   503  		)
   504  		Expect(err).ToNot(HaveOccurred())
   505  		defer ln.Close()
   506  		proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   507  		defer proxy.Close()
   508  
   509  		conn, err := quic.DialAddrEarly(
   510  			context.Background(),
   511  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   512  			clientConf,
   513  			getQuicConfig(nil),
   514  		)
   515  		Expect(err).ToNot(HaveOccurred())
   516  		str, err := conn.OpenUniStream()
   517  		Expect(err).ToNot(HaveOccurred())
   518  		_, err = str.Write([]byte("foobar"))
   519  		Expect(err).ToNot(HaveOccurred())
   520  		Expect(str.Close()).To(Succeed())
   521  		// The client remembers the old limit and refuses to open a new stream.
   522  		_, err = conn.OpenUniStream()
   523  		Expect(err).To(HaveOccurred())
   524  		Expect(err.Error()).To(ContainSubstring("too many open streams"))
   525  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   526  		defer cancel()
   527  		_, err = conn.OpenUniStreamSync(ctx)
   528  		Expect(err).ToNot(HaveOccurred())
   529  		Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
   530  		Expect(conn.CloseWithError(0, "")).To(Succeed())
   531  	})
   532  
   533  	It("rejects 0-RTT when the server's stream limit decreased", func() {
   534  		const maxStreams = 42
   535  		tlsConf, clientConf := dialAndReceiveSessionTicket(getQuicConfig(&quic.Config{
   536  			MaxIncomingStreams: maxStreams,
   537  		}))
   538  
   539  		tracer := newPacketTracer()
   540  		ln, err := quic.ListenAddrEarly(
   541  			"localhost:0",
   542  			tlsConf,
   543  			getQuicConfig(&quic.Config{
   544  				MaxIncomingStreams: maxStreams - 1,
   545  				Allow0RTT:          true,
   546  				Tracer:             newTracer(tracer),
   547  			}),
   548  		)
   549  		Expect(err).ToNot(HaveOccurred())
   550  		defer ln.Close()
   551  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   552  		defer proxy.Close()
   553  		check0RTTRejected(ln, proxy.LocalPort(), clientConf)
   554  
   555  		// The client should send 0-RTT packets, but the server doesn't process them.
   556  		num0RTT := atomic.LoadUint32(num0RTTPackets)
   557  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   558  		Expect(num0RTT).ToNot(BeZero())
   559  		Expect(get0RTTPackets(tracer.getRcvdLongHeaderPackets())).To(BeEmpty())
   560  	})
   561  
   562  	It("rejects 0-RTT when the ALPN changed", func() {
   563  		tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
   564  
   565  		// now close the listener and dial new connection with a different ALPN
   566  		clientConf.NextProtos = []string{"new-alpn"}
   567  		tlsConf.NextProtos = []string{"new-alpn"}
   568  		tracer := newPacketTracer()
   569  		ln, err := quic.ListenAddrEarly(
   570  			"localhost:0",
   571  			tlsConf,
   572  			getQuicConfig(&quic.Config{
   573  				Allow0RTT: true,
   574  				Tracer:    newTracer(tracer),
   575  			}),
   576  		)
   577  		Expect(err).ToNot(HaveOccurred())
   578  		defer ln.Close()
   579  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   580  		defer proxy.Close()
   581  
   582  		check0RTTRejected(ln, proxy.LocalPort(), clientConf)
   583  
   584  		// The client should send 0-RTT packets, but the server doesn't process them.
   585  		num0RTT := atomic.LoadUint32(num0RTTPackets)
   586  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   587  		Expect(num0RTT).ToNot(BeZero())
   588  		Expect(get0RTTPackets(tracer.getRcvdLongHeaderPackets())).To(BeEmpty())
   589  	})
   590  
   591  	It("rejects 0-RTT when the application doesn't allow it", func() {
   592  		tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
   593  
   594  		// now close the listener and dial new connection with a different ALPN
   595  		tracer := newPacketTracer()
   596  		ln, err := quic.ListenAddrEarly(
   597  			"localhost:0",
   598  			tlsConf,
   599  			getQuicConfig(&quic.Config{
   600  				Allow0RTT: false, // application rejects 0-RTT
   601  				Tracer:    newTracer(tracer),
   602  			}),
   603  		)
   604  		Expect(err).ToNot(HaveOccurred())
   605  		defer ln.Close()
   606  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   607  		defer proxy.Close()
   608  
   609  		check0RTTRejected(ln, proxy.LocalPort(), clientConf)
   610  
   611  		// The client should send 0-RTT packets, but the server doesn't process them.
   612  		num0RTT := atomic.LoadUint32(num0RTTPackets)
   613  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   614  		Expect(num0RTT).ToNot(BeZero())
   615  		Expect(get0RTTPackets(tracer.getRcvdLongHeaderPackets())).To(BeEmpty())
   616  	})
   617  
   618  	DescribeTable("flow control limits",
   619  		func(addFlowControlLimit func(*quic.Config, uint64)) {
   620  			tracer := newPacketTracer()
   621  			firstConf := getQuicConfig(&quic.Config{Allow0RTT: true})
   622  			addFlowControlLimit(firstConf, 3)
   623  			tlsConf, clientConf := dialAndReceiveSessionTicket(firstConf)
   624  
   625  			secondConf := getQuicConfig(&quic.Config{
   626  				Allow0RTT: true,
   627  				Tracer:    newTracer(tracer),
   628  			})
   629  			addFlowControlLimit(secondConf, 100)
   630  			ln, err := quic.ListenAddrEarly(
   631  				"localhost:0",
   632  				tlsConf,
   633  				secondConf,
   634  			)
   635  			Expect(err).ToNot(HaveOccurred())
   636  			defer ln.Close()
   637  			proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   638  			defer proxy.Close()
   639  
   640  			conn, err := quic.DialAddrEarly(
   641  				context.Background(),
   642  				fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   643  				clientConf,
   644  				getQuicConfig(nil),
   645  			)
   646  			Expect(err).ToNot(HaveOccurred())
   647  			str, err := conn.OpenUniStream()
   648  			Expect(err).ToNot(HaveOccurred())
   649  			written := make(chan struct{})
   650  			go func() {
   651  				defer GinkgoRecover()
   652  				defer close(written)
   653  				_, err := str.Write([]byte("foobar"))
   654  				Expect(err).ToNot(HaveOccurred())
   655  				Expect(str.Close()).To(Succeed())
   656  			}()
   657  
   658  			Eventually(written).Should(BeClosed())
   659  
   660  			serverConn, err := ln.Accept(context.Background())
   661  			Expect(err).ToNot(HaveOccurred())
   662  			rstr, err := serverConn.AcceptUniStream(context.Background())
   663  			Expect(err).ToNot(HaveOccurred())
   664  			data, err := io.ReadAll(rstr)
   665  			Expect(err).ToNot(HaveOccurred())
   666  			Expect(data).To(Equal([]byte("foobar")))
   667  			Expect(serverConn.ConnectionState().Used0RTT).To(BeTrue())
   668  			Expect(serverConn.CloseWithError(0, "")).To(Succeed())
   669  			Eventually(conn.Context().Done()).Should(BeClosed())
   670  
   671  			var processedFirst bool
   672  			for _, p := range tracer.getRcvdLongHeaderPackets() {
   673  				for _, f := range p.frames {
   674  					if sf, ok := f.(*logging.StreamFrame); ok {
   675  						if !processedFirst {
   676  							// The first STREAM should have been sent in a 0-RTT packet.
   677  							// Due to the flow control limit, the STREAM frame was limit to the first 3 bytes.
   678  							Expect(p.hdr.Type).To(Equal(protocol.PacketType0RTT))
   679  							Expect(sf.Length).To(BeEquivalentTo(3))
   680  							processedFirst = true
   681  						} else {
   682  							Fail("STREAM was shouldn't have been sent in 0-RTT")
   683  						}
   684  					}
   685  				}
   686  			}
   687  		},
   688  		Entry("doesn't reject 0-RTT when the server's transport stream flow control limit increased", func(c *quic.Config, limit uint64) { c.InitialStreamReceiveWindow = limit }),
   689  		Entry("doesn't reject 0-RTT when the server's transport connection flow control limit increased", func(c *quic.Config, limit uint64) { c.InitialConnectionReceiveWindow = limit }),
   690  	)
   691  
   692  	for _, l := range []int{0, 15} {
   693  		connIDLen := l
   694  
   695  		It(fmt.Sprintf("correctly deals with 0-RTT rejections, for %d byte connection IDs", connIDLen), func() {
   696  			tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
   697  			// now dial new connection with different transport parameters
   698  			tracer := newPacketTracer()
   699  			ln, err := quic.ListenAddrEarly(
   700  				"localhost:0",
   701  				tlsConf,
   702  				getQuicConfig(&quic.Config{
   703  					MaxIncomingUniStreams: 1,
   704  					Tracer:                newTracer(tracer),
   705  				}),
   706  			)
   707  			Expect(err).ToNot(HaveOccurred())
   708  			defer ln.Close()
   709  			proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   710  			defer proxy.Close()
   711  
   712  			conn, err := quic.DialAddrEarly(
   713  				context.Background(),
   714  				fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   715  				clientConf,
   716  				getQuicConfig(nil),
   717  			)
   718  			Expect(err).ToNot(HaveOccurred())
   719  			// The client remembers that it was allowed to open 2 uni-directional streams.
   720  			firstStr, err := conn.OpenUniStream()
   721  			Expect(err).ToNot(HaveOccurred())
   722  			written := make(chan struct{}, 2)
   723  			go func() {
   724  				defer GinkgoRecover()
   725  				defer func() { written <- struct{}{} }()
   726  				_, err := firstStr.Write([]byte("first flight"))
   727  				Expect(err).ToNot(HaveOccurred())
   728  			}()
   729  			secondStr, err := conn.OpenUniStream()
   730  			Expect(err).ToNot(HaveOccurred())
   731  			go func() {
   732  				defer GinkgoRecover()
   733  				defer func() { written <- struct{}{} }()
   734  				_, err := secondStr.Write([]byte("first flight"))
   735  				Expect(err).ToNot(HaveOccurred())
   736  			}()
   737  
   738  			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   739  			defer cancel()
   740  			_, err = conn.AcceptStream(ctx)
   741  			Expect(err).To(MatchError(quic.Err0RTTRejected))
   742  			Eventually(written).Should(Receive())
   743  			Eventually(written).Should(Receive())
   744  			_, err = firstStr.Write([]byte("foobar"))
   745  			Expect(err).To(MatchError(quic.Err0RTTRejected))
   746  			_, err = conn.OpenUniStream()
   747  			Expect(err).To(MatchError(quic.Err0RTTRejected))
   748  
   749  			_, err = conn.AcceptStream(ctx)
   750  			Expect(err).To(Equal(quic.Err0RTTRejected))
   751  
   752  			newConn := conn.NextConnection()
   753  			str, err := newConn.OpenUniStream()
   754  			Expect(err).ToNot(HaveOccurred())
   755  			_, err = newConn.OpenUniStream()
   756  			Expect(err).To(HaveOccurred())
   757  			Expect(err.Error()).To(ContainSubstring("too many open streams"))
   758  			_, err = str.Write([]byte("second flight"))
   759  			Expect(err).ToNot(HaveOccurred())
   760  			Expect(str.Close()).To(Succeed())
   761  			Expect(conn.CloseWithError(0, "")).To(Succeed())
   762  
   763  			// The client should send 0-RTT packets, but the server doesn't process them.
   764  			num0RTT := atomic.LoadUint32(num0RTTPackets)
   765  			fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   766  			Expect(num0RTT).ToNot(BeZero())
   767  			Expect(get0RTTPackets(tracer.getRcvdLongHeaderPackets())).To(BeEmpty())
   768  		})
   769  	}
   770  
   771  	It("queues 0-RTT packets, if the Initial is delayed", func() {
   772  		tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
   773  
   774  		tracer := newPacketTracer()
   775  		ln, err := quic.ListenAddrEarly(
   776  			"localhost:0",
   777  			tlsConf,
   778  			getQuicConfig(&quic.Config{
   779  				Allow0RTT: true,
   780  				Tracer:    newTracer(tracer),
   781  			}),
   782  		)
   783  		Expect(err).ToNot(HaveOccurred())
   784  		defer ln.Close()
   785  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
   786  			RemoteAddr: ln.Addr().String(),
   787  			DelayPacket: func(dir quicproxy.Direction, data []byte) time.Duration {
   788  				if dir == quicproxy.DirectionIncoming && wire.IsLongHeaderPacket(data[0]) && data[0]&0x30>>4 == 0 { // Initial packet from client
   789  					return rtt/2 + rtt
   790  				}
   791  				return rtt / 2
   792  			},
   793  		})
   794  		Expect(err).ToNot(HaveOccurred())
   795  		defer proxy.Close()
   796  
   797  		transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData)
   798  
   799  		Expect(tracer.getRcvdLongHeaderPackets()[0].hdr.Type).To(Equal(protocol.PacketTypeInitial))
   800  		zeroRTTPackets := get0RTTPackets(tracer.getRcvdLongHeaderPackets())
   801  		Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10))
   802  		Expect(zeroRTTPackets[0]).To(Equal(protocol.PacketNumber(0)))
   803  	})
   804  })