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