github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/integrationtests/self/handshake_rtt_test.go (about)

     1  package self_test
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"time"
    10  
    11  	"github.com/apernet/quic-go"
    12  	quicproxy "github.com/apernet/quic-go/integrationtests/tools/proxy"
    13  
    14  	. "github.com/onsi/ginkgo/v2"
    15  	. "github.com/onsi/gomega"
    16  )
    17  
    18  var _ = Describe("Handshake RTT tests", func() {
    19  	var (
    20  		proxy           *quicproxy.QuicProxy
    21  		serverConfig    *quic.Config
    22  		serverTLSConfig *tls.Config
    23  	)
    24  
    25  	const rtt = 400 * time.Millisecond
    26  
    27  	BeforeEach(func() {
    28  		serverConfig = getQuicConfig(nil)
    29  		serverTLSConfig = getTLSConfig()
    30  	})
    31  
    32  	AfterEach(func() {
    33  		Expect(proxy.Close()).To(Succeed())
    34  	})
    35  
    36  	runProxy := func(serverAddr net.Addr) {
    37  		var err error
    38  		// start the proxy
    39  		proxy, err = quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
    40  			RemoteAddr:  serverAddr.String(),
    41  			DelayPacket: func(_ quicproxy.Direction, _ []byte) time.Duration { return rtt / 2 },
    42  		})
    43  		Expect(err).ToNot(HaveOccurred())
    44  	}
    45  
    46  	expectDurationInRTTs := func(startTime time.Time, num int) {
    47  		testDuration := time.Since(startTime)
    48  		rtts := float32(testDuration) / float32(rtt)
    49  		Expect(rtts).To(SatisfyAll(
    50  			BeNumerically(">=", num),
    51  			BeNumerically("<", num+1),
    52  		))
    53  	}
    54  
    55  	// 1 RTT for verifying the source address
    56  	// 1 RTT for the TLS handshake
    57  	It("is forward-secure after 2 RTTs with Retry", func() {
    58  		laddr, err := net.ResolveUDPAddr("udp", "localhost:0")
    59  		Expect(err).ToNot(HaveOccurred())
    60  		udpConn, err := net.ListenUDP("udp", laddr)
    61  		Expect(err).ToNot(HaveOccurred())
    62  		defer udpConn.Close()
    63  		tr := &quic.Transport{
    64  			Conn:                udpConn,
    65  			VerifySourceAddress: func(net.Addr) bool { return true },
    66  		}
    67  		addTracer(tr)
    68  		defer tr.Close()
    69  		ln, err := tr.Listen(serverTLSConfig, serverConfig)
    70  		Expect(err).ToNot(HaveOccurred())
    71  		defer ln.Close()
    72  
    73  		runProxy(ln.Addr())
    74  		startTime := time.Now()
    75  		conn, err := quic.DialAddr(
    76  			context.Background(),
    77  			fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
    78  			getTLSClientConfig(),
    79  			getQuicConfig(&quic.Config{GetConfigForClient: func(info *quic.ClientHelloInfo) (*quic.Config, error) {
    80  				Expect(info.AddrVerified).To(BeTrue())
    81  				return nil, nil
    82  			}}),
    83  		)
    84  		Expect(err).ToNot(HaveOccurred())
    85  		defer conn.CloseWithError(0, "")
    86  		expectDurationInRTTs(startTime, 2)
    87  	})
    88  
    89  	It("establishes a connection in 1 RTT when the server doesn't require a token", func() {
    90  		ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig)
    91  		Expect(err).ToNot(HaveOccurred())
    92  		defer ln.Close()
    93  
    94  		runProxy(ln.Addr())
    95  		startTime := time.Now()
    96  		conn, err := quic.DialAddr(
    97  			context.Background(),
    98  			fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
    99  			getTLSClientConfig(),
   100  			getQuicConfig(&quic.Config{GetConfigForClient: func(info *quic.ClientHelloInfo) (*quic.Config, error) {
   101  				Expect(info.AddrVerified).To(BeFalse())
   102  				return nil, nil
   103  			}}),
   104  		)
   105  		Expect(err).ToNot(HaveOccurred())
   106  		defer conn.CloseWithError(0, "")
   107  		expectDurationInRTTs(startTime, 1)
   108  	})
   109  
   110  	It("establishes a connection in 2 RTTs if a HelloRetryRequest is performed", func() {
   111  		serverTLSConfig.CurvePreferences = []tls.CurveID{tls.CurveP384}
   112  		ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig)
   113  		Expect(err).ToNot(HaveOccurred())
   114  		defer ln.Close()
   115  
   116  		runProxy(ln.Addr())
   117  		startTime := time.Now()
   118  		conn, err := quic.DialAddr(
   119  			context.Background(),
   120  			fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
   121  			getTLSClientConfig(),
   122  			getQuicConfig(nil),
   123  		)
   124  		Expect(err).ToNot(HaveOccurred())
   125  		defer conn.CloseWithError(0, "")
   126  		expectDurationInRTTs(startTime, 2)
   127  	})
   128  
   129  	It("receives the first message from the server after 2 RTTs, when the server uses ListenAddr", func() {
   130  		ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig)
   131  		Expect(err).ToNot(HaveOccurred())
   132  		go func() {
   133  			defer GinkgoRecover()
   134  			conn, err := ln.Accept(context.Background())
   135  			Expect(err).ToNot(HaveOccurred())
   136  			str, err := conn.OpenUniStream()
   137  			Expect(err).ToNot(HaveOccurred())
   138  			_, err = str.Write([]byte("foobar"))
   139  			Expect(err).ToNot(HaveOccurred())
   140  			Expect(str.Close()).To(Succeed())
   141  		}()
   142  		defer ln.Close()
   143  
   144  		runProxy(ln.Addr())
   145  		startTime := time.Now()
   146  		conn, err := quic.DialAddr(
   147  			context.Background(),
   148  			fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
   149  			getTLSClientConfig(),
   150  			getQuicConfig(nil),
   151  		)
   152  		Expect(err).ToNot(HaveOccurred())
   153  		defer conn.CloseWithError(0, "")
   154  		str, err := conn.AcceptUniStream(context.Background())
   155  		Expect(err).ToNot(HaveOccurred())
   156  		data, err := io.ReadAll(str)
   157  		Expect(err).ToNot(HaveOccurred())
   158  		Expect(data).To(Equal([]byte("foobar")))
   159  		expectDurationInRTTs(startTime, 2)
   160  	})
   161  
   162  	It("receives the first message from the server after 1 RTT, when the server uses ListenAddrEarly", func() {
   163  		ln, err := quic.ListenAddrEarly("localhost:0", serverTLSConfig, serverConfig)
   164  		Expect(err).ToNot(HaveOccurred())
   165  		go func() {
   166  			defer GinkgoRecover()
   167  			conn, err := ln.Accept(context.Background())
   168  			Expect(err).ToNot(HaveOccurred())
   169  			// Check the ALPN now. This is probably what an application would do.
   170  			// It makes sure that ConnectionState does not block until the handshake completes.
   171  			Expect(conn.ConnectionState().TLS.NegotiatedProtocol).To(Equal(alpn))
   172  			str, err := conn.OpenUniStream()
   173  			Expect(err).ToNot(HaveOccurred())
   174  			_, err = str.Write([]byte("foobar"))
   175  			Expect(err).ToNot(HaveOccurred())
   176  			Expect(str.Close()).To(Succeed())
   177  		}()
   178  		defer ln.Close()
   179  
   180  		runProxy(ln.Addr())
   181  		startTime := time.Now()
   182  		conn, err := quic.DialAddr(
   183  			context.Background(),
   184  			fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
   185  			getTLSClientConfig(),
   186  			getQuicConfig(nil),
   187  		)
   188  		Expect(err).ToNot(HaveOccurred())
   189  		defer conn.CloseWithError(0, "")
   190  		str, err := conn.AcceptUniStream(context.Background())
   191  		Expect(err).ToNot(HaveOccurred())
   192  		data, err := io.ReadAll(str)
   193  		Expect(err).ToNot(HaveOccurred())
   194  		Expect(data).To(Equal([]byte("foobar")))
   195  		expectDurationInRTTs(startTime, 1)
   196  	})
   197  })