github.com/MerlinKodo/quic-go@v0.39.2/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/MerlinKodo/quic-go"
    17  	quicproxy "github.com/MerlinKodo/quic-go/integrationtests/tools/proxy"
    18  	"github.com/MerlinKodo/quic-go/internal/protocol"
    19  	"github.com/MerlinKodo/quic-go/internal/wire"
    20  	"github.com/MerlinKodo/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 packetCounter
   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  			counter, 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 counter.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(counter.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  		counter, 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 counter.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  		counter, 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(counter.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  		counter, 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(counter.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  		ln, err := quic.ListenAddrEarly(
   495  			"localhost:0",
   496  			tlsConf,
   497  			getQuicConfig(&quic.Config{
   498  				MaxIncomingUniStreams: maxStreams + 1,
   499  				Allow0RTT:             true,
   500  			}),
   501  		)
   502  		Expect(err).ToNot(HaveOccurred())
   503  		defer ln.Close()
   504  		proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   505  		defer proxy.Close()
   506  
   507  		conn, err := quic.DialAddrEarly(
   508  			context.Background(),
   509  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   510  			clientConf,
   511  			getQuicConfig(nil),
   512  		)
   513  		Expect(err).ToNot(HaveOccurred())
   514  		str, err := conn.OpenUniStream()
   515  		Expect(err).ToNot(HaveOccurred())
   516  		_, err = str.Write([]byte("foobar"))
   517  		Expect(err).ToNot(HaveOccurred())
   518  		Expect(str.Close()).To(Succeed())
   519  		// The client remembers the old limit and refuses to open a new stream.
   520  		_, err = conn.OpenUniStream()
   521  		Expect(err).To(HaveOccurred())
   522  		Expect(err.Error()).To(ContainSubstring("too many open streams"))
   523  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   524  		defer cancel()
   525  		_, err = conn.OpenUniStreamSync(ctx)
   526  		Expect(err).ToNot(HaveOccurred())
   527  		Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
   528  		Expect(conn.CloseWithError(0, "")).To(Succeed())
   529  	})
   530  
   531  	It("rejects 0-RTT when the server's stream limit decreased", func() {
   532  		const maxStreams = 42
   533  		tlsConf, clientConf := dialAndReceiveSessionTicket(getQuicConfig(&quic.Config{
   534  			MaxIncomingStreams: maxStreams,
   535  		}))
   536  
   537  		counter, tracer := newPacketTracer()
   538  		ln, err := quic.ListenAddrEarly(
   539  			"localhost:0",
   540  			tlsConf,
   541  			getQuicConfig(&quic.Config{
   542  				MaxIncomingStreams: maxStreams - 1,
   543  				Allow0RTT:          true,
   544  				Tracer:             newTracer(tracer),
   545  			}),
   546  		)
   547  		Expect(err).ToNot(HaveOccurred())
   548  		defer ln.Close()
   549  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   550  		defer proxy.Close()
   551  		check0RTTRejected(ln, proxy.LocalPort(), clientConf)
   552  
   553  		// The client should send 0-RTT packets, but the server doesn't process them.
   554  		num0RTT := atomic.LoadUint32(num0RTTPackets)
   555  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   556  		Expect(num0RTT).ToNot(BeZero())
   557  		Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
   558  	})
   559  
   560  	It("rejects 0-RTT when the ALPN changed", func() {
   561  		tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
   562  
   563  		// now close the listener and dial new connection with a different ALPN
   564  		clientConf.NextProtos = []string{"new-alpn"}
   565  		tlsConf.NextProtos = []string{"new-alpn"}
   566  		counter, tracer := newPacketTracer()
   567  		ln, err := quic.ListenAddrEarly(
   568  			"localhost:0",
   569  			tlsConf,
   570  			getQuicConfig(&quic.Config{
   571  				Allow0RTT: true,
   572  				Tracer:    newTracer(tracer),
   573  			}),
   574  		)
   575  		Expect(err).ToNot(HaveOccurred())
   576  		defer ln.Close()
   577  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   578  		defer proxy.Close()
   579  
   580  		check0RTTRejected(ln, proxy.LocalPort(), clientConf)
   581  
   582  		// The client should send 0-RTT packets, but the server doesn't process them.
   583  		num0RTT := atomic.LoadUint32(num0RTTPackets)
   584  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   585  		Expect(num0RTT).ToNot(BeZero())
   586  		Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
   587  	})
   588  
   589  	It("rejects 0-RTT when the application doesn't allow it", func() {
   590  		tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
   591  
   592  		// now close the listener and dial new connection with a different ALPN
   593  		counter, tracer := newPacketTracer()
   594  		ln, err := quic.ListenAddrEarly(
   595  			"localhost:0",
   596  			tlsConf,
   597  			getQuicConfig(&quic.Config{
   598  				Allow0RTT: false, // application rejects 0-RTT
   599  				Tracer:    newTracer(tracer),
   600  			}),
   601  		)
   602  		Expect(err).ToNot(HaveOccurred())
   603  		defer ln.Close()
   604  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   605  		defer proxy.Close()
   606  
   607  		check0RTTRejected(ln, proxy.LocalPort(), clientConf)
   608  
   609  		// The client should send 0-RTT packets, but the server doesn't process them.
   610  		num0RTT := atomic.LoadUint32(num0RTTPackets)
   611  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   612  		Expect(num0RTT).ToNot(BeZero())
   613  		Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
   614  	})
   615  
   616  	DescribeTable("flow control limits",
   617  		func(addFlowControlLimit func(*quic.Config, uint64)) {
   618  			counter, tracer := newPacketTracer()
   619  			firstConf := getQuicConfig(&quic.Config{Allow0RTT: true})
   620  			addFlowControlLimit(firstConf, 3)
   621  			tlsConf, clientConf := dialAndReceiveSessionTicket(firstConf)
   622  
   623  			secondConf := getQuicConfig(&quic.Config{
   624  				Allow0RTT: true,
   625  				Tracer:    newTracer(tracer),
   626  			})
   627  			addFlowControlLimit(secondConf, 100)
   628  			ln, err := quic.ListenAddrEarly(
   629  				"localhost:0",
   630  				tlsConf,
   631  				secondConf,
   632  			)
   633  			Expect(err).ToNot(HaveOccurred())
   634  			defer ln.Close()
   635  			proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   636  			defer proxy.Close()
   637  
   638  			conn, err := quic.DialAddrEarly(
   639  				context.Background(),
   640  				fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   641  				clientConf,
   642  				getQuicConfig(nil),
   643  			)
   644  			Expect(err).ToNot(HaveOccurred())
   645  			str, err := conn.OpenUniStream()
   646  			Expect(err).ToNot(HaveOccurred())
   647  			written := make(chan struct{})
   648  			go func() {
   649  				defer GinkgoRecover()
   650  				defer close(written)
   651  				_, err := str.Write([]byte("foobar"))
   652  				Expect(err).ToNot(HaveOccurred())
   653  				Expect(str.Close()).To(Succeed())
   654  			}()
   655  
   656  			Eventually(written).Should(BeClosed())
   657  
   658  			serverConn, err := ln.Accept(context.Background())
   659  			Expect(err).ToNot(HaveOccurred())
   660  			rstr, err := serverConn.AcceptUniStream(context.Background())
   661  			Expect(err).ToNot(HaveOccurred())
   662  			data, err := io.ReadAll(rstr)
   663  			Expect(err).ToNot(HaveOccurred())
   664  			Expect(data).To(Equal([]byte("foobar")))
   665  			Expect(serverConn.ConnectionState().Used0RTT).To(BeTrue())
   666  			Expect(serverConn.CloseWithError(0, "")).To(Succeed())
   667  			Eventually(conn.Context().Done()).Should(BeClosed())
   668  
   669  			var processedFirst bool
   670  			for _, p := range counter.getRcvdLongHeaderPackets() {
   671  				for _, f := range p.frames {
   672  					if sf, ok := f.(*logging.StreamFrame); ok {
   673  						if !processedFirst {
   674  							// The first STREAM should have been sent in a 0-RTT packet.
   675  							// Due to the flow control limit, the STREAM frame was limit to the first 3 bytes.
   676  							Expect(p.hdr.Type).To(Equal(protocol.PacketType0RTT))
   677  							Expect(sf.Length).To(BeEquivalentTo(3))
   678  							processedFirst = true
   679  						} else {
   680  							Fail("STREAM was shouldn't have been sent in 0-RTT")
   681  						}
   682  					}
   683  				}
   684  			}
   685  		},
   686  		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 }),
   687  		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 }),
   688  	)
   689  
   690  	for _, l := range []int{0, 15} {
   691  		connIDLen := l
   692  
   693  		It(fmt.Sprintf("correctly deals with 0-RTT rejections, for %d byte connection IDs", connIDLen), func() {
   694  			tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
   695  			// now dial new connection with different transport parameters
   696  			counter, tracer := newPacketTracer()
   697  			ln, err := quic.ListenAddrEarly(
   698  				"localhost:0",
   699  				tlsConf,
   700  				getQuicConfig(&quic.Config{
   701  					MaxIncomingUniStreams: 1,
   702  					Tracer:                newTracer(tracer),
   703  				}),
   704  			)
   705  			Expect(err).ToNot(HaveOccurred())
   706  			defer ln.Close()
   707  			proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   708  			defer proxy.Close()
   709  
   710  			conn, err := quic.DialAddrEarly(
   711  				context.Background(),
   712  				fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   713  				clientConf,
   714  				getQuicConfig(nil),
   715  			)
   716  			Expect(err).ToNot(HaveOccurred())
   717  			// The client remembers that it was allowed to open 2 uni-directional streams.
   718  			firstStr, err := conn.OpenUniStream()
   719  			Expect(err).ToNot(HaveOccurred())
   720  			written := make(chan struct{}, 2)
   721  			go func() {
   722  				defer GinkgoRecover()
   723  				defer func() { written <- struct{}{} }()
   724  				_, err := firstStr.Write([]byte("first flight"))
   725  				Expect(err).ToNot(HaveOccurred())
   726  			}()
   727  			secondStr, err := conn.OpenUniStream()
   728  			Expect(err).ToNot(HaveOccurred())
   729  			go func() {
   730  				defer GinkgoRecover()
   731  				defer func() { written <- struct{}{} }()
   732  				_, err := secondStr.Write([]byte("first flight"))
   733  				Expect(err).ToNot(HaveOccurred())
   734  			}()
   735  
   736  			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   737  			defer cancel()
   738  			_, err = conn.AcceptStream(ctx)
   739  			Expect(err).To(MatchError(quic.Err0RTTRejected))
   740  			Eventually(written).Should(Receive())
   741  			Eventually(written).Should(Receive())
   742  			_, err = firstStr.Write([]byte("foobar"))
   743  			Expect(err).To(MatchError(quic.Err0RTTRejected))
   744  			_, err = conn.OpenUniStream()
   745  			Expect(err).To(MatchError(quic.Err0RTTRejected))
   746  
   747  			_, err = conn.AcceptStream(ctx)
   748  			Expect(err).To(Equal(quic.Err0RTTRejected))
   749  
   750  			newConn := conn.NextConnection()
   751  			str, err := newConn.OpenUniStream()
   752  			Expect(err).ToNot(HaveOccurred())
   753  			_, err = newConn.OpenUniStream()
   754  			Expect(err).To(HaveOccurred())
   755  			Expect(err.Error()).To(ContainSubstring("too many open streams"))
   756  			_, err = str.Write([]byte("second flight"))
   757  			Expect(err).ToNot(HaveOccurred())
   758  			Expect(str.Close()).To(Succeed())
   759  			Expect(conn.CloseWithError(0, "")).To(Succeed())
   760  
   761  			// The client should send 0-RTT packets, but the server doesn't process them.
   762  			num0RTT := atomic.LoadUint32(num0RTTPackets)
   763  			fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   764  			Expect(num0RTT).ToNot(BeZero())
   765  			Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
   766  		})
   767  	}
   768  
   769  	It("queues 0-RTT packets, if the Initial is delayed", func() {
   770  		tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
   771  
   772  		counter, tracer := newPacketTracer()
   773  		ln, err := quic.ListenAddrEarly(
   774  			"localhost:0",
   775  			tlsConf,
   776  			getQuicConfig(&quic.Config{
   777  				Allow0RTT: true,
   778  				Tracer:    newTracer(tracer),
   779  			}),
   780  		)
   781  		Expect(err).ToNot(HaveOccurred())
   782  		defer ln.Close()
   783  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
   784  			RemoteAddr: ln.Addr().String(),
   785  			DelayPacket: func(dir quicproxy.Direction, data []byte) time.Duration {
   786  				if dir == quicproxy.DirectionIncoming && wire.IsLongHeaderPacket(data[0]) && data[0]&0x30>>4 == 0 { // Initial packet from client
   787  					return rtt/2 + rtt
   788  				}
   789  				return rtt / 2
   790  			},
   791  		})
   792  		Expect(err).ToNot(HaveOccurred())
   793  		defer proxy.Close()
   794  
   795  		transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData)
   796  
   797  		Expect(counter.getRcvdLongHeaderPackets()[0].hdr.Type).To(Equal(protocol.PacketTypeInitial))
   798  		zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
   799  		Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10))
   800  		Expect(zeroRTTPackets[0]).To(Equal(protocol.PacketNumber(0)))
   801  	})
   802  
   803  	It("sends 0-RTT datagrams", func() {
   804  		tlsConf, clientTLSConf := dialAndReceiveSessionTicket(getQuicConfig(&quic.Config{
   805  			EnableDatagrams: true,
   806  		}))
   807  
   808  		counter, tracer := newPacketTracer()
   809  		ln, err := quic.ListenAddrEarly(
   810  			"localhost:0",
   811  			tlsConf,
   812  			getQuicConfig(&quic.Config{
   813  				Allow0RTT:       true,
   814  				EnableDatagrams: true,
   815  				Tracer:          newTracer(tracer),
   816  			}),
   817  		)
   818  		Expect(err).ToNot(HaveOccurred())
   819  		defer ln.Close()
   820  
   821  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   822  		defer proxy.Close()
   823  
   824  		// second connection
   825  		sentMessage := GeneratePRData(100)
   826  		var receivedMessage []byte
   827  		received := make(chan struct{})
   828  		go func() {
   829  			defer GinkgoRecover()
   830  			defer close(received)
   831  			conn, err := ln.Accept(context.Background())
   832  			Expect(err).ToNot(HaveOccurred())
   833  			receivedMessage, err = conn.ReceiveMessage(context.Background())
   834  			Expect(err).ToNot(HaveOccurred())
   835  			Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
   836  		}()
   837  		conn, err := quic.DialAddrEarly(
   838  			context.Background(),
   839  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   840  			clientTLSConf,
   841  			getQuicConfig(&quic.Config{
   842  				EnableDatagrams: true,
   843  			}),
   844  		)
   845  		Expect(err).ToNot(HaveOccurred())
   846  		Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
   847  		Expect(conn.SendMessage(sentMessage)).To(Succeed())
   848  		<-conn.HandshakeComplete()
   849  		<-received
   850  
   851  		Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
   852  		Expect(receivedMessage).To(Equal(sentMessage))
   853  		Expect(conn.CloseWithError(0, "")).To(Succeed())
   854  		num0RTT := atomic.LoadUint32(num0RTTPackets)
   855  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   856  		Expect(num0RTT).ToNot(BeZero())
   857  		zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
   858  		Expect(zeroRTTPackets).To(HaveLen(1))
   859  	})
   860  
   861  	It("rejects 0-RTT datagrams when the server doesn't support datagrams anymore", func() {
   862  		tlsConf, clientTLSConf := dialAndReceiveSessionTicket(getQuicConfig(&quic.Config{
   863  			EnableDatagrams: true,
   864  		}))
   865  
   866  		counter, tracer := newPacketTracer()
   867  		ln, err := quic.ListenAddrEarly(
   868  			"localhost:0",
   869  			tlsConf,
   870  			getQuicConfig(&quic.Config{
   871  				Allow0RTT:       true,
   872  				EnableDatagrams: false,
   873  				Tracer:          newTracer(tracer),
   874  			}),
   875  		)
   876  		Expect(err).ToNot(HaveOccurred())
   877  		defer ln.Close()
   878  
   879  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   880  		defer proxy.Close()
   881  
   882  		// second connection
   883  		go func() {
   884  			defer GinkgoRecover()
   885  			conn, err := ln.Accept(context.Background())
   886  			Expect(err).ToNot(HaveOccurred())
   887  			_, err = conn.ReceiveMessage(context.Background())
   888  			Expect(err.Error()).To(Equal("datagram support disabled"))
   889  			<-conn.HandshakeComplete()
   890  			Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
   891  		}()
   892  		conn, err := quic.DialAddrEarly(
   893  			context.Background(),
   894  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   895  			clientTLSConf,
   896  			getQuicConfig(&quic.Config{
   897  				EnableDatagrams: true,
   898  			}),
   899  		)
   900  		Expect(err).ToNot(HaveOccurred())
   901  		// the client can temporarily send datagrams but the server doesn't process them.
   902  		Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
   903  		Expect(conn.SendMessage(make([]byte, 100))).To(Succeed())
   904  		<-conn.HandshakeComplete()
   905  
   906  		Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse())
   907  		Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
   908  		Expect(conn.CloseWithError(0, "")).To(Succeed())
   909  		num0RTT := atomic.LoadUint32(num0RTTPackets)
   910  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   911  		Expect(num0RTT).ToNot(BeZero())
   912  		Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
   913  	})
   914  })