diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 574fe38aa0e3c..1e7b7fe08ee5f 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -967,6 +967,7 @@ _BANNED_CPP_FUNCTIONS: Sequence[BanRule] = ( r'android_webview/browser/ip_protection/.*', r'chrome/browser/ip_protection/.*', r'components/ip_protection/.*', + r'net/quic/dedicated_web_transport_http3_client\.cc', # Needed to use MediaPipe API. r'components/media_effects/.*\.cc', diff --git a/net/quic/dedicated_web_transport_http3_client.cc b/net/quic/dedicated_web_transport_http3_client.cc index b1c857e8a8880..9879fd4203058 100644 --- a/net/quic/dedicated_web_transport_http3_client.cc +++ b/net/quic/dedicated_web_transport_http3_client.cc @@ -28,6 +28,7 @@ #include "net/third_party/quiche/src/quiche/quic/core/quic_connection.h" #include "net/third_party/quiche/src/quiche/quic/core/quic_types.h" #include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quiche/web_transport/web_transport_headers.h" #include "net/url_request/url_request_context.h" #include "url/scheme_host_port.h" @@ -365,6 +366,7 @@ DedicatedWebTransportHttp3Client::DedicatedWebTransportHttp3Client( : url_(url), origin_(origin), anonymization_key_(anonymization_key), + application_protocols_(parameters.application_protocols), context_(context), visitor_(visitor), quic_context_(context->quic_context()), @@ -748,6 +750,14 @@ int DedicatedWebTransportHttp3Client::DoSendRequest() { headers[":protocol"] = "webtransport"; headers["sec-webtransport-http3-draft02"] = "1"; headers["origin"] = origin_.Serialize(); + if (!application_protocols_.empty()) { + absl::StatusOr<std::string> protocols_header = + webtransport::SerializeSubprotocolRequestHeader(application_protocols_); + if (protocols_header.ok()) { + headers[webtransport::kSubprotocolRequestHeader] = + *std::move(protocols_header); + } + } stream->WriteHeaders(std::move(headers), /*fin=*/false, nullptr); web_transport_session_ = stream->web_transport(); diff --git a/net/quic/dedicated_web_transport_http3_client.h b/net/quic/dedicated_web_transport_http3_client.h index 30d000ce4bc8a..516bef6c81bdc 100644 --- a/net/quic/dedicated_web_transport_http3_client.h +++ b/net/quic/dedicated_web_transport_http3_client.h @@ -150,6 +150,7 @@ class NET_EXPORT DedicatedWebTransportHttp3Client const GURL url_; const url::Origin origin_; const NetworkAnonymizationKey anonymization_key_; + const std::vector<std::string> application_protocols_; const raw_ptr<URLRequestContext> context_; // Unowned. const raw_ptr<WebTransportClientVisitor> visitor_; // Unowned. diff --git a/net/quic/dedicated_web_transport_http3_client_test.cc b/net/quic/dedicated_web_transport_http3_client_test.cc index de283198da4f3..26adffd17befa 100644 --- a/net/quic/dedicated_web_transport_http3_client_test.cc +++ b/net/quic/dedicated_web_transport_http3_client_test.cc @@ -210,7 +210,7 @@ TEST_F(DedicatedWebTransportHttp3Test, Connect) { GetURL("/echo"), origin_, &visitor_, anonymization_key_, context_.get(), WebTransportParameters()); - EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning()); + EXPECT_CALL(visitor_, OnConnected).WillOnce(StopRunning()); client_->Connect(); Run(); ASSERT_TRUE(client_->session() != nullptr); @@ -251,7 +251,7 @@ TEST_F(DedicatedWebTransportHttp3Test, MAYBE_CloseTimeout) { GetURL("/echo"), origin_, &visitor_, anonymization_key_, context_.get(), WebTransportParameters()); - EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning()); + EXPECT_CALL(visitor_, OnConnected).WillOnce(StopRunning()); client_->Connect(); Run(); ASSERT_TRUE(client_->session() != nullptr); @@ -277,7 +277,7 @@ TEST_F(DedicatedWebTransportHttp3Test, CloseReason) { GetURL("/session-close"), origin_, &visitor_, anonymization_key_, context_.get(), WebTransportParameters()); - EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning()); + EXPECT_CALL(visitor_, OnConnected).WillOnce(StopRunning()); client_->Connect(); Run(); ASSERT_TRUE(client_->session() != nullptr); @@ -296,5 +296,42 @@ TEST_F(DedicatedWebTransportHttp3Test, CloseReason) { EXPECT_THAT(received_close_info, Optional(close_info)); } +// Test negotiation of the application protocol via +// https://www.ietf.org/archive/id/draft-ietf-webtrans-http3-12.html#name-application-protocol-negoti +TEST_F(DedicatedWebTransportHttp3Test, SubprotocolHeader) { + StartServer(); + WebTransportParameters parameters; + parameters.application_protocols = {"first", "second", "third"}; + // The selected-subprotocol endpoint selects the first of the offered + // protocols by default, and echoes it on a unidirectional stream. + client_ = std::make_unique<DedicatedWebTransportHttp3Client>( + GetURL("/selected-subprotocol"), origin_, &visitor_, anonymization_key_, + context_.get(), parameters); + + bool stream_received = false; + EXPECT_CALL(visitor_, OnConnected).WillOnce(StopRunning()); + EXPECT_CALL(visitor_, OnIncomingUnidirectionalStreamAvailable).WillOnce([&] { + stream_received = true; + StopRunning(); + }); + client_->Connect(); + Run(); + ASSERT_TRUE(client_->session() != nullptr); + + EXPECT_EQ(client_->session()->GetNegotiatedSubprotocol(), "first"); + + if (!stream_received) { + Run(); + } + + quic::WebTransportStream* stream = + client_->session()->AcceptIncomingUnidirectionalStream(); + ASSERT_TRUE(stream != nullptr); + std::string read_buffer; + webtransport::Stream::ReadResult read_result = stream->Read(&read_buffer); + ASSERT_TRUE(read_result.fin); + EXPECT_EQ(read_buffer, "first"); +} + } // namespace } // namespace net::test diff --git a/net/quic/web_transport_client.h b/net/quic/web_transport_client.h index f597a4dc902d2..7b7e187983a0f 100644 --- a/net/quic/web_transport_client.h +++ b/net/quic/web_transport_client.h @@ -106,9 +106,13 @@ struct NET_EXPORT WebTransportParameters { bool enable_web_transport_http3 = false; // A vector of fingerprints for expected server certificates, as described in - // https://wicg.github.io/web-transport/#dom-quictransportconfiguration-server_certificate_fingerprints + // https://w3c.github.io/webtransport/#dom-webtransportoptions-servercertificatehashes // When empty, Web PKI is used. std::vector<quic::CertificateFingerprint> server_certificate_fingerprints; + + // A vector of strings offered by client as a list of potential subprotocols. + // https://w3c.github.io/webtransport/#dom-webtransportoptions-protocols + std::vector<std::string> application_protocols; }; // An abstract base for a WebTransport client. Most of the useful operations