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"); }