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") {