diff --git a/interop/client/main.go b/interop/client/main.go index 1f051f1ec..28624e2e3 100644 --- a/interop/client/main.go +++ b/interop/client/main.go @@ -74,14 +74,16 @@ func runTestcase(testcase string) error { QuicConfig: quicConf, } defer r.Close() - return downloadFiles(r, urls) + return downloadFiles(r, urls, false) case "handshake", "transfer", "retry": case "multiconnect": return runMultiConnectTest(urls) case "versionnegotiation": return runVersionNegotiationTest(urls) case "resumption": - return runResumptionTest(urls) + return runResumptionTest(urls, false) + case "zerortt": + return runResumptionTest(urls, true) default: return errUnsupported } @@ -91,7 +93,7 @@ func runTestcase(testcase string) error { QuicConfig: quicConf, } defer r.Close() - return downloadFiles(r, urls) + return downloadFiles(r, urls, false) } func runVersionNegotiationTest(urls []string) error { @@ -99,7 +101,7 @@ func runVersionNegotiationTest(urls []string) error { return errors.New("expected at least 2 URLs") } protocol.SupportedVersions = []protocol.VersionNumber{0x1a2a3a4a} - err := downloadFile(&http09.RoundTripper{}, urls[0]) + err := downloadFile(&http09.RoundTripper{}, urls[0], false) if err == nil { return errors.New("expected version negotiation to fail") } @@ -112,7 +114,7 @@ func runVersionNegotiationTest(urls []string) error { func runMultiConnectTest(urls []string) error { for _, url := range urls { r := &http09.RoundTripper{TLSClientConfig: tlsConf} - if err := downloadFile(r, url); err != nil { + if err := downloadFile(r, url, false); err != nil { return err } if err := r.Close(); err != nil { @@ -122,7 +124,7 @@ func runMultiConnectTest(urls []string) error { return nil } -func runResumptionTest(urls []string) error { +func runResumptionTest(urls []string, use0RTT bool) error { if len(urls) < 2 { return errors.New("expected at least 2 URLs") } @@ -131,7 +133,7 @@ func runResumptionTest(urls []string) error { // do the first transfer r := &http09.RoundTripper{TLSClientConfig: tlsConf} - if err := downloadFiles(r, urls[:1]); err != nil { + if err := downloadFiles(r, urls[:1], false); err != nil { return err } r.Close() @@ -139,22 +141,26 @@ func runResumptionTest(urls []string) error { // reestablish the connection, using the session ticket that the server (hopefully provided) r = &http09.RoundTripper{TLSClientConfig: tlsConf} defer r.Close() - return downloadFiles(r, urls[1:]) + return downloadFiles(r, urls[1:], use0RTT) } -func downloadFiles(cl http.RoundTripper, urls []string) error { +func downloadFiles(cl http.RoundTripper, urls []string, use0RTT bool) error { var g errgroup.Group for _, u := range urls { url := u g.Go(func() error { - return downloadFile(cl, url) + return downloadFile(cl, url, use0RTT) }) } return g.Wait() } -func downloadFile(cl http.RoundTripper, url string) error { - req, err := http.NewRequest(http.MethodGet, url, nil) +func downloadFile(cl http.RoundTripper, url string, use0RTT bool) error { + method := http.MethodGet + if use0RTT { + method = http09.MethodGet0RTT + } + req, err := http.NewRequest(method, url, nil) if err != nil { return err } diff --git a/interop/http09/client.go b/interop/http09/client.go index 69bbced1e..c5a5166e8 100644 --- a/interop/http09/client.go +++ b/interop/http09/client.go @@ -15,6 +15,10 @@ import ( "github.com/lucas-clemente/quic-go" ) +// MethodGet0RTT allows a GET request to be sent using 0-RTT. +// Note that 0-RTT data doesn't provide replay protection. +const MethodGet0RTT = "GET_0RTT" + // RoundTripper performs HTTP/0.9 roundtrips over QUIC. type RoundTripper struct { mutex sync.Mutex @@ -30,7 +34,7 @@ var _ http.RoundTripper = &RoundTripper{} // RoundTrip performs a HTTP/0.9 request. // It only supports GET requests. func (r *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - if req.Method != http.MethodGet { + if req.Method != http.MethodGet && req.Method != MethodGet0RTT { return nil, errors.New("only GET requests supported") } @@ -76,17 +80,20 @@ type client struct { quicConf *quic.Config once sync.Once - sess quic.Session + sess quic.EarlySession dialErr error } func (c *client) RoundTrip(req *http.Request) (*http.Response, error) { c.once.Do(func() { - c.sess, c.dialErr = quic.DialAddr(c.hostname, c.tlsConf, c.quicConf) + c.sess, c.dialErr = quic.DialAddrEarly(c.hostname, c.tlsConf, c.quicConf) }) if c.dialErr != nil { return nil, c.dialErr } + if req.Method != MethodGet0RTT { + <-c.sess.HandshakeComplete().Done() + } return c.doRequest(req) } diff --git a/interop/http09/http_test.go b/interop/http09/http_test.go index d81ec0302..4942135c6 100644 --- a/interop/http09/http_test.go +++ b/interop/http09/http_test.go @@ -36,8 +36,8 @@ var _ = Describe("HTTP 0.9 integration tests", func() { defer close(done) _ = server.ListenAndServe() }() - var ln quic.Listener - Eventually(func() quic.Listener { + var ln quic.EarlyListener + Eventually(func() quic.EarlyListener { server.mutex.Lock() defer server.mutex.Unlock() ln = server.listener diff --git a/interop/http09/server.go b/interop/http09/server.go index 0277ef31e..99e653798 100644 --- a/interop/http09/server.go +++ b/interop/http09/server.go @@ -41,7 +41,7 @@ type Server struct { QuicConfig *quic.Config mutex sync.Mutex - listener quic.Listener + listener quic.EarlyListener } // Close closes the server. @@ -69,7 +69,7 @@ func (s *Server) ListenAndServe() error { tlsConf := s.TLSConfig.Clone() tlsConf.NextProtos = []string{h09alpn} - ln, err := quic.Listen(conn, tlsConf, s.QuicConfig) + ln, err := quic.ListenEarly(conn, tlsConf, s.QuicConfig) if err != nil { return err } diff --git a/interop/server/main.go b/interop/server/main.go index 7f3484515..f830ad30f 100644 --- a/interop/server/main.go +++ b/interop/server/main.go @@ -51,7 +51,7 @@ func main() { tlsConf.KeyLogWriter = keyLog switch testcase { - case "versionnegotiation", "handshake", "transfer", "resumption", "multiconnect": + case "versionnegotiation", "handshake", "transfer", "resumption", "zerortt", "multiconnect": err = runHTTP09Server(quicConf) case "retry": // By default, quic-go performs a Retry on every incoming connection.