github.com/MerlinKodo/quic-go@v0.39.2/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/MerlinKodo/quic-go" 12 quicproxy "github.com/MerlinKodo/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", func() { 58 serverConfig.RequireAddressValidation = func(net.Addr) bool { return true } 59 ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig) 60 Expect(err).ToNot(HaveOccurred()) 61 defer ln.Close() 62 63 runProxy(ln.Addr()) 64 startTime := time.Now() 65 conn, err := quic.DialAddr( 66 context.Background(), 67 fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), 68 getTLSClientConfig(), 69 getQuicConfig(nil), 70 ) 71 Expect(err).ToNot(HaveOccurred()) 72 defer conn.CloseWithError(0, "") 73 expectDurationInRTTs(startTime, 2) 74 }) 75 76 It("establishes a connection in 1 RTT when the server doesn't require a token", func() { 77 ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig) 78 Expect(err).ToNot(HaveOccurred()) 79 defer ln.Close() 80 81 runProxy(ln.Addr()) 82 startTime := time.Now() 83 conn, err := quic.DialAddr( 84 context.Background(), 85 fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), 86 getTLSClientConfig(), 87 getQuicConfig(nil), 88 ) 89 Expect(err).ToNot(HaveOccurred()) 90 defer conn.CloseWithError(0, "") 91 expectDurationInRTTs(startTime, 1) 92 }) 93 94 It("establishes a connection in 2 RTTs if a HelloRetryRequest is performed", func() { 95 serverTLSConfig.CurvePreferences = []tls.CurveID{tls.CurveP384} 96 ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig) 97 Expect(err).ToNot(HaveOccurred()) 98 defer ln.Close() 99 100 runProxy(ln.Addr()) 101 startTime := time.Now() 102 conn, err := quic.DialAddr( 103 context.Background(), 104 fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), 105 getTLSClientConfig(), 106 getQuicConfig(nil), 107 ) 108 Expect(err).ToNot(HaveOccurred()) 109 defer conn.CloseWithError(0, "") 110 expectDurationInRTTs(startTime, 2) 111 }) 112 113 It("receives the first message from the server after 2 RTTs, when the server uses ListenAddr", func() { 114 ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig) 115 Expect(err).ToNot(HaveOccurred()) 116 go func() { 117 defer GinkgoRecover() 118 conn, err := ln.Accept(context.Background()) 119 Expect(err).ToNot(HaveOccurred()) 120 str, err := conn.OpenUniStream() 121 Expect(err).ToNot(HaveOccurred()) 122 _, err = str.Write([]byte("foobar")) 123 Expect(err).ToNot(HaveOccurred()) 124 Expect(str.Close()).To(Succeed()) 125 }() 126 defer ln.Close() 127 128 runProxy(ln.Addr()) 129 startTime := time.Now() 130 conn, err := quic.DialAddr( 131 context.Background(), 132 fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), 133 getTLSClientConfig(), 134 getQuicConfig(nil), 135 ) 136 Expect(err).ToNot(HaveOccurred()) 137 defer conn.CloseWithError(0, "") 138 str, err := conn.AcceptUniStream(context.Background()) 139 Expect(err).ToNot(HaveOccurred()) 140 data, err := io.ReadAll(str) 141 Expect(err).ToNot(HaveOccurred()) 142 Expect(data).To(Equal([]byte("foobar"))) 143 expectDurationInRTTs(startTime, 2) 144 }) 145 146 It("receives the first message from the server after 1 RTT, when the server uses ListenAddrEarly", func() { 147 ln, err := quic.ListenAddrEarly("localhost:0", serverTLSConfig, serverConfig) 148 Expect(err).ToNot(HaveOccurred()) 149 go func() { 150 defer GinkgoRecover() 151 conn, err := ln.Accept(context.Background()) 152 Expect(err).ToNot(HaveOccurred()) 153 // Check the ALPN now. This is probably what an application would do. 154 // It makes sure that ConnectionState does not block until the handshake completes. 155 Expect(conn.ConnectionState().TLS.NegotiatedProtocol).To(Equal(alpn)) 156 str, err := conn.OpenUniStream() 157 Expect(err).ToNot(HaveOccurred()) 158 _, err = str.Write([]byte("foobar")) 159 Expect(err).ToNot(HaveOccurred()) 160 Expect(str.Close()).To(Succeed()) 161 }() 162 defer ln.Close() 163 164 runProxy(ln.Addr()) 165 startTime := time.Now() 166 conn, err := quic.DialAddr( 167 context.Background(), 168 fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), 169 getTLSClientConfig(), 170 getQuicConfig(nil), 171 ) 172 Expect(err).ToNot(HaveOccurred()) 173 defer conn.CloseWithError(0, "") 174 str, err := conn.AcceptUniStream(context.Background()) 175 Expect(err).ToNot(HaveOccurred()) 176 data, err := io.ReadAll(str) 177 Expect(err).ToNot(HaveOccurred()) 178 Expect(data).To(Equal([]byte("foobar"))) 179 expectDurationInRTTs(startTime, 1) 180 }) 181 })