Add support for HTTP/3 datagram SETTING The flag is marked as enabling_blocked_by until we fully support draft-ietf-masque-h3-datagram. Protected by FLAGS_quic_reloadable_flag_quic_h3_datagram. PiperOrigin-RevId: 359679117 Change-Id: Ica032348a93be9bafc75307c9d1e1e936c92cb4b
diff --git a/quic/core/http/http_constants.cc b/quic/core/http/http_constants.cc index b3262d2..d7281a3 100644 --- a/quic/core/http/http_constants.cc +++ b/quic/core/http/http_constants.cc
@@ -17,6 +17,7 @@ RETURN_STRING_LITERAL(SETTINGS_QPACK_MAX_TABLE_CAPACITY); RETURN_STRING_LITERAL(SETTINGS_MAX_FIELD_SECTION_SIZE); RETURN_STRING_LITERAL(SETTINGS_QPACK_BLOCKED_STREAMS); + RETURN_STRING_LITERAL(SETTINGS_H3_DATAGRAM); } return absl::StrCat("UNSUPPORTED_SETTINGS_TYPE(", identifier, ")"); }
diff --git a/quic/core/http/http_constants.h b/quic/core/http/http_constants.h index 8c68e74..27ce56b 100644 --- a/quic/core/http/http_constants.h +++ b/quic/core/http/http_constants.h
@@ -34,6 +34,8 @@ // Same value as spdy::SETTINGS_MAX_HEADER_LIST_SIZE. SETTINGS_MAX_FIELD_SECTION_SIZE = 0x06, SETTINGS_QPACK_BLOCKED_STREAMS = 0x07, + // draft-ietf-masque-h3-datagram. + SETTINGS_H3_DATAGRAM = 0x276, }; // Returns HTTP/3 SETTINGS identifier as a string.
diff --git a/quic/core/http/quic_send_control_stream_test.cc b/quic/core/http/quic_send_control_stream_test.cc index fe84973..d1be0df 100644 --- a/quic/core/http/quic_send_control_stream_test.cc +++ b/quic/core/http/quic_send_control_stream_test.cc
@@ -14,6 +14,7 @@ #include "quic/test_tools/quic_spdy_session_peer.h" #include "quic/test_tools/quic_test_utils.h" #include "common/platform/api/quiche_text_utils.h" +#include "common/test_tools/quiche_test_utils.h" namespace quic { namespace test { @@ -133,6 +134,25 @@ "4040" // 0x40 as the reserved frame type "01" // 1 byte frame length "61"); // payload "a" + if (GetQuicReloadableFlag(quic_h3_datagram)) { + expected_write_data = absl::HexStringToBytes( + "00" // stream type: control stream + "04" // frame type: SETTINGS frame + "0e" // frame length + "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY + "40ff" // 255 + "06" // SETTINGS_MAX_HEADER_LIST_SIZE + "4400" // 1024 + "07" // SETTINGS_QPACK_BLOCKED_STREAMS + "10" // 16 + "4040" // 0x40 as the reserved settings id + "14" // 20 + "4276" // SETTINGS_H3_DATAGRAM + "01" // 1 + "4040" // 0x40 as the reserved frame type + "01" // 1 byte frame length + "61"); // payload "a" + } auto buffer = std::make_unique<char[]>(expected_write_data.size()); QuicDataWriter writer(expected_write_data.size(), buffer.get()); @@ -158,8 +178,9 @@ .WillOnce(Invoke(save_write_data)); send_control_stream_->MaybeSendSettingsFrame(); - EXPECT_EQ(expected_write_data, - absl::string_view(writer.data(), writer.length())); + quiche::test::CompareCharArraysWithHexError( + "settings", writer.data(), writer.length(), expected_write_data.data(), + expected_write_data.length()); } TEST_P(QuicSendControlStreamTest, WriteSettingsOnlyOnce) {
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc index 346add8..8418bb2 100644 --- a/quic/core/http/quic_spdy_session.cc +++ b/quic/core/http/quic_spdy_session.cc
@@ -569,6 +569,10 @@ qpack_maximum_blocked_streams_; settings_.values[SETTINGS_MAX_FIELD_SECTION_SIZE] = max_inbound_header_list_size_; + if (GetQuicReloadableFlag(quic_h3_datagram) && version().UsesHttp3()) { + QUIC_RELOADABLE_FLAG_COUNT(quic_h3_datagram); + settings_.values[SETTINGS_H3_DATAGRAM] = 1; + } } void QuicSpdySession::OnDecoderStreamError(QuicErrorCode error_code, @@ -1204,6 +1208,26 @@ absl::StrCat("received HTTP/2 specific setting in HTTP/3 session: ", id)); return false; + case SETTINGS_H3_DATAGRAM: { + if (!GetQuicReloadableFlag(quic_h3_datagram)) { + break; + } + QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_H3_DATAGRAM received with value " + << value; + if (!version().UsesHttp3()) { + break; + } + if (value != 0 && value != 1) { + std::string error_details = absl::StrCat( + "received SETTINGS_H3_DATAGRAM with invalid value ", value); + QUIC_PEER_BUG << ENDPOINT << error_details; + CloseConnectionWithDetails(QUIC_HTTP_RECEIVE_SPDY_SETTING, + error_details); + return false; + } + h3_datagram_supported_ = !!value; + break; + } default: QUIC_DVLOG(1) << ENDPOINT << "Unknown setting identifier " << id << " received with value " << value;
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h index ad5fa02..7e13afa 100644 --- a/quic/core/http/quic_spdy_session.h +++ b/quic/core/http/quic_spdy_session.h
@@ -420,6 +420,10 @@ // Generates a new HTTP/3 datagram flow ID. QuicDatagramFlowId GetNextDatagramFlowId(); + // Whether HTTP/3 datagrams are supported on this session, based on received + // SETTINGS. + bool h3_datagram_supported() const { return h3_datagram_supported_; } + protected: // Override CreateIncomingStream(), CreateOutgoingBidirectionalStream() and // CreateOutgoingUnidirectionalStream() with QuicSpdyStream return type to @@ -629,6 +633,9 @@ // Value of the smallest unused HTTP/3 datagram flow ID that this endpoint's // datagram flow ID allocation service will use next. QuicDatagramFlowId next_available_datagram_flow_id_; + + // Whether both this endpoint and our peer support HTTP/3 datagrams. + bool h3_datagram_supported_ = false; }; } // namespace quic
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc index d173917..848f215 100644 --- a/quic/core/http/quic_spdy_session_test.cc +++ b/quic/core/http/quic_spdy_session_test.cc
@@ -3397,6 +3397,29 @@ EXPECT_EQ(session_.GetNextDatagramFlowId(), 7u); } +TEST_P(QuicSpdySessionTestClient, H3DatagramSetting) { + if (!version().UsesHttp3()) { + return; + } + SetQuicReloadableFlag(quic_h3_datagram, true); + // HTTP/3 datagrams aren't supported before SETTINGS are received. + EXPECT_FALSE(session_.h3_datagram_supported()); + // Receive SETTINGS. + SettingsFrame settings; + settings.values[SETTINGS_H3_DATAGRAM] = 1; + std::string data = std::string(1, kControlStream) + EncodeSettings(settings); + QuicStreamId stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3); + QuicStreamFrame frame(stream_id, /*fin=*/false, /*offset=*/0, data); + StrictMock<MockHttp3DebugVisitor> debug_visitor; + session_.set_debug_visitor(&debug_visitor); + EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(stream_id)); + EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(settings)); + session_.OnStreamFrame(frame); + // HTTP/3 datagrams are now supported. + EXPECT_TRUE(session_.h3_datagram_supported()); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h index a3bc225..a9fa0cf 100644 --- a/quic/core/quic_flags_list.h +++ b/quic/core/quic_flags_list.h
@@ -46,6 +46,7 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_willing_and_able_to_write2, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_goaway_with_max_stream_id, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_h3_datagram, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_parse_accept_ch_frame, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_require_handshake_confirmation, false)
diff --git a/quic/masque/masque_client_bin.cc b/quic/masque/masque_client_bin.cc index afd991a..4d0523f 100644 --- a/quic/masque/masque_client_bin.cc +++ b/quic/masque/masque_client_bin.cc
@@ -51,6 +51,8 @@ return 1; } + SetQuicReloadableFlag(quic_h3_datagram, true); + const bool disable_certificate_verification = GetQuicFlag(FLAGS_disable_certificate_verification); QuicEpollServer epoll_server;
diff --git a/quic/masque/masque_server_bin.cc b/quic/masque/masque_server_bin.cc index a6b6225..17fee23 100644 --- a/quic/masque/masque_server_bin.cc +++ b/quic/masque/masque_server_bin.cc
@@ -50,6 +50,8 @@ return 0; } + SetQuicReloadableFlag(quic_h3_datagram, true); + quic::MasqueMode masque_mode = quic::MasqueMode::kOpen; std::string mode_string = GetQuicFlag(FLAGS_masque_mode); if (mode_string == "legacy") {