github.com/danielpfeifer02/quic-go-prio-packs@v0.41.0-28/integrationtests/self/zero_rtt_test.go (about)

     1  package self_test
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"sync"
    10  	"sync/atomic"
    11  	"time"
    12  
    13  	"github.com/danielpfeifer02/quic-go-prio-packs"
    14  	quicproxy "github.com/danielpfeifer02/quic-go-prio-packs/integrationtests/tools/proxy"
    15  	"github.com/danielpfeifer02/quic-go-prio-packs/internal/protocol"
    16  	"github.com/danielpfeifer02/quic-go-prio-packs/internal/wire"
    17  	"github.com/danielpfeifer02/quic-go-prio-packs/logging"
    18  
    19  	. "github.com/onsi/ginkgo/v2"
    20  	. "github.com/onsi/gomega"
    21  )
    22  
    23  type metadataClientSessionCache struct {
    24  	toAdd    []byte
    25  	restored func([]byte)
    26  
    27  	cache tls.ClientSessionCache
    28  }
    29  
    30  func (m metadataClientSessionCache) Get(key string) (*tls.ClientSessionState, bool) {
    31  	session, ok := m.cache.Get(key)
    32  	if !ok || session == nil {
    33  		return session, ok
    34  	}
    35  	ticket, state, err := session.ResumptionState()
    36  	Expect(err).ToNot(HaveOccurred())
    37  	Expect(state.Extra).To(HaveLen(2)) // ours, and the quic-go's
    38  	m.restored(state.Extra[1])
    39  	session, err = tls.NewResumptionState(ticket, state)
    40  	Expect(err).ToNot(HaveOccurred())
    41  	return session, true
    42  }
    43  
    44  func (m metadataClientSessionCache) Put(key string, session *tls.ClientSessionState) {
    45  	ticket, state, err := session.ResumptionState()
    46  	Expect(err).ToNot(HaveOccurred())
    47  	state.Extra = append(state.Extra, m.toAdd)
    48  	session, err = tls.NewResumptionState(ticket, state)
    49  	Expect(err).ToNot(HaveOccurred())
    50  	m.cache.Put(key, session)
    51  }
    52  
    53  var _ = Describe("0-RTT", func() {
    54  	rtt := scaleDuration(5 * time.Millisecond)
    55  
    56  	runCountingProxy := func(serverPort int) (*quicproxy.QuicProxy, *atomic.Uint32) {
    57  		var num0RTTPackets atomic.Uint32
    58  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
    59  			RemoteAddr: fmt.Sprintf("localhost:%d", serverPort),
    60  			DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration {
    61  				for len(data) > 0 {
    62  					if !wire.IsLongHeaderPacket(data[0]) {
    63  						break
    64  					}
    65  					hdr, _, rest, err := wire.ParsePacket(data)
    66  					Expect(err).ToNot(HaveOccurred())
    67  					if hdr.Type == protocol.PacketType0RTT {
    68  						num0RTTPackets.Add(1)
    69  						break
    70  					}
    71  					data = rest
    72  				}
    73  				return rtt / 2
    74  			},
    75  		})
    76  		Expect(err).ToNot(HaveOccurred())
    77  
    78  		return proxy, &num0RTTPackets
    79  	}
    80  
    81  	dialAndReceiveSessionTicket := func(serverTLSConf *tls.Config, serverConf *quic.Config, clientTLSConf *tls.Config) {
    82  		if serverConf == nil {
    83  			serverConf = getQuicConfig(nil)
    84  		}
    85  		serverConf.Allow0RTT = true
    86  		ln, err := quic.ListenAddrEarly(
    87  			"localhost:0",
    88  			serverTLSConf,
    89  			serverConf,
    90  		)
    91  		Expect(err).ToNot(HaveOccurred())
    92  		defer ln.Close()
    93  
    94  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
    95  			RemoteAddr:  fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port),
    96  			DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { return rtt / 2 },
    97  		})
    98  		Expect(err).ToNot(HaveOccurred())
    99  		defer proxy.Close()
   100  
   101  		// dial the first connection in order to receive a session ticket
   102  		done := make(chan struct{})
   103  		go func() {
   104  			defer GinkgoRecover()
   105  			defer close(done)
   106  			conn, err := ln.Accept(context.Background())
   107  			Expect(err).ToNot(HaveOccurred())
   108  			<-conn.Context().Done()
   109  		}()
   110  
   111  		puts := make(chan string, 100)
   112  		cache := clientTLSConf.ClientSessionCache
   113  		if cache == nil {
   114  			cache = tls.NewLRUClientSessionCache(100)
   115  		}
   116  		clientTLSConf.ClientSessionCache = newClientSessionCache(cache, make(chan string, 100), puts)
   117  		conn, err := quic.DialAddr(
   118  			context.Background(),
   119  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   120  			clientTLSConf,
   121  			getQuicConfig(nil),
   122  		)
   123  		Expect(err).ToNot(HaveOccurred())
   124  		Eventually(puts).Should(Receive())
   125  		// received the session ticket. We're done here.
   126  		Expect(conn.CloseWithError(0, "")).To(Succeed())
   127  		Eventually(done).Should(BeClosed())
   128  	}
   129  
   130  	transfer0RTTData := func(
   131  		ln *quic.EarlyListener,
   132  		proxyPort int,
   133  		connIDLen int,
   134  		clientTLSConf *tls.Config,
   135  		clientConf *quic.Config,
   136  		testdata []byte, // data to transfer
   137  	) {
   138  		// accept the second connection, and receive the data sent in 0-RTT
   139  		done := make(chan struct{})
   140  		go func() {
   141  			defer GinkgoRecover()
   142  			conn, err := ln.Accept(context.Background())
   143  			Expect(err).ToNot(HaveOccurred())
   144  			str, err := conn.AcceptStream(context.Background())
   145  			Expect(err).ToNot(HaveOccurred())
   146  			data, err := io.ReadAll(str)
   147  			Expect(err).ToNot(HaveOccurred())
   148  			Expect(data).To(Equal(testdata))
   149  			Expect(str.Close()).To(Succeed())
   150  			Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
   151  			<-conn.Context().Done()
   152  			close(done)
   153  		}()
   154  
   155  		if clientConf == nil {
   156  			clientConf = getQuicConfig(nil)
   157  		}
   158  		var conn quic.EarlyConnection
   159  		if connIDLen == 0 {
   160  			var err error
   161  			conn, err = quic.DialAddrEarly(
   162  				context.Background(),
   163  				fmt.Sprintf("localhost:%d", proxyPort),
   164  				clientTLSConf,
   165  				clientConf,
   166  			)
   167  			Expect(err).ToNot(HaveOccurred())
   168  		} else {
   169  			addr, err := net.ResolveUDPAddr("udp", "localhost:0")
   170  			Expect(err).ToNot(HaveOccurred())
   171  			udpConn, err := net.ListenUDP("udp", addr)
   172  			Expect(err).ToNot(HaveOccurred())
   173  			defer udpConn.Close()
   174  			tr := &quic.Transport{
   175  				Conn:               udpConn,
   176  				ConnectionIDLength: connIDLen,
   177  			}
   178  			defer tr.Close()
   179  			conn, err = tr.DialEarly(
   180  				context.Background(),
   181  				&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: proxyPort},
   182  				clientTLSConf,
   183  				clientConf,
   184  			)
   185  			Expect(err).ToNot(HaveOccurred())
   186  		}
   187  		defer conn.CloseWithError(0, "")
   188  		str, err := conn.OpenStream()
   189  		Expect(err).ToNot(HaveOccurred())
   190  		_, err = str.Write(testdata)
   191  		Expect(err).ToNot(HaveOccurred())
   192  		Expect(str.Close()).To(Succeed())
   193  		<-conn.HandshakeComplete()
   194  		Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
   195  		io.ReadAll(str) // wait for the EOF from the server to arrive before closing the conn
   196  		conn.CloseWithError(0, "")
   197  		Eventually(done).Should(BeClosed())
   198  		Eventually(conn.Context().Done()).Should(BeClosed())
   199  	}
   200  
   201  	check0RTTRejected := func(
   202  		ln *quic.EarlyListener,
   203  		proxyPort int,
   204  		clientConf *tls.Config,
   205  	) {
   206  		conn, err := quic.DialAddrEarly(
   207  			context.Background(),
   208  			fmt.Sprintf("localhost:%d", proxyPort),
   209  			clientConf,
   210  			getQuicConfig(nil),
   211  		)
   212  		Expect(err).ToNot(HaveOccurred())
   213  		str, err := conn.OpenUniStream()
   214  		Expect(err).ToNot(HaveOccurred())
   215  		_, err = str.Write(make([]byte, 3000))
   216  		Expect(err).ToNot(HaveOccurred())
   217  		Expect(str.Close()).To(Succeed())
   218  		Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
   219  
   220  		// make sure the server doesn't process the data
   221  		ctx, cancel := context.WithTimeout(context.Background(), scaleDuration(50*time.Millisecond))
   222  		defer cancel()
   223  		serverConn, err := ln.Accept(ctx)
   224  		Expect(err).ToNot(HaveOccurred())
   225  		Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse())
   226  		_, err = serverConn.AcceptUniStream(ctx)
   227  		Expect(err).To(Equal(context.DeadlineExceeded))
   228  		Expect(serverConn.CloseWithError(0, "")).To(Succeed())
   229  		Eventually(conn.Context().Done()).Should(BeClosed())
   230  	}
   231  
   232  	// can be used to extract 0-RTT from a packetCounter
   233  	get0RTTPackets := func(packets []packet) []protocol.PacketNumber {
   234  		var zeroRTTPackets []protocol.PacketNumber
   235  		for _, p := range packets {
   236  			if p.hdr.Type == protocol.PacketType0RTT {
   237  				zeroRTTPackets = append(zeroRTTPackets, p.hdr.PacketNumber)
   238  			}
   239  		}
   240  		return zeroRTTPackets
   241  	}
   242  
   243  	for _, l := range []int{0, 15} {
   244  		connIDLen := l
   245  
   246  		It(fmt.Sprintf("transfers 0-RTT data, with %d byte connection IDs", connIDLen), func() {
   247  			tlsConf := getTLSConfig()
   248  			clientTLSConf := getTLSClientConfig()
   249  			dialAndReceiveSessionTicket(tlsConf, nil, clientTLSConf)
   250  
   251  			counter, tracer := newPacketTracer()
   252  			ln, err := quic.ListenAddrEarly(
   253  				"localhost:0",
   254  				tlsConf,
   255  				getQuicConfig(&quic.Config{
   256  					Allow0RTT: true,
   257  					Tracer:    newTracer(tracer),
   258  				}),
   259  			)
   260  			Expect(err).ToNot(HaveOccurred())
   261  			defer ln.Close()
   262  
   263  			proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   264  			defer proxy.Close()
   265  
   266  			transfer0RTTData(
   267  				ln,
   268  				proxy.LocalPort(),
   269  				connIDLen,
   270  				clientTLSConf,
   271  				getQuicConfig(nil),
   272  				PRData,
   273  			)
   274  
   275  			var numNewConnIDs int
   276  			for _, p := range counter.getRcvdLongHeaderPackets() {
   277  				for _, f := range p.frames {
   278  					if _, ok := f.(*logging.NewConnectionIDFrame); ok {
   279  						numNewConnIDs++
   280  					}
   281  				}
   282  			}
   283  			if connIDLen == 0 {
   284  				Expect(numNewConnIDs).To(BeZero())
   285  			} else {
   286  				Expect(numNewConnIDs).ToNot(BeZero())
   287  			}
   288  
   289  			num0RTT := num0RTTPackets.Load()
   290  			fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   291  			Expect(num0RTT).ToNot(BeZero())
   292  			zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
   293  			Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10))
   294  			Expect(zeroRTTPackets).To(ContainElement(protocol.PacketNumber(0)))
   295  		})
   296  	}
   297  
   298  	// Test that data intended to be sent with 1-RTT protection is not sent in 0-RTT packets.
   299  	It("waits for a connection until the handshake is done", func() {
   300  		tlsConf := getTLSConfig()
   301  		clientConf := getTLSClientConfig()
   302  		dialAndReceiveSessionTicket(tlsConf, nil, clientConf)
   303  
   304  		zeroRTTData := GeneratePRData(5 << 10)
   305  		oneRTTData := PRData
   306  
   307  		counter, tracer := newPacketTracer()
   308  		ln, err := quic.ListenAddrEarly(
   309  			"localhost:0",
   310  			tlsConf,
   311  			getQuicConfig(&quic.Config{
   312  				Allow0RTT: true,
   313  				Tracer:    newTracer(tracer),
   314  			}),
   315  		)
   316  		Expect(err).ToNot(HaveOccurred())
   317  		defer ln.Close()
   318  
   319  		// now accept the second connection, and receive the 0-RTT data
   320  		go func() {
   321  			defer GinkgoRecover()
   322  			conn, err := ln.Accept(context.Background())
   323  			Expect(err).ToNot(HaveOccurred())
   324  			str, err := conn.AcceptUniStream(context.Background())
   325  			Expect(err).ToNot(HaveOccurred())
   326  			data, err := io.ReadAll(str)
   327  			Expect(err).ToNot(HaveOccurred())
   328  			Expect(data).To(Equal(zeroRTTData))
   329  			str, err = conn.AcceptUniStream(context.Background())
   330  			Expect(err).ToNot(HaveOccurred())
   331  			data, err = io.ReadAll(str)
   332  			Expect(err).ToNot(HaveOccurred())
   333  			Expect(data).To(Equal(oneRTTData))
   334  			Expect(conn.CloseWithError(0, "")).To(Succeed())
   335  		}()
   336  
   337  		proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   338  		defer proxy.Close()
   339  
   340  		conn, err := quic.DialAddrEarly(
   341  			context.Background(),
   342  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   343  			clientConf,
   344  			getQuicConfig(nil),
   345  		)
   346  		Expect(err).ToNot(HaveOccurred())
   347  		firstStr, err := conn.OpenUniStream()
   348  		Expect(err).ToNot(HaveOccurred())
   349  		_, err = firstStr.Write(zeroRTTData)
   350  		Expect(err).ToNot(HaveOccurred())
   351  		Expect(firstStr.Close()).To(Succeed())
   352  
   353  		// wait for the handshake to complete
   354  		Eventually(conn.HandshakeComplete()).Should(BeClosed())
   355  		str, err := conn.OpenUniStream()
   356  		Expect(err).ToNot(HaveOccurred())
   357  		_, err = str.Write(PRData)
   358  		Expect(err).ToNot(HaveOccurred())
   359  		Expect(str.Close()).To(Succeed())
   360  		<-conn.Context().Done()
   361  
   362  		// check that 0-RTT packets only contain STREAM frames for the first stream
   363  		var num0RTT int
   364  		for _, p := range counter.getRcvdLongHeaderPackets() {
   365  			if p.hdr.Header.Type != protocol.PacketType0RTT {
   366  				continue
   367  			}
   368  			for _, f := range p.frames {
   369  				sf, ok := f.(*logging.StreamFrame)
   370  				if !ok {
   371  					continue
   372  				}
   373  				num0RTT++
   374  				Expect(sf.StreamID).To(Equal(firstStr.StreamID()))
   375  			}
   376  		}
   377  		fmt.Fprintf(GinkgoWriter, "received %d STREAM frames in 0-RTT packets\n", num0RTT)
   378  		Expect(num0RTT).ToNot(BeZero())
   379  	})
   380  
   381  	It("transfers 0-RTT data, when 0-RTT packets are lost", func() {
   382  		var num0RTTPackets, numDropped atomic.Uint32
   383  
   384  		tlsConf := getTLSConfig()
   385  		clientConf := getTLSClientConfig()
   386  		dialAndReceiveSessionTicket(tlsConf, nil, clientConf)
   387  
   388  		counter, tracer := newPacketTracer()
   389  		ln, err := quic.ListenAddrEarly(
   390  			"localhost:0",
   391  			tlsConf,
   392  			getQuicConfig(&quic.Config{
   393  				Allow0RTT: true,
   394  				Tracer:    newTracer(tracer),
   395  			}),
   396  		)
   397  		Expect(err).ToNot(HaveOccurred())
   398  		defer ln.Close()
   399  
   400  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
   401  			RemoteAddr:  fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port),
   402  			DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { return rtt / 2 },
   403  			DropPacket: func(_ quicproxy.Direction, data []byte) bool {
   404  				if !wire.IsLongHeaderPacket(data[0]) {
   405  					return false
   406  				}
   407  				hdr, _, _, err := wire.ParsePacket(data)
   408  				Expect(err).ToNot(HaveOccurred())
   409  				if hdr.Type == protocol.PacketType0RTT {
   410  					count := num0RTTPackets.Add(1)
   411  					// drop 25% of the 0-RTT packets
   412  					drop := count%4 == 0
   413  					if drop {
   414  						numDropped.Add(1)
   415  					}
   416  					return drop
   417  				}
   418  				return false
   419  			},
   420  		})
   421  		Expect(err).ToNot(HaveOccurred())
   422  		defer proxy.Close()
   423  
   424  		transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData)
   425  
   426  		num0RTT := num0RTTPackets.Load()
   427  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets. Dropped %d of those.", num0RTT, numDropped.Load())
   428  		Expect(numDropped.Load()).ToNot(BeZero())
   429  		Expect(num0RTT).ToNot(BeZero())
   430  		Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).ToNot(BeEmpty())
   431  	})
   432  
   433  	It("retransmits all 0-RTT data when the server performs a Retry", func() {
   434  		var mutex sync.Mutex
   435  		var firstConnID, secondConnID *protocol.ConnectionID
   436  		var firstCounter, secondCounter protocol.ByteCount
   437  
   438  		tlsConf := getTLSConfig()
   439  		clientConf := getTLSClientConfig()
   440  		dialAndReceiveSessionTicket(tlsConf, nil, clientConf)
   441  
   442  		countZeroRTTBytes := func(data []byte) (n protocol.ByteCount) {
   443  			for len(data) > 0 {
   444  				hdr, _, rest, err := wire.ParsePacket(data)
   445  				if err != nil {
   446  					return
   447  				}
   448  				data = rest
   449  				if hdr.Type == protocol.PacketType0RTT {
   450  					n += hdr.Length - 16 /* AEAD tag */
   451  				}
   452  			}
   453  			return
   454  		}
   455  
   456  		counter, tracer := newPacketTracer()
   457  		laddr, err := net.ResolveUDPAddr("udp", "localhost:0")
   458  		Expect(err).ToNot(HaveOccurred())
   459  		udpConn, err := net.ListenUDP("udp", laddr)
   460  		Expect(err).ToNot(HaveOccurred())
   461  		defer udpConn.Close()
   462  		tr := &quic.Transport{
   463  			Conn:                     udpConn,
   464  			MaxUnvalidatedHandshakes: -1,
   465  		}
   466  		defer tr.Close()
   467  		ln, err := tr.ListenEarly(
   468  			tlsConf,
   469  			getQuicConfig(&quic.Config{Allow0RTT: true, Tracer: newTracer(tracer)}),
   470  		)
   471  		Expect(err).ToNot(HaveOccurred())
   472  		defer ln.Close()
   473  
   474  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
   475  			RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port),
   476  			DelayPacket: func(dir quicproxy.Direction, data []byte) time.Duration {
   477  				connID, err := wire.ParseConnectionID(data, 0)
   478  				Expect(err).ToNot(HaveOccurred())
   479  
   480  				mutex.Lock()
   481  				defer mutex.Unlock()
   482  
   483  				if zeroRTTBytes := countZeroRTTBytes(data); zeroRTTBytes > 0 {
   484  					if firstConnID == nil {
   485  						firstConnID = &connID
   486  						firstCounter += zeroRTTBytes
   487  					} else if firstConnID != nil && *firstConnID == connID {
   488  						Expect(secondConnID).To(BeNil())
   489  						firstCounter += zeroRTTBytes
   490  					} else if secondConnID == nil {
   491  						secondConnID = &connID
   492  						secondCounter += zeroRTTBytes
   493  					} else if secondConnID != nil && *secondConnID == connID {
   494  						secondCounter += zeroRTTBytes
   495  					} else {
   496  						Fail("received 3 connection IDs on 0-RTT packets")
   497  					}
   498  				}
   499  				return rtt / 2
   500  			},
   501  		})
   502  		Expect(err).ToNot(HaveOccurred())
   503  		defer proxy.Close()
   504  
   505  		transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, GeneratePRData(5000)) // ~5 packets
   506  
   507  		mutex.Lock()
   508  		defer mutex.Unlock()
   509  		Expect(firstCounter).To(BeNumerically("~", 5000+100 /* framing overhead */, 100)) // the FIN bit might be sent extra
   510  		Expect(secondCounter).To(BeNumerically("~", firstCounter, 20))
   511  		zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
   512  		Expect(len(zeroRTTPackets)).To(BeNumerically(">=", 5))
   513  		Expect(zeroRTTPackets[0]).To(BeNumerically(">=", protocol.PacketNumber(5)))
   514  	})
   515  
   516  	It("doesn't use 0-RTT when Dial is used for the resumed connection", func() {
   517  		tlsConf := getTLSConfig()
   518  		clientConf := getTLSClientConfig()
   519  		dialAndReceiveSessionTicket(tlsConf, getQuicConfig(nil), clientConf)
   520  
   521  		ln, err := quic.ListenAddrEarly(
   522  			"localhost:0",
   523  			tlsConf,
   524  			getQuicConfig(&quic.Config{Allow0RTT: true}),
   525  		)
   526  		Expect(err).ToNot(HaveOccurred())
   527  		defer ln.Close()
   528  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   529  		defer proxy.Close()
   530  
   531  		conn, err := quic.DialAddr(
   532  			context.Background(),
   533  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   534  			clientConf,
   535  			getQuicConfig(nil),
   536  		)
   537  		Expect(err).ToNot(HaveOccurred())
   538  		defer conn.CloseWithError(0, "")
   539  		Expect(conn.ConnectionState().TLS.DidResume).To(BeTrue())
   540  		Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
   541  		Expect(num0RTTPackets.Load()).To(BeZero())
   542  
   543  		serverConn, err := ln.Accept(context.Background())
   544  		Expect(err).ToNot(HaveOccurred())
   545  		Expect(serverConn.ConnectionState().TLS.DidResume).To(BeTrue())
   546  		Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse())
   547  	})
   548  
   549  	It("doesn't reject 0-RTT when the server's transport stream limit increased", func() {
   550  		const maxStreams = 1
   551  		tlsConf := getTLSConfig()
   552  		clientConf := getTLSClientConfig()
   553  		dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{
   554  			MaxIncomingUniStreams: maxStreams,
   555  		}), clientConf)
   556  
   557  		ln, err := quic.ListenAddrEarly(
   558  			"localhost:0",
   559  			tlsConf,
   560  			getQuicConfig(&quic.Config{
   561  				MaxIncomingUniStreams: maxStreams + 1,
   562  				Allow0RTT:             true,
   563  			}),
   564  		)
   565  		Expect(err).ToNot(HaveOccurred())
   566  		defer ln.Close()
   567  		proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   568  		defer proxy.Close()
   569  
   570  		conn, err := quic.DialAddrEarly(
   571  			context.Background(),
   572  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   573  			clientConf,
   574  			getQuicConfig(nil),
   575  		)
   576  		Expect(err).ToNot(HaveOccurred())
   577  		str, err := conn.OpenUniStream()
   578  		Expect(err).ToNot(HaveOccurred())
   579  		_, err = str.Write([]byte("foobar"))
   580  		Expect(err).ToNot(HaveOccurred())
   581  		Expect(str.Close()).To(Succeed())
   582  		// The client remembers the old limit and refuses to open a new stream.
   583  		_, err = conn.OpenUniStream()
   584  		Expect(err).To(HaveOccurred())
   585  		Expect(err.Error()).To(ContainSubstring("too many open streams"))
   586  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   587  		defer cancel()
   588  		_, err = conn.OpenUniStreamSync(ctx)
   589  		Expect(err).ToNot(HaveOccurred())
   590  		Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
   591  		Expect(conn.CloseWithError(0, "")).To(Succeed())
   592  	})
   593  
   594  	It("rejects 0-RTT when the server's stream limit decreased", func() {
   595  		const maxStreams = 42
   596  		tlsConf := getTLSConfig()
   597  		clientConf := getTLSClientConfig()
   598  		dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{
   599  			MaxIncomingStreams: maxStreams,
   600  		}), clientConf)
   601  
   602  		counter, tracer := newPacketTracer()
   603  		ln, err := quic.ListenAddrEarly(
   604  			"localhost:0",
   605  			tlsConf,
   606  			getQuicConfig(&quic.Config{
   607  				MaxIncomingStreams: maxStreams - 1,
   608  				Allow0RTT:          true,
   609  				Tracer:             newTracer(tracer),
   610  			}),
   611  		)
   612  		Expect(err).ToNot(HaveOccurred())
   613  		defer ln.Close()
   614  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   615  		defer proxy.Close()
   616  
   617  		check0RTTRejected(ln, proxy.LocalPort(), clientConf)
   618  
   619  		// The client should send 0-RTT packets, but the server doesn't process them.
   620  		num0RTT := num0RTTPackets.Load()
   621  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   622  		Expect(num0RTT).ToNot(BeZero())
   623  		Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
   624  	})
   625  
   626  	It("rejects 0-RTT when the ALPN changed", func() {
   627  		tlsConf := getTLSConfig()
   628  		clientConf := getTLSClientConfig()
   629  		dialAndReceiveSessionTicket(tlsConf, nil, clientConf)
   630  
   631  		// switch to different ALPN on the server side
   632  		tlsConf.NextProtos = []string{"new-alpn"}
   633  		// Append to the client's ALPN.
   634  		// crypto/tls will attempt to resume with the ALPN from the original connection
   635  		clientConf.NextProtos = append(clientConf.NextProtos, "new-alpn")
   636  		counter, tracer := newPacketTracer()
   637  		ln, err := quic.ListenAddrEarly(
   638  			"localhost:0",
   639  			tlsConf,
   640  			getQuicConfig(&quic.Config{
   641  				Allow0RTT: true,
   642  				Tracer:    newTracer(tracer),
   643  			}),
   644  		)
   645  		Expect(err).ToNot(HaveOccurred())
   646  		defer ln.Close()
   647  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   648  		defer proxy.Close()
   649  
   650  		check0RTTRejected(ln, proxy.LocalPort(), clientConf)
   651  
   652  		// The client should send 0-RTT packets, but the server doesn't process them.
   653  		num0RTT := num0RTTPackets.Load()
   654  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   655  		Expect(num0RTT).ToNot(BeZero())
   656  		Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
   657  	})
   658  
   659  	It("rejects 0-RTT when the application doesn't allow it", func() {
   660  		tlsConf := getTLSConfig()
   661  		clientConf := getTLSClientConfig()
   662  		dialAndReceiveSessionTicket(tlsConf, nil, clientConf)
   663  
   664  		// now close the listener and dial new connection with a different ALPN
   665  		counter, tracer := newPacketTracer()
   666  		ln, err := quic.ListenAddrEarly(
   667  			"localhost:0",
   668  			tlsConf,
   669  			getQuicConfig(&quic.Config{
   670  				Allow0RTT: false, // application rejects 0-RTT
   671  				Tracer:    newTracer(tracer),
   672  			}),
   673  		)
   674  		Expect(err).ToNot(HaveOccurred())
   675  		defer ln.Close()
   676  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   677  		defer proxy.Close()
   678  
   679  		check0RTTRejected(ln, proxy.LocalPort(), clientConf)
   680  
   681  		// The client should send 0-RTT packets, but the server doesn't process them.
   682  		num0RTT := num0RTTPackets.Load()
   683  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   684  		Expect(num0RTT).ToNot(BeZero())
   685  		Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
   686  	})
   687  
   688  	It("doesn't use 0-RTT, if the server didn't enable it", func() {
   689  		server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil))
   690  		Expect(err).ToNot(HaveOccurred())
   691  		defer server.Close()
   692  
   693  		gets := make(chan string, 100)
   694  		puts := make(chan string, 100)
   695  		cache := newClientSessionCache(tls.NewLRUClientSessionCache(10), gets, puts)
   696  		tlsConf := getTLSClientConfig()
   697  		tlsConf.ClientSessionCache = cache
   698  		conn1, err := quic.DialAddr(
   699  			context.Background(),
   700  			fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
   701  			tlsConf,
   702  			getQuicConfig(nil),
   703  		)
   704  		Expect(err).ToNot(HaveOccurred())
   705  		defer conn1.CloseWithError(0, "")
   706  		var sessionKey string
   707  		Eventually(puts).Should(Receive(&sessionKey))
   708  		Expect(conn1.ConnectionState().TLS.DidResume).To(BeFalse())
   709  
   710  		serverConn, err := server.Accept(context.Background())
   711  		Expect(err).ToNot(HaveOccurred())
   712  		Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse())
   713  
   714  		conn2, err := quic.DialAddrEarly(
   715  			context.Background(),
   716  			fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
   717  			tlsConf,
   718  			getQuicConfig(nil),
   719  		)
   720  		Expect(err).ToNot(HaveOccurred())
   721  		Expect(gets).To(Receive(Equal(sessionKey)))
   722  		Expect(conn2.ConnectionState().TLS.DidResume).To(BeTrue())
   723  
   724  		serverConn, err = server.Accept(context.Background())
   725  		Expect(err).ToNot(HaveOccurred())
   726  		Expect(serverConn.ConnectionState().TLS.DidResume).To(BeTrue())
   727  		Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse())
   728  		conn2.CloseWithError(0, "")
   729  	})
   730  
   731  	DescribeTable("flow control limits",
   732  		func(addFlowControlLimit func(*quic.Config, uint64)) {
   733  			counter, tracer := newPacketTracer()
   734  			firstConf := getQuicConfig(&quic.Config{Allow0RTT: true})
   735  			addFlowControlLimit(firstConf, 3)
   736  			tlsConf := getTLSConfig()
   737  			clientConf := getTLSClientConfig()
   738  			dialAndReceiveSessionTicket(tlsConf, firstConf, clientConf)
   739  
   740  			secondConf := getQuicConfig(&quic.Config{
   741  				Allow0RTT: true,
   742  				Tracer:    newTracer(tracer),
   743  			})
   744  			addFlowControlLimit(secondConf, 100)
   745  			ln, err := quic.ListenAddrEarly(
   746  				"localhost:0",
   747  				tlsConf,
   748  				secondConf,
   749  			)
   750  			Expect(err).ToNot(HaveOccurred())
   751  			defer ln.Close()
   752  			proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   753  			defer proxy.Close()
   754  
   755  			conn, err := quic.DialAddrEarly(
   756  				context.Background(),
   757  				fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   758  				clientConf,
   759  				getQuicConfig(nil),
   760  			)
   761  			Expect(err).ToNot(HaveOccurred())
   762  			str, err := conn.OpenUniStream()
   763  			Expect(err).ToNot(HaveOccurred())
   764  			written := make(chan struct{})
   765  			go func() {
   766  				defer GinkgoRecover()
   767  				defer close(written)
   768  				_, err := str.Write([]byte("foobar"))
   769  				Expect(err).ToNot(HaveOccurred())
   770  				Expect(str.Close()).To(Succeed())
   771  			}()
   772  
   773  			Eventually(written).Should(BeClosed())
   774  
   775  			serverConn, err := ln.Accept(context.Background())
   776  			Expect(err).ToNot(HaveOccurred())
   777  			rstr, err := serverConn.AcceptUniStream(context.Background())
   778  			Expect(err).ToNot(HaveOccurred())
   779  			data, err := io.ReadAll(rstr)
   780  			Expect(err).ToNot(HaveOccurred())
   781  			Expect(data).To(Equal([]byte("foobar")))
   782  			Expect(serverConn.ConnectionState().Used0RTT).To(BeTrue())
   783  			Expect(serverConn.CloseWithError(0, "")).To(Succeed())
   784  			Eventually(conn.Context().Done()).Should(BeClosed())
   785  
   786  			var processedFirst bool
   787  			for _, p := range counter.getRcvdLongHeaderPackets() {
   788  				for _, f := range p.frames {
   789  					if sf, ok := f.(*logging.StreamFrame); ok {
   790  						if !processedFirst {
   791  							// The first STREAM should have been sent in a 0-RTT packet.
   792  							// Due to the flow control limit, the STREAM frame was limit to the first 3 bytes.
   793  							Expect(p.hdr.Type).To(Equal(protocol.PacketType0RTT))
   794  							Expect(sf.Length).To(BeEquivalentTo(3))
   795  							processedFirst = true
   796  						} else {
   797  							Fail("STREAM was shouldn't have been sent in 0-RTT")
   798  						}
   799  					}
   800  				}
   801  			}
   802  		},
   803  		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 }),
   804  		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 }),
   805  	)
   806  
   807  	for _, l := range []int{0, 15} {
   808  		connIDLen := l
   809  
   810  		It(fmt.Sprintf("correctly deals with 0-RTT rejections, for %d byte connection IDs", connIDLen), func() {
   811  			tlsConf := getTLSConfig()
   812  			clientConf := getTLSClientConfig()
   813  			dialAndReceiveSessionTicket(tlsConf, nil, clientConf)
   814  			// now dial new connection with different transport parameters
   815  			counter, tracer := newPacketTracer()
   816  			ln, err := quic.ListenAddrEarly(
   817  				"localhost:0",
   818  				tlsConf,
   819  				getQuicConfig(&quic.Config{
   820  					MaxIncomingUniStreams: 1,
   821  					Tracer:                newTracer(tracer),
   822  				}),
   823  			)
   824  			Expect(err).ToNot(HaveOccurred())
   825  			defer ln.Close()
   826  			proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
   827  			defer proxy.Close()
   828  
   829  			conn, err := quic.DialAddrEarly(
   830  				context.Background(),
   831  				fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   832  				clientConf,
   833  				getQuicConfig(nil),
   834  			)
   835  			Expect(err).ToNot(HaveOccurred())
   836  			// The client remembers that it was allowed to open 2 uni-directional streams.
   837  			firstStr, err := conn.OpenUniStream()
   838  			Expect(err).ToNot(HaveOccurred())
   839  			written := make(chan struct{}, 2)
   840  			go func() {
   841  				defer GinkgoRecover()
   842  				defer func() { written <- struct{}{} }()
   843  				_, err := firstStr.Write([]byte("first flight"))
   844  				Expect(err).ToNot(HaveOccurred())
   845  			}()
   846  			secondStr, err := conn.OpenUniStream()
   847  			Expect(err).ToNot(HaveOccurred())
   848  			go func() {
   849  				defer GinkgoRecover()
   850  				defer func() { written <- struct{}{} }()
   851  				_, err := secondStr.Write([]byte("first flight"))
   852  				Expect(err).ToNot(HaveOccurred())
   853  			}()
   854  
   855  			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   856  			defer cancel()
   857  			_, err = conn.AcceptStream(ctx)
   858  			Expect(err).To(MatchError(quic.Err0RTTRejected))
   859  			Eventually(written).Should(Receive())
   860  			Eventually(written).Should(Receive())
   861  			_, err = firstStr.Write([]byte("foobar"))
   862  			Expect(err).To(MatchError(quic.Err0RTTRejected))
   863  			_, err = conn.OpenUniStream()
   864  			Expect(err).To(MatchError(quic.Err0RTTRejected))
   865  
   866  			_, err = conn.AcceptStream(ctx)
   867  			Expect(err).To(Equal(quic.Err0RTTRejected))
   868  
   869  			newConn := conn.NextConnection()
   870  			str, err := newConn.OpenUniStream()
   871  			Expect(err).ToNot(HaveOccurred())
   872  			_, err = newConn.OpenUniStream()
   873  			Expect(err).To(HaveOccurred())
   874  			Expect(err.Error()).To(ContainSubstring("too many open streams"))
   875  			_, err = str.Write([]byte("second flight"))
   876  			Expect(err).ToNot(HaveOccurred())
   877  			Expect(str.Close()).To(Succeed())
   878  			Expect(conn.CloseWithError(0, "")).To(Succeed())
   879  
   880  			// The client should send 0-RTT packets, but the server doesn't process them.
   881  			num0RTT := num0RTTPackets.Load()
   882  			fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
   883  			Expect(num0RTT).ToNot(BeZero())
   884  			Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
   885  		})
   886  	}
   887  
   888  	It("queues 0-RTT packets, if the Initial is delayed", func() {
   889  		tlsConf := getTLSConfig()
   890  		clientConf := getTLSClientConfig()
   891  		dialAndReceiveSessionTicket(tlsConf, nil, clientConf)
   892  
   893  		counter, tracer := newPacketTracer()
   894  		ln, err := quic.ListenAddrEarly(
   895  			"localhost:0",
   896  			tlsConf,
   897  			getQuicConfig(&quic.Config{
   898  				Allow0RTT: true,
   899  				Tracer:    newTracer(tracer),
   900  			}),
   901  		)
   902  		Expect(err).ToNot(HaveOccurred())
   903  		defer ln.Close()
   904  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
   905  			RemoteAddr: ln.Addr().String(),
   906  			DelayPacket: func(dir quicproxy.Direction, data []byte) time.Duration {
   907  				if dir == quicproxy.DirectionIncoming && wire.IsLongHeaderPacket(data[0]) && data[0]&0x30>>4 == 0 { // Initial packet from client
   908  					return rtt/2 + rtt
   909  				}
   910  				return rtt / 2
   911  			},
   912  		})
   913  		Expect(err).ToNot(HaveOccurred())
   914  		defer proxy.Close()
   915  
   916  		transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData)
   917  
   918  		Expect(counter.getRcvdLongHeaderPackets()[0].hdr.Type).To(Equal(protocol.PacketTypeInitial))
   919  		zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
   920  		Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10))
   921  		Expect(zeroRTTPackets[0]).To(Equal(protocol.PacketNumber(0)))
   922  	})
   923  
   924  	It("allows the application to attach data to the session ticket, for the server", func() {
   925  		tlsConf := getTLSConfig()
   926  		tlsConf.WrapSession = func(cs tls.ConnectionState, ss *tls.SessionState) ([]byte, error) {
   927  			ss.Extra = append(ss.Extra, []byte("foobar"))
   928  			return tlsConf.EncryptTicket(cs, ss)
   929  		}
   930  		var unwrapped bool
   931  		tlsConf.UnwrapSession = func(identity []byte, cs tls.ConnectionState) (*tls.SessionState, error) {
   932  			defer GinkgoRecover()
   933  			state, err := tlsConf.DecryptTicket(identity, cs)
   934  			if err != nil {
   935  				return nil, err
   936  			}
   937  			Expect(state.Extra).To(HaveLen(2))
   938  			Expect(state.Extra[1]).To(Equal([]byte("foobar")))
   939  			unwrapped = true
   940  			return state, nil
   941  		}
   942  		clientTLSConf := getTLSClientConfig()
   943  		dialAndReceiveSessionTicket(tlsConf, nil, clientTLSConf)
   944  
   945  		ln, err := quic.ListenAddrEarly(
   946  			"localhost:0",
   947  			tlsConf,
   948  			getQuicConfig(&quic.Config{Allow0RTT: true}),
   949  		)
   950  		Expect(err).ToNot(HaveOccurred())
   951  		defer ln.Close()
   952  
   953  		transfer0RTTData(
   954  			ln,
   955  			ln.Addr().(*net.UDPAddr).Port,
   956  			10,
   957  			clientTLSConf,
   958  			getQuicConfig(nil),
   959  			PRData,
   960  		)
   961  		Expect(unwrapped).To(BeTrue())
   962  	})
   963  
   964  	It("allows the application to attach data to the session ticket, for the client", func() {
   965  		tlsConf := getTLSConfig()
   966  		clientTLSConf := getTLSClientConfig()
   967  		var restored bool
   968  		clientTLSConf.ClientSessionCache = &metadataClientSessionCache{
   969  			toAdd: []byte("foobar"),
   970  			restored: func(b []byte) {
   971  				defer GinkgoRecover()
   972  				Expect(b).To(Equal([]byte("foobar")))
   973  				restored = true
   974  			},
   975  			cache: tls.NewLRUClientSessionCache(100),
   976  		}
   977  		dialAndReceiveSessionTicket(tlsConf, nil, clientTLSConf)
   978  
   979  		ln, err := quic.ListenAddrEarly(
   980  			"localhost:0",
   981  			tlsConf,
   982  			getQuicConfig(&quic.Config{Allow0RTT: true}),
   983  		)
   984  		Expect(err).ToNot(HaveOccurred())
   985  		defer ln.Close()
   986  
   987  		transfer0RTTData(
   988  			ln,
   989  			ln.Addr().(*net.UDPAddr).Port,
   990  			10,
   991  			clientTLSConf,
   992  			getQuicConfig(nil),
   993  			PRData,
   994  		)
   995  		Expect(restored).To(BeTrue())
   996  	})
   997  
   998  	It("sends 0-RTT datagrams", func() {
   999  		tlsConf := getTLSConfig()
  1000  		clientTLSConf := getTLSClientConfig()
  1001  		dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{
  1002  			EnableDatagrams: true,
  1003  		}), clientTLSConf)
  1004  
  1005  		counter, tracer := newPacketTracer()
  1006  		ln, err := quic.ListenAddrEarly(
  1007  			"localhost:0",
  1008  			tlsConf,
  1009  			getQuicConfig(&quic.Config{
  1010  				Allow0RTT:       true,
  1011  				EnableDatagrams: true,
  1012  				Tracer:          newTracer(tracer),
  1013  			}),
  1014  		)
  1015  		Expect(err).ToNot(HaveOccurred())
  1016  		defer ln.Close()
  1017  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
  1018  		defer proxy.Close()
  1019  
  1020  		// second connection
  1021  		sentMessage := GeneratePRData(100)
  1022  		var receivedMessage []byte
  1023  		received := make(chan struct{})
  1024  		go func() {
  1025  			defer GinkgoRecover()
  1026  			defer close(received)
  1027  			conn, err := ln.Accept(context.Background())
  1028  			Expect(err).ToNot(HaveOccurred())
  1029  			receivedMessage, err = conn.ReceiveDatagram(context.Background())
  1030  			Expect(err).ToNot(HaveOccurred())
  1031  			Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
  1032  		}()
  1033  		conn, err := quic.DialAddrEarly(
  1034  			context.Background(),
  1035  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
  1036  			clientTLSConf,
  1037  			getQuicConfig(&quic.Config{
  1038  				EnableDatagrams: true,
  1039  			}),
  1040  		)
  1041  		Expect(err).ToNot(HaveOccurred())
  1042  		Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
  1043  		Expect(conn.SendDatagram(sentMessage)).To(Succeed())
  1044  		<-conn.HandshakeComplete()
  1045  		<-received
  1046  
  1047  		Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
  1048  		Expect(conn.CloseWithError(0, "")).To(Succeed())
  1049  		Expect(receivedMessage).To(Equal(sentMessage))
  1050  		num0RTT := num0RTTPackets.Load()
  1051  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
  1052  		Expect(num0RTT).ToNot(BeZero())
  1053  		zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
  1054  		Expect(zeroRTTPackets).To(HaveLen(1))
  1055  	})
  1056  
  1057  	It("rejects 0-RTT datagrams when the server doesn't support datagrams anymore", func() {
  1058  		tlsConf := getTLSConfig()
  1059  		clientTLSConf := getTLSClientConfig()
  1060  		dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{
  1061  			EnableDatagrams: true,
  1062  		}), clientTLSConf)
  1063  
  1064  		counter, tracer := newPacketTracer()
  1065  		ln, err := quic.ListenAddrEarly(
  1066  			"localhost:0",
  1067  			tlsConf,
  1068  			getQuicConfig(&quic.Config{
  1069  				Allow0RTT:       true,
  1070  				EnableDatagrams: false,
  1071  				Tracer:          newTracer(tracer),
  1072  			}),
  1073  		)
  1074  		Expect(err).ToNot(HaveOccurred())
  1075  		defer ln.Close()
  1076  
  1077  		proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
  1078  		defer proxy.Close()
  1079  
  1080  		// second connection
  1081  		go func() {
  1082  			defer GinkgoRecover()
  1083  			conn, err := ln.Accept(context.Background())
  1084  			Expect(err).ToNot(HaveOccurred())
  1085  			_, err = conn.ReceiveDatagram(context.Background())
  1086  			Expect(err.Error()).To(Equal("datagram support disabled"))
  1087  			<-conn.HandshakeComplete()
  1088  			Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
  1089  		}()
  1090  		conn, err := quic.DialAddrEarly(
  1091  			context.Background(),
  1092  			fmt.Sprintf("localhost:%d", proxy.LocalPort()),
  1093  			clientTLSConf,
  1094  			getQuicConfig(&quic.Config{
  1095  				EnableDatagrams: true,
  1096  			}),
  1097  		)
  1098  		Expect(err).ToNot(HaveOccurred())
  1099  		// the client can temporarily send datagrams but the server doesn't process them.
  1100  		Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
  1101  		Expect(conn.SendDatagram(make([]byte, 100))).To(Succeed())
  1102  		<-conn.HandshakeComplete()
  1103  
  1104  		Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse())
  1105  		Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
  1106  		Expect(conn.CloseWithError(0, "")).To(Succeed())
  1107  		num0RTT := num0RTTPackets.Load()
  1108  		fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
  1109  		Expect(num0RTT).ToNot(BeZero())
  1110  		Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
  1111  	})
  1112  })