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