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