Implement an end-to-end test for WebTransport negotiation.

PiperOrigin-RevId: 362632936
Change-Id: Ifd4a18fdb86f24bdcb7a043e3d318b4317b5dbae
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 1b300e9..de43714 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -24,6 +24,7 @@
 #include "quic/core/quic_packet_writer_wrapper.h"
 #include "quic/core/quic_packets.h"
 #include "quic/core/quic_session.h"
+#include "quic/core/quic_types.h"
 #include "quic/core/quic_utils.h"
 #include "quic/platform/api/quic_epoll.h"
 #include "quic/platform/api/quic_error_code_wrappers.h"
@@ -55,6 +56,7 @@
 #include "quic/test_tools/quic_stream_id_manager_peer.h"
 #include "quic/test_tools/quic_stream_peer.h"
 #include "quic/test_tools/quic_stream_sequencer_peer.h"
+#include "quic/test_tools/quic_test_backend.h"
 #include "quic/test_tools/quic_test_client.h"
 #include "quic/test_tools/quic_test_server.h"
 #include "quic/test_tools/quic_test_utils.h"
@@ -220,6 +222,7 @@
     client->UseConnectionIdLength(override_server_connection_id_length_);
     client->UseClientConnectionIdLength(override_client_connection_id_length_);
     client->client()->set_connection_debug_visitor(connection_debug_visitor_);
+    client->client()->set_enable_web_transport(enable_web_transport_);
     client->Connect();
     return client;
   }
@@ -356,6 +359,11 @@
   }
 
   bool Initialize() {
+    if (enable_web_transport_) {
+      SetQuicReloadableFlag(quic_h3_datagram, true);
+      memory_cache_backend_.set_enable_webtransport(true);
+    }
+
     QuicTagVector copt;
     server_config_.SetConnectionOptionsToSend(copt);
     copt = client_extra_copts_;
@@ -676,7 +684,7 @@
   bool connect_to_server_on_initialize_;
   QuicSocketAddress server_address_;
   std::string server_hostname_;
-  QuicMemoryCacheBackend memory_cache_backend_;
+  QuicTestBackend memory_cache_backend_;
   std::unique_ptr<ServerThread> server_thread_;
   std::unique_ptr<QuicTestClient> client_;
   QuicConnectionDebugVisitor* connection_debug_visitor_ = nullptr;
@@ -695,6 +703,7 @@
   int override_server_connection_id_length_ = -1;
   int override_client_connection_id_length_ = -1;
   uint8_t expected_server_connection_id_length_;
+  bool enable_web_transport_ = false;
 };
 
 // Run all end to end tests with all supported versions.
@@ -5667,6 +5676,35 @@
   ADD_FAILURE() << "Client should not have 10 resumption tickets.";
 }
 
+TEST_P(EndToEndTest, WebTransportSession) {
+  enable_web_transport_ = true;
+  ASSERT_TRUE(Initialize());
+
+  if (!version_.UsesHttp3()) {
+    return;
+  }
+
+  spdy::SpdyHeaderBlock headers;
+  headers[":scheme"] = "https";
+  headers[":authority"] = "localhost";
+  headers[":path"] = "/echo";
+  headers[":method"] = "CONNECT";
+  headers[":protocol"] = "webtransport";
+
+  client_->SendMessage(headers, "", /*fin=*/false);
+  QuicSpdyStream* stream = client_->latest_created_stream();
+  EXPECT_TRUE(stream->web_transport() != nullptr);
+  WebTransportSessionId id = client_->latest_created_stream()->id();
+  QuicSpdySession* client_session = GetClientSession();
+  EXPECT_TRUE(client_session->GetWebTransportSession(id) != nullptr);
+  client_->WaitUntil(-1, [stream]() { return stream->headers_decompressed(); });
+
+  server_thread_->Pause();
+  QuicSpdySession* server_session = GetServerSession();
+  EXPECT_TRUE(server_session->GetWebTransportSession(id) != nullptr);
+  server_thread_->Resume();
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/quic_test_backend.cc b/quic/test_tools/quic_test_backend.cc
new file mode 100644
index 0000000..f33c6f1
--- /dev/null
+++ b/quic/test_tools/quic_test_backend.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quic/test_tools/quic_test_backend.h"
+
+namespace quic {
+namespace test {
+
+QuicSimpleServerBackend::WebTransportResponse
+QuicTestBackend::ProcessWebTransportRequest(
+    const spdy::Http2HeaderBlock& request_headers) {
+  if (!SupportsWebTransport()) {
+    return QuicSimpleServerBackend::ProcessWebTransportRequest(request_headers);
+  }
+
+  WebTransportResponse response;
+  response.response_headers[":status"] = "200";
+  return response;
+}
+
+}  // namespace test
+}  // namespace quic
diff --git a/quic/test_tools/quic_test_backend.h b/quic/test_tools/quic_test_backend.h
new file mode 100644
index 0000000..fe5b454
--- /dev/null
+++ b/quic/test_tools/quic_test_backend.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_BACKEND_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_BACKEND_H_
+
+#include "quic/tools/quic_memory_cache_backend.h"
+
+namespace quic {
+namespace test {
+
+// QuicTestBackend is a QuicSimpleServer backend usable in tests.  It has extra
+// WebTransport endpoints on top of what QuicMemoryCacheBackend already
+// provides.
+class QuicTestBackend : public QuicMemoryCacheBackend {
+ public:
+  WebTransportResponse ProcessWebTransportRequest(
+      const spdy::Http2HeaderBlock& /*request_headers*/) override;
+  bool SupportsWebTransport() override { return enable_webtransport_; }
+
+  void set_enable_webtransport(bool enable_webtransport) {
+    enable_webtransport_ = enable_webtransport;
+  }
+
+ private:
+  bool enable_webtransport_ = false;
+};
+
+}  // namespace test
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_BACKEND_H_
diff --git a/quic/tools/quic_client.cc b/quic/tools/quic_client.cc
index f8375ab..448291b 100644
--- a/quic/tools/quic_client.cc
+++ b/quic/tools/quic_client.cc
@@ -165,7 +165,7 @@
     QuicConnection* connection) {
   return std::make_unique<QuicSimpleClientSession>(
       *config(), supported_versions, connection, server_id(), crypto_config(),
-      push_promise_index(), drop_response_body());
+      push_promise_index(), drop_response_body(), enable_web_transport());
 }
 
 QuicClientEpollNetworkHelper* QuicClient::epoll_network_helper() {
diff --git a/quic/tools/quic_simple_client_session.cc b/quic/tools/quic_simple_client_session.cc
index 7f64495..d1efcba 100644
--- a/quic/tools/quic_simple_client_session.cc
+++ b/quic/tools/quic_simple_client_session.cc
@@ -16,13 +16,32 @@
     QuicCryptoClientConfig* crypto_config,
     QuicClientPushPromiseIndex* push_promise_index,
     bool drop_response_body)
+    : QuicSimpleClientSession(config,
+                              supported_versions,
+                              connection,
+                              server_id,
+                              crypto_config,
+                              push_promise_index,
+                              drop_response_body,
+                              /*enable_web_transport=*/false) {}
+
+QuicSimpleClientSession::QuicSimpleClientSession(
+    const QuicConfig& config,
+    const ParsedQuicVersionVector& supported_versions,
+    QuicConnection* connection,
+    const QuicServerId& server_id,
+    QuicCryptoClientConfig* crypto_config,
+    QuicClientPushPromiseIndex* push_promise_index,
+    bool drop_response_body,
+    bool enable_web_transport)
     : QuicSpdyClientSession(config,
                             supported_versions,
                             connection,
                             server_id,
                             crypto_config,
                             push_promise_index),
-      drop_response_body_(drop_response_body) {}
+      drop_response_body_(drop_response_body),
+      enable_web_transport_(enable_web_transport) {}
 
 std::unique_ptr<QuicSpdyClientStream>
 QuicSimpleClientSession::CreateClientStream() {
@@ -31,4 +50,8 @@
       drop_response_body_);
 }
 
+bool QuicSimpleClientSession::ShouldNegotiateWebTransport() {
+  return enable_web_transport_;
+}
+
 }  // namespace quic
diff --git a/quic/tools/quic_simple_client_session.h b/quic/tools/quic_simple_client_session.h
index 5b53141..aa86d9a 100644
--- a/quic/tools/quic_simple_client_session.h
+++ b/quic/tools/quic_simple_client_session.h
@@ -19,11 +19,21 @@
                           QuicCryptoClientConfig* crypto_config,
                           QuicClientPushPromiseIndex* push_promise_index,
                           bool drop_response_body);
+  QuicSimpleClientSession(const QuicConfig& config,
+                          const ParsedQuicVersionVector& supported_versions,
+                          QuicConnection* connection,
+                          const QuicServerId& server_id,
+                          QuicCryptoClientConfig* crypto_config,
+                          QuicClientPushPromiseIndex* push_promise_index,
+                          bool drop_response_body,
+                          bool enable_web_transport);
 
   std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override;
+  bool ShouldNegotiateWebTransport() override;
 
  private:
   const bool drop_response_body_;
+  const bool enable_web_transport_;
 };
 
 }  // namespace quic
diff --git a/quic/tools/quic_simple_server_backend.h b/quic/tools/quic_simple_server_backend.h
index 9e16d4e..1a1125a 100644
--- a/quic/tools/quic_simple_server_backend.h
+++ b/quic/tools/quic_simple_server_backend.h
@@ -5,7 +5,10 @@
 #ifndef QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_
 #define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_
 
+#include <memory>
+
 #include "quic/core/quic_types.h"
+#include "quic/core/web_transport_interface.h"
 #include "quic/tools/quic_backend_response.h"
 #include "spdy/core/spdy_header_block.h"
 
@@ -33,6 +36,11 @@
         std::list<QuicBackendResponse::ServerPushInfo> resources) = 0;
   };
 
+  struct WebTransportResponse {
+    spdy::Http2HeaderBlock response_headers;
+    std::unique_ptr<WebTransportVisitor> visitor;
+  };
+
   virtual ~QuicSimpleServerBackend() = default;
   // This method initializes the backend instance to fetch responses
   // from a backend server, in-memory cache etc.
@@ -51,6 +59,14 @@
       RequestHandler* request_handler) = 0;
   // Clears the state of the backend  instance
   virtual void CloseBackendResponseStream(RequestHandler* request_handler) = 0;
+
+  virtual WebTransportResponse ProcessWebTransportRequest(
+      const spdy::Http2HeaderBlock& /*request_headers*/) {
+    WebTransportResponse response;
+    response.response_headers[":status"] = "400";
+    return response;
+  }
+  virtual bool SupportsWebTransport() { return false; }
 };
 
 }  // namespace quic
diff --git a/quic/tools/quic_simple_server_session.h b/quic/tools/quic_simple_server_session.h
index 49aaae5..c117cdd 100644
--- a/quic/tools/quic_simple_server_session.h
+++ b/quic/tools/quic_simple_server_session.h
@@ -104,6 +104,10 @@
 
   void MaybeInitializeHttp3UnidirectionalStreams() override;
 
+  bool ShouldNegotiateWebTransport() override {
+    return quic_simple_server_backend_->SupportsWebTransport();
+  }
+
  private:
   friend class test::QuicSimpleServerSessionPeer;
 
diff --git a/quic/tools/quic_spdy_client_base.h b/quic/tools/quic_spdy_client_base.h
index 54c73f7..1b4ca81 100644
--- a/quic/tools/quic_spdy_client_base.h
+++ b/quic/tools/quic_spdy_client_base.h
@@ -139,6 +139,11 @@
   }
   bool drop_response_body() const { return drop_response_body_; }
 
+  void set_enable_web_transport(bool enable_web_transport) {
+    enable_web_transport_ = enable_web_transport;
+  }
+  bool enable_web_transport() const { return enable_web_transport_; }
+
   // Set the max promise id for the client session.
   // TODO(b/151641466): Rename this method.
   void SetMaxAllowedPushId(PushId max) { max_allowed_push_id_ = max; }
@@ -221,6 +226,7 @@
   std::unique_ptr<ClientQuicDataToResend> push_promise_data_to_resend_;
 
   bool drop_response_body_ = false;
+  bool enable_web_transport_ = false;
 
   // The max promise id to set on the client session when created.
   PushId max_allowed_push_id_;