github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/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/metacubex/quic-go" 12 quicproxy "github.com/metacubex/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 })