Wait for settings in MASQUE
This is required to simultaneously support multiple versions of draft-ietf-masque-h3-datagram.
This CL also fixes a bug in MASQUE with IPv4.
PiperOrigin-RevId: 379545552
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 214d1da..810297d 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -433,6 +433,9 @@
// Indicates whether the HTTP/3 session supports WebTransport.
bool SupportsWebTransport();
+ // Indicates whether both the peer and us support HTTP/3 Datagrams.
+ bool SupportsH3Datagram() { return h3_datagram_supported_; }
+
// Indicates whether the HTTP/3 session will indicate WebTransport support to
// the peer.
bool WillNegotiateWebTransport();
@@ -445,7 +448,7 @@
// until the SETTINGS are received. Only works for HTTP/3.
bool ShouldBufferRequestsUntilSettings() {
return version().UsesHttp3() && perspective() == Perspective::IS_SERVER &&
- WillNegotiateWebTransport();
+ ShouldNegotiateHttp3Datagram();
}
// Returns if the incoming bidirectional streams should process data. This is
diff --git a/quic/masque/masque_client_session.cc b/quic/masque/masque_client_session.cc
index 6eee553..31edff1 100644
--- a/quic/masque/masque_client_session.cc
+++ b/quic/masque/masque_client_session.cc
@@ -233,6 +233,19 @@
QuicSpdyClientSession::OnStreamClosed(stream_id);
}
+bool MasqueClientSession::OnSettingsFrame(const SettingsFrame& frame) {
+ if (!QuicSpdyClientSession::OnSettingsFrame(frame)) {
+ QUIC_DLOG(ERROR) << "Failed to parse received settings";
+ return false;
+ }
+ if (!SupportsH3Datagram()) {
+ QUIC_DLOG(ERROR) << "Refusing to use MASQUE without HTTP/3 Datagrams";
+ return false;
+ }
+ owner_->OnSettingsReceived();
+ return true;
+}
+
MasqueClientSession::ConnectUdpClientState::ConnectUdpClientState(
QuicSpdyClientStream* stream,
EncapsulatedClientSession* encapsulated_client_session,
diff --git a/quic/masque/masque_client_session.h b/quic/masque/masque_client_session.h
index c32a961..0b2a3dd 100644
--- a/quic/masque/masque_client_session.h
+++ b/quic/masque/masque_client_session.h
@@ -31,6 +31,9 @@
// Notifies the owner that the client connection ID is no longer in use.
virtual void UnregisterClientConnectionId(
QuicConnectionId client_connection_id) = 0;
+
+ // Notifies the owner that a settings frame has been received.
+ virtual void OnSettingsReceived() = 0;
};
// Interface meant to be implemented by encapsulated client sessions, i.e.
// the end-to-end QUIC client sessions that run inside MASQUE encapsulation.
@@ -75,6 +78,9 @@
ConnectionCloseSource source) override;
void OnStreamClosed(QuicStreamId stream_id) override;
+ // From QuicSpdySession.
+ bool OnSettingsFrame(const SettingsFrame& frame) override;
+
// Send encapsulated packet.
void SendPacket(QuicConnectionId client_connection_id,
QuicConnectionId server_connection_id,
diff --git a/quic/masque/masque_epoll_client.cc b/quic/masque/masque_epoll_client.cc
index 5ad91cf..ec47520 100644
--- a/quic/masque/masque_epoll_client.cc
+++ b/quic/masque/masque_epoll_client.cc
@@ -81,6 +81,11 @@
return nullptr;
}
+ if (!masque_client->WaitUntilSettingsReceived()) {
+ QUIC_LOG(ERROR) << "Failed to receive settings";
+ return nullptr;
+ }
+
if (masque_client->masque_mode() == MasqueMode::kLegacy) {
// Construct the legacy mode init request.
spdy::Http2HeaderBlock header_block;
@@ -114,6 +119,17 @@
return masque_client;
}
+void MasqueEpollClient::OnSettingsReceived() {
+ settings_received_ = true;
+}
+
+bool MasqueEpollClient::WaitUntilSettingsReceived() {
+ while (connected() && !settings_received_) {
+ network_helper()->RunEventLoop();
+ }
+ return connected() && settings_received_;
+}
+
void MasqueEpollClient::UnregisterClientConnectionId(
QuicConnectionId client_connection_id) {
std::string body(client_connection_id.data(), client_connection_id.length());
diff --git a/quic/masque/masque_epoll_client.h b/quic/masque/masque_epoll_client.h
index 1db161f..2f5faca 100644
--- a/quic/masque/masque_epoll_client.h
+++ b/quic/masque/masque_epoll_client.h
@@ -35,6 +35,8 @@
// Convenience accessor for the underlying connection ID.
QuicConnectionId connection_id();
+ // From MasqueClientSession::Owner.
+ void OnSettingsReceived() override;
// Send a MASQUE client connection ID unregister command to the server.
void UnregisterClientConnectionId(
QuicConnectionId client_connection_id) override;
@@ -50,12 +52,17 @@
std::unique_ptr<ProofVerifier> proof_verifier,
const std::string& authority);
+ // Wait synchronously until we receive the peer's settings. Returns whether
+ // they were received.
+ bool WaitUntilSettingsReceived();
+
// Disallow copy and assign.
MasqueEpollClient(const MasqueEpollClient&) = delete;
MasqueEpollClient& operator=(const MasqueEpollClient&) = delete;
MasqueMode masque_mode_;
std::string authority_;
+ bool settings_received_ = false;
};
} // namespace quic
diff --git a/quic/masque/masque_server_session.cc b/quic/masque/masque_server_session.cc
index 8a531ca..3c14fca 100644
--- a/quic/masque/masque_server_session.cc
+++ b/quic/masque/masque_server_session.cc
@@ -255,9 +255,12 @@
QUIC_DLOG(ERROR) << "Socket creation failed";
return CreateBackendErrorResponse("500", "Socket creation failed");
}
- QuicSocketAddress any_v6_address(QuicIpAddress::Any6(), 0);
+ QuicSocketAddress empty_address(QuicIpAddress::Any6(), 0);
+ if (target_server_address.host().IsIPv4()) {
+ empty_address = QuicSocketAddress(QuicIpAddress::Any4(), 0);
+ }
QuicUdpSocketApi socket_api;
- if (!socket_api.Bind(fd_wrapper.fd(), any_v6_address)) {
+ if (!socket_api.Bind(fd_wrapper.fd(), empty_address)) {
QUIC_DLOG(ERROR) << "Socket bind failed";
return CreateBackendErrorResponse("500", "Socket bind failed");
}