0

Implement subprotocol negotiation in WebTransport.

This implements the //net part; the //services/network will follow.

Change-Id: I87ecaf1231cc7140b51e7f3d5f19fc24ec12b8f9
Bug: 416080492
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6512202
Commit-Queue: Victor Vasiliev <vasilvv@chromium.org>
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1456804}
This commit is contained in:
Victor Vasiliev
2025-05-07 01:33:39 -07:00
committed by Chromium LUCI CQ
parent ce41c97e34
commit 435ec1039c
5 changed files with 57 additions and 4 deletions

@ -967,6 +967,7 @@ _BANNED_CPP_FUNCTIONS: Sequence[BanRule] = (
r'android_webview/browser/ip_protection/.*', r'android_webview/browser/ip_protection/.*',
r'chrome/browser/ip_protection/.*', r'chrome/browser/ip_protection/.*',
r'components/ip_protection/.*', r'components/ip_protection/.*',
r'net/quic/dedicated_web_transport_http3_client\.cc',
# Needed to use MediaPipe API. # Needed to use MediaPipe API.
r'components/media_effects/.*\.cc', r'components/media_effects/.*\.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_connection.h"
#include "net/third_party/quiche/src/quiche/quic/core/quic_types.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/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 "net/url_request/url_request_context.h"
#include "url/scheme_host_port.h" #include "url/scheme_host_port.h"
@ -365,6 +366,7 @@ DedicatedWebTransportHttp3Client::DedicatedWebTransportHttp3Client(
: url_(url), : url_(url),
origin_(origin), origin_(origin),
anonymization_key_(anonymization_key), anonymization_key_(anonymization_key),
application_protocols_(parameters.application_protocols),
context_(context), context_(context),
visitor_(visitor), visitor_(visitor),
quic_context_(context->quic_context()), quic_context_(context->quic_context()),
@ -748,6 +750,14 @@ int DedicatedWebTransportHttp3Client::DoSendRequest() {
headers[":protocol"] = "webtransport"; headers[":protocol"] = "webtransport";
headers["sec-webtransport-http3-draft02"] = "1"; headers["sec-webtransport-http3-draft02"] = "1";
headers["origin"] = origin_.Serialize(); 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); stream->WriteHeaders(std::move(headers), /*fin=*/false, nullptr);
web_transport_session_ = stream->web_transport(); web_transport_session_ = stream->web_transport();

@ -150,6 +150,7 @@ class NET_EXPORT DedicatedWebTransportHttp3Client
const GURL url_; const GURL url_;
const url::Origin origin_; const url::Origin origin_;
const NetworkAnonymizationKey anonymization_key_; const NetworkAnonymizationKey anonymization_key_;
const std::vector<std::string> application_protocols_;
const raw_ptr<URLRequestContext> context_; // Unowned. const raw_ptr<URLRequestContext> context_; // Unowned.
const raw_ptr<WebTransportClientVisitor> visitor_; // Unowned. const raw_ptr<WebTransportClientVisitor> visitor_; // Unowned.

@ -210,7 +210,7 @@ TEST_F(DedicatedWebTransportHttp3Test, Connect) {
GetURL("/echo"), origin_, &visitor_, anonymization_key_, context_.get(), GetURL("/echo"), origin_, &visitor_, anonymization_key_, context_.get(),
WebTransportParameters()); WebTransportParameters());
EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning()); EXPECT_CALL(visitor_, OnConnected).WillOnce(StopRunning());
client_->Connect(); client_->Connect();
Run(); Run();
ASSERT_TRUE(client_->session() != nullptr); ASSERT_TRUE(client_->session() != nullptr);
@ -251,7 +251,7 @@ TEST_F(DedicatedWebTransportHttp3Test, MAYBE_CloseTimeout) {
GetURL("/echo"), origin_, &visitor_, anonymization_key_, context_.get(), GetURL("/echo"), origin_, &visitor_, anonymization_key_, context_.get(),
WebTransportParameters()); WebTransportParameters());
EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning()); EXPECT_CALL(visitor_, OnConnected).WillOnce(StopRunning());
client_->Connect(); client_->Connect();
Run(); Run();
ASSERT_TRUE(client_->session() != nullptr); ASSERT_TRUE(client_->session() != nullptr);
@ -277,7 +277,7 @@ TEST_F(DedicatedWebTransportHttp3Test, CloseReason) {
GetURL("/session-close"), origin_, &visitor_, anonymization_key_, GetURL("/session-close"), origin_, &visitor_, anonymization_key_,
context_.get(), WebTransportParameters()); context_.get(), WebTransportParameters());
EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning()); EXPECT_CALL(visitor_, OnConnected).WillOnce(StopRunning());
client_->Connect(); client_->Connect();
Run(); Run();
ASSERT_TRUE(client_->session() != nullptr); ASSERT_TRUE(client_->session() != nullptr);
@ -296,5 +296,42 @@ TEST_F(DedicatedWebTransportHttp3Test, CloseReason) {
EXPECT_THAT(received_close_info, Optional(close_info)); 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
} // namespace net::test } // namespace net::test

@ -106,9 +106,13 @@ struct NET_EXPORT WebTransportParameters {
bool enable_web_transport_http3 = false; bool enable_web_transport_http3 = false;
// A vector of fingerprints for expected server certificates, as described in // 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. // When empty, Web PKI is used.
std::vector<quic::CertificateFingerprint> server_certificate_fingerprints; 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 // An abstract base for a WebTransport client. Most of the useful operations