Insert a greased frame after a HTTP/3 SETTINGS frame is sent. gfe-relnote: protected by disabled v99 flag. PiperOrigin-RevId: 293253018 Change-Id: I4c1090bb2f2f673b800bbff2cb7ba3b439731466
diff --git a/quic/core/http/http_encoder.cc b/quic/core/http/http_encoder.cc index 00bb980..5aeb44c 100644 --- a/quic/core/http/http_encoder.cc +++ b/quic/core/http/http_encoder.cc
@@ -3,8 +3,12 @@ // found in the LICENSE file. #include "net/third_party/quiche/src/quic/core/http/http_encoder.h" +#include <cstdint> +#include <memory> +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" namespace quic { @@ -241,4 +245,39 @@ return 0; } +// static +QuicByteCount HttpEncoder::SerializeGreasingFrame( + std::unique_ptr<char[]>* output) { + // To not congest the network, a greasing frame only contains a uint8_t as + // payload. + uint64_t frame_type; + uint8_t frame_payload; + if (!GetQuicFlag(FLAGS_quic_enable_http3_grease_randomness)) { + frame_type = 0x40; + frame_payload = 20; + } else { + uint32_t result; + QuicRandom::GetInstance()->RandBytes(&result, sizeof(result)); + frame_type = 0x1fULL * static_cast<uint64_t>(result) + 0x21ULL; + QuicRandom::GetInstance()->RandBytes(&frame_payload, sizeof(frame_payload)); + } + QuicByteCount total_length = + QuicDataWriter::GetVarInt62Len(frame_type) + + QuicDataWriter::GetVarInt62Len(sizeof(frame_payload)) + + sizeof(frame_payload); + + output->reset(new char[total_length]); + QuicDataWriter writer(total_length, output->get()); + + if (writer.WriteVarInt62(frame_type) && + writer.WriteVarInt62(sizeof(frame_payload)) && + writer.WriteUInt8(frame_payload)) { + return total_length; + } + + QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " + "greasing frame."; + return 0; +} + } // namespace quic
diff --git a/quic/core/http/http_encoder.h b/quic/core/http/http_encoder.h index ce58408..5a835c2 100644 --- a/quic/core/http/http_encoder.h +++ b/quic/core/http/http_encoder.h
@@ -5,6 +5,7 @@ #ifndef QUICHE_QUIC_CORE_HTTP_HTTP_ENCODER_H_ #define QUICHE_QUIC_CORE_HTTP_HTTP_ENCODER_H_ +#include <memory> #include "net/third_party/quiche/src/quic/core/http/http_frames.h" #include "net/third_party/quiche/src/quic/core/quic_error_codes.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" @@ -71,6 +72,10 @@ static QuicByteCount SerializePriorityUpdateFrame( const PriorityUpdateFrame& priority_update, std::unique_ptr<char[]>* output); + + // Serializes a frame with reserved frame type specified in + // https://tools.ietf.org/html/draft-ietf-quic-http-25#section-7.2.9. + static QuicByteCount SerializeGreasingFrame(std::unique_ptr<char[]>* output); }; } // namespace quic
diff --git a/quic/core/http/quic_send_control_stream.cc b/quic/core/http/quic_send_control_stream.cc index f7adc70..79ea11b 100644 --- a/quic/core/http/quic_send_control_stream.cc +++ b/quic/core/http/quic_send_control_stream.cc
@@ -62,7 +62,7 @@ // https://tools.ietf.org/html/draft-ietf-quic-http-25#section-7.2.4.1 // specifies that setting identifiers of 0x1f * N + 0x21 are reserved and // greasing should be attempted. - if (GetQuicFlag(FLAGS_quic_disable_http3_settings_grease_randomness)) { + if (!GetQuicFlag(FLAGS_quic_enable_http3_grease_randomness)) { settings.values[0x40] = 20; } else { uint32_t result; @@ -84,6 +84,14 @@ WriteOrBufferData(quiche::QuicheStringPiece(buffer.get(), frame_length), /*fin = */ false, nullptr); settings_sent_ = true; + + // https://tools.ietf.org/html/draft-ietf-quic-http-25#section-7.2.9 + // specifies that a reserved frame type has no semantic meaning and should be + // discarded. A greasing frame is added here. + std::unique_ptr<char[]> grease; + QuicByteCount grease_length = HttpEncoder::SerializeGreasingFrame(&grease); + WriteOrBufferData(quiche::QuicheStringPiece(grease.get(), grease_length), + /*fin = */ false, nullptr); } void QuicSendControlStream::WritePriorityUpdate(
diff --git a/quic/core/http/quic_send_control_stream_test.cc b/quic/core/http/quic_send_control_stream_test.cc index 507ccdc..66fcbcd 100644 --- a/quic/core/http/quic_send_control_stream_test.cc +++ b/quic/core/http/quic_send_control_stream_test.cc
@@ -103,7 +103,7 @@ ::testing::PrintToStringParamName()); TEST_P(QuicSendControlStreamTest, WriteSettings) { - SetQuicFlag(FLAGS_quic_disable_http3_settings_grease_randomness, true); + SetQuicFlag(FLAGS_quic_enable_http3_grease_randomness, false); session_.set_qpack_maximum_dynamic_table_capacity(255); session_.set_qpack_maximum_blocked_streams(16); session_.set_max_inbound_header_list_size(1024); @@ -122,6 +122,9 @@ "07" // SETTINGS_QPACK_BLOCKED_STREAMS "10" // 16 "4040" // 0x40 as the reserved settings id + "14" // 20 + "4040" // 0x40 as the reserved frame type + "01" // 8 bytes for uint8_t "14"); // 20 auto buffer = std::make_unique<char[]>(expected_write_data.size()); @@ -140,7 +143,9 @@ EXPECT_CALL(session_, WritevData(send_control_stream_, _, 1, _, _)) .WillOnce(Invoke(save_write_data)); EXPECT_CALL(session_, WritevData(send_control_stream_, _, - expected_write_data.size() - 1, _, _)) + expected_write_data.size() - 5, _, _)) + .WillOnce(Invoke(save_write_data)); + EXPECT_CALL(session_, WritevData(send_control_stream_, _, 4, _, _)) .WillOnce(Invoke(save_write_data)); send_control_stream_->MaybeSendSettingsFrame(); @@ -153,7 +158,7 @@ testing::InSequence s; EXPECT_CALL(session_, WritevData(send_control_stream_, _, 1, _, _)); - EXPECT_CALL(session_, WritevData(send_control_stream_, _, _, _, _)); + EXPECT_CALL(session_, WritevData(send_control_stream_, _, _, _, _)).Times(2); send_control_stream_->MaybeSendSettingsFrame(); // No data should be written the second time MaybeSendSettingsFrame() is @@ -166,9 +171,9 @@ Initialize(); testing::InSequence s; - // The first write will trigger the control stream to write stream type and a - // SETTINGS frame before the PRIORITY_UPDATE frame. - EXPECT_CALL(session_, WritevData(send_control_stream_, _, _, _, _)).Times(3); + // The first write will trigger the control stream to write stream type, a + // SETTINGS frame, and a greased frame before the PRIORITY_UPDATE frame. + EXPECT_CALL(session_, WritevData(send_control_stream_, _, _, _, _)).Times(4); PriorityUpdateFrame frame; send_control_stream_->WritePriorityUpdate(frame);
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc index dd08b20..aae249c 100644 --- a/quic/core/http/quic_spdy_stream_test.cc +++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -227,8 +227,9 @@ EXPECT_CALL(*connection_, OnCanWrite()); } if (UsesHttp3()) { - // The control stream will write the stream type and SETTINGS frame. - int num_control_stream_writes = 2; + // The control stream will write the stream type, a greased frame, and + // SETTINGS frame. + int num_control_stream_writes = 3; if (session_->perspective() == Perspective::IS_CLIENT) { // The control stream also writes the max push id frame. num_control_stream_writes++;