Support draft-ietf-masque-h3-datagram-09

This CL adds support for HTTP Datagrams draft-09. The main difference between draft-04 and draft-09 is the removal of context IDs and their registration, but those have already been fully removed from our codebase in previous CLs. The only remaining difference is the value of the h3 setting. This CL intentionally keeps WebTransport on draft-04: we want to keep our only production deployment off of the final setting codepoint in case there are last-minute changes to the specification. HTTP Datagrams are not used in any of our production servers so we can modify this code without flag protection.

PiperOrigin-RevId: 448347052
diff --git a/quiche/quic/core/http/http_constants.cc b/quiche/quic/core/http/http_constants.cc
index 03dea06..a22620d 100644
--- a/quiche/quic/core/http/http_constants.cc
+++ b/quiche/quic/core/http/http_constants.cc
@@ -18,6 +18,7 @@
     RETURN_STRING_LITERAL(SETTINGS_MAX_FIELD_SECTION_SIZE);
     RETURN_STRING_LITERAL(SETTINGS_QPACK_BLOCKED_STREAMS);
     RETURN_STRING_LITERAL(SETTINGS_H3_DATAGRAM_DRAFT04);
+    RETURN_STRING_LITERAL(SETTINGS_H3_DATAGRAM_DRAFT09);
     RETURN_STRING_LITERAL(SETTINGS_WEBTRANS_DRAFT00);
     RETURN_STRING_LITERAL(SETTINGS_ENABLE_CONNECT_PROTOCOL);
   }
diff --git a/quiche/quic/core/http/http_constants.h b/quiche/quic/core/http/http_constants.h
index ee0a3f3..9e1a696 100644
--- a/quiche/quic/core/http/http_constants.h
+++ b/quiche/quic/core/http/http_constants.h
@@ -40,6 +40,8 @@
   SETTINGS_QPACK_BLOCKED_STREAMS = 0x07,
   // draft-ietf-masque-h3-datagram-04.
   SETTINGS_H3_DATAGRAM_DRAFT04 = 0xffd277,
+  // draft-ietf-masque-h3-datagram-09.
+  SETTINGS_H3_DATAGRAM_DRAFT09 = 0x33,
   // draft-ietf-webtrans-http3-00
   SETTINGS_WEBTRANS_DRAFT00 = 0x2b603742,
   // draft-ietf-httpbis-h3-websockets
diff --git a/quiche/quic/core/http/quic_spdy_session.cc b/quiche/quic/core/http/quic_spdy_session.cc
index 12efa66..af40f36 100644
--- a/quiche/quic/core/http/quic_spdy_session.cc
+++ b/quiche/quic/core/http/quic_spdy_session.cc
@@ -516,10 +516,19 @@
   settings_.values[SETTINGS_MAX_FIELD_SECTION_SIZE] =
       max_inbound_header_list_size_;
   if (version().UsesHttp3()) {
-    HttpDatagramSupport local_http_datagram_support =
-        LocalHttpDatagramSupport();
-    if (local_http_datagram_support == HttpDatagramSupport::kDraft04) {
-      settings_.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1;
+    switch (LocalHttpDatagramSupport()) {
+      case HttpDatagramSupport::kNone:
+        break;
+      case HttpDatagramSupport::kDraft04:
+        settings_.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1;
+        break;
+      case HttpDatagramSupport::kDraft09:
+        settings_.values[SETTINGS_H3_DATAGRAM_DRAFT09] = 1;
+        break;
+      case HttpDatagramSupport::kDraft04And09:
+        settings_.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1;
+        settings_.values[SETTINGS_H3_DATAGRAM_DRAFT09] = 1;
+        break;
     }
   }
   if (WillNegotiateWebTransport()) {
@@ -1139,7 +1148,8 @@
       case SETTINGS_H3_DATAGRAM_DRAFT04: {
         HttpDatagramSupport local_http_datagram_support =
             LocalHttpDatagramSupport();
-        if (local_http_datagram_support != HttpDatagramSupport::kDraft04) {
+        if (local_http_datagram_support != HttpDatagramSupport::kDraft04 &&
+            local_http_datagram_support != HttpDatagramSupport::kDraft04And09) {
           break;
         }
         QUIC_DVLOG(1) << ENDPOINT
@@ -1151,11 +1161,33 @@
         if (!VerifySettingIsZeroOrOne(id, value)) {
           return false;
         }
-        if (value) {
+        if (value && http_datagram_support_ != HttpDatagramSupport::kDraft09) {
+          // If both draft-04 and draft-09 are supported, use draft-09.
           http_datagram_support_ = HttpDatagramSupport::kDraft04;
         }
         break;
       }
+      case SETTINGS_H3_DATAGRAM_DRAFT09: {
+        HttpDatagramSupport local_http_datagram_support =
+            LocalHttpDatagramSupport();
+        if (local_http_datagram_support != HttpDatagramSupport::kDraft09 &&
+            local_http_datagram_support != HttpDatagramSupport::kDraft04And09) {
+          break;
+        }
+        QUIC_DVLOG(1) << ENDPOINT
+                      << "SETTINGS_H3_DATAGRAM_DRAFT09 received with value "
+                      << value;
+        if (!version().UsesHttp3()) {
+          break;
+        }
+        if (!VerifySettingIsZeroOrOne(id, value)) {
+          return false;
+        }
+        if (value) {
+          http_datagram_support_ = HttpDatagramSupport::kDraft09;
+        }
+        break;
+      }
       case SETTINGS_WEBTRANS_DRAFT00:
         if (!WillNegotiateWebTransport()) {
           break;
@@ -1790,6 +1822,10 @@
       return "None";
     case HttpDatagramSupport::kDraft04:
       return "Draft04";
+    case HttpDatagramSupport::kDraft09:
+      return "Draft09";
+    case HttpDatagramSupport::kDraft04And09:
+      return "Draft04And09";
   }
   return absl::StrCat("Unknown(", static_cast<int>(http_datagram_support), ")");
 }
diff --git a/quiche/quic/core/http/quic_spdy_session.h b/quiche/quic/core/http/quic_spdy_session.h
index a6418b9..25d3be0 100644
--- a/quiche/quic/core/http/quic_spdy_session.h
+++ b/quiche/quic/core/http/quic_spdy_session.h
@@ -120,6 +120,8 @@
 enum class HttpDatagramSupport : uint8_t {
   kNone,  // HTTP Datagrams are not supported for this session.
   kDraft04,
+  kDraft09,
+  kDraft04And09,  // Only used locally for sending, we only negotiate one draft.
 };
 
 QUIC_EXPORT_PRIVATE std::string HttpDatagramSupportToString(
diff --git a/quiche/quic/core/http/quic_spdy_session_test.cc b/quiche/quic/core/http/quic_spdy_session_test.cc
index 84b50c7..f854763 100644
--- a/quiche/quic/core/http/quic_spdy_session_test.cc
+++ b/quiche/quic/core/http/quic_spdy_session_test.cc
@@ -3388,6 +3388,13 @@
     case HttpDatagramSupport::kDraft04:
       settings.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1;
       break;
+    case HttpDatagramSupport::kDraft09:
+      settings.values[SETTINGS_H3_DATAGRAM_DRAFT09] = 1;
+      break;
+    case HttpDatagramSupport::kDraft04And09:
+      settings.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1;
+      settings.values[SETTINGS_H3_DATAGRAM_DRAFT09] = 1;
+      break;
   }
   std::string data = std::string(1, kControlStream) + EncodeSettings(settings);
   QuicStreamId stream_id =
@@ -3410,6 +3417,70 @@
       /*expected_datagram_supported=*/true);
 }
 
+TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal04Remote09) {
+  TestHttpDatagramSetting(
+      /*local_support=*/HttpDatagramSupport::kDraft04,
+      /*remote_support=*/HttpDatagramSupport::kDraft09,
+      /*expected_support=*/HttpDatagramSupport::kNone,
+      /*expected_datagram_supported=*/false);
+}
+
+TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal04Remote04And09) {
+  TestHttpDatagramSetting(
+      /*local_support=*/HttpDatagramSupport::kDraft04,
+      /*remote_support=*/HttpDatagramSupport::kDraft04And09,
+      /*expected_support=*/HttpDatagramSupport::kDraft04,
+      /*expected_datagram_supported=*/true);
+}
+
+TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal09Remote04) {
+  TestHttpDatagramSetting(
+      /*local_support=*/HttpDatagramSupport::kDraft09,
+      /*remote_support=*/HttpDatagramSupport::kDraft04,
+      /*expected_support=*/HttpDatagramSupport::kNone,
+      /*expected_datagram_supported=*/false);
+}
+
+TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal09Remote09) {
+  TestHttpDatagramSetting(
+      /*local_support=*/HttpDatagramSupport::kDraft09,
+      /*remote_support=*/HttpDatagramSupport::kDraft09,
+      /*expected_support=*/HttpDatagramSupport::kDraft09,
+      /*expected_datagram_supported=*/true);
+}
+
+TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal09Remote04And09) {
+  TestHttpDatagramSetting(
+      /*local_support=*/HttpDatagramSupport::kDraft09,
+      /*remote_support=*/HttpDatagramSupport::kDraft04And09,
+      /*expected_support=*/HttpDatagramSupport::kDraft09,
+      /*expected_datagram_supported=*/true);
+}
+
+TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal04And09Remote04) {
+  TestHttpDatagramSetting(
+      /*local_support=*/HttpDatagramSupport::kDraft04And09,
+      /*remote_support=*/HttpDatagramSupport::kDraft04,
+      /*expected_support=*/HttpDatagramSupport::kDraft04,
+      /*expected_datagram_supported=*/true);
+}
+
+TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal04And09Remote09) {
+  TestHttpDatagramSetting(
+      /*local_support=*/HttpDatagramSupport::kDraft04And09,
+      /*remote_support=*/HttpDatagramSupport::kDraft09,
+      /*expected_support=*/HttpDatagramSupport::kDraft09,
+      /*expected_datagram_supported=*/true);
+}
+
+TEST_P(QuicSpdySessionTestClient,
+       HttpDatagramSettingLocal04And09Remote04And09) {
+  TestHttpDatagramSetting(
+      /*local_support=*/HttpDatagramSupport::kDraft04And09,
+      /*remote_support=*/HttpDatagramSupport::kDraft04And09,
+      /*expected_support=*/HttpDatagramSupport::kDraft09,
+      /*expected_datagram_supported=*/true);
+}
 TEST_P(QuicSpdySessionTestClient, WebTransportSetting) {
   if (!version().UsesHttp3()) {
     return;
diff --git a/quiche/quic/core/http/quic_spdy_stream.cc b/quiche/quic/core/http/quic_spdy_stream.cc
index 4064d28..10d76af 100644
--- a/quiche/quic/core/http/quic_spdy_stream.cc
+++ b/quiche/quic/core/http/quic_spdy_stream.cc
@@ -1511,10 +1511,12 @@
   QuicByteCount prefix_size = 0;
   switch (spdy_session_->http_datagram_support()) {
     case HttpDatagramSupport::kDraft04:
+    case HttpDatagramSupport::kDraft09:
       prefix_size =
           QuicDataWriter::GetVarInt62Len(id() / kHttpDatagramStreamIdDivisor);
       break;
     case HttpDatagramSupport::kNone:
+    case HttpDatagramSupport::kDraft04And09:
       QUIC_BUG(GetMaxDatagramSize called with no datagram support)
           << "GetMaxDatagramSize() called when no HTTP/3 datagram support has "
              "been negotiated.  Support value: "
diff --git a/quiche/quic/core/http/quic_spdy_stream_test.cc b/quiche/quic/core/http/quic_spdy_stream_test.cc
index 80d9ee2..3c00c7b 100644
--- a/quiche/quic/core/http/quic_spdy_stream_test.cc
+++ b/quiche/quic/core/http/quic_spdy_stream_test.cc
@@ -3131,9 +3131,9 @@
     return;
   }
   InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT);
-  session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft04);
+  session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft09);
   QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(),
-                                              HttpDatagramSupport::kDraft04);
+                                              HttpDatagramSupport::kDraft09);
   headers_[":method"] = "CONNECT";
   headers_[":protocol"] = "webtransport";
   ProcessHeaders(false, headers_);
@@ -3171,9 +3171,9 @@
     return;
   }
   Initialize(kShouldProcessData);
-  session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft04);
+  session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft09);
   QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(),
-                                              HttpDatagramSupport::kDraft04);
+                                              HttpDatagramSupport::kDraft09);
   std::string http_datagram_payload = {1, 2, 3, 4, 5, 6};
   EXPECT_CALL(*connection_, SendMessage(1, _, false))
       .WillOnce(Return(MESSAGE_STATUS_SUCCESS));
@@ -3186,9 +3186,9 @@
     return;
   }
   Initialize(kShouldProcessData);
-  session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft04);
+  session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft09);
   QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(),
-                                              HttpDatagramSupport::kDraft04);
+                                              HttpDatagramSupport::kDraft09);
   EXPECT_GT(stream_->GetMaxDatagramSize(), 512u);
 }
 
diff --git a/quiche/quic/masque/masque_client_session.h b/quiche/quic/masque/masque_client_session.h
index 41c63a9..66c7e2e 100644
--- a/quiche/quic/masque/masque_client_session.h
+++ b/quiche/quic/masque/masque_client_session.h
@@ -125,7 +125,7 @@
   };
 
   HttpDatagramSupport LocalHttpDatagramSupport() override {
-    return HttpDatagramSupport::kDraft04;
+    return HttpDatagramSupport::kDraft09;
   }
 
   const ConnectUdpClientState* GetOrCreateConnectUdpClientState(
diff --git a/quiche/quic/masque/masque_server_session.h b/quiche/quic/masque/masque_server_session.h
index 922e710..59b33c2 100644
--- a/quiche/quic/masque/masque_server_session.h
+++ b/quiche/quic/masque/masque_server_session.h
@@ -97,7 +97,7 @@
   // From QuicSpdySession.
   bool OnSettingsFrame(const SettingsFrame& frame) override;
   HttpDatagramSupport LocalHttpDatagramSupport() override {
-    return HttpDatagramSupport::kDraft04;
+    return HttpDatagramSupport::kDraft09;
   }
 
   MasqueServerBackend* masque_server_backend_;  // Unowned.