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++;