Support WebTransport negotiation in HTTP/3.

PiperOrigin-RevId: 360499528
Change-Id: I1ca3ee42d10991704994f14fb009a56b91e3e63c
diff --git a/quic/core/http/http_constants.cc b/quic/core/http/http_constants.cc
index dbf49df..f3ed523 100644
--- a/quic/core/http/http_constants.cc
+++ b/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);
+    RETURN_STRING_LITERAL(SETTINGS_WEBTRANS_DRAFT00);
   }
   return absl::StrCat("UNSUPPORTED_SETTINGS_TYPE(", identifier, ")");
 }
diff --git a/quic/core/http/http_constants.h b/quic/core/http/http_constants.h
index 9757e72..a5409b5 100644
--- a/quic/core/http/http_constants.h
+++ b/quic/core/http/http_constants.h
@@ -38,6 +38,8 @@
   SETTINGS_QPACK_BLOCKED_STREAMS = 0x07,
   // draft-ietf-masque-h3-datagram.
   SETTINGS_H3_DATAGRAM = 0x276,
+  // draft-ietf-webtrans-http3-00
+  SETTINGS_WEBTRANS_DRAFT00 = 0x2b603742,
 };
 
 // Returns HTTP/3 SETTINGS identifier as a string.
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 3bce452..42ae554 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -570,6 +570,9 @@
     QUIC_RELOADABLE_FLAG_COUNT(quic_h3_datagram);
     settings_.values[SETTINGS_H3_DATAGRAM] = 1;
   }
+  if (WillNegotiateWebTransport()) {
+    settings_.values[SETTINGS_WEBTRANS_DRAFT00] = 1;
+  }
 }
 
 void QuicSpdySession::OnDecoderStreamError(QuicErrorCode error_code,
@@ -948,6 +951,15 @@
   }
 }
 
+bool QuicSpdySession::ShouldNegotiateWebTransport() {
+  return false;
+}
+
+bool QuicSpdySession::WillNegotiateWebTransport() {
+  return GetQuicReloadableFlag(quic_h3_datagram) && version().UsesHttp3() &&
+         ShouldNegotiateWebTransport();
+}
+
 // True if there are open HTTP requests.
 bool QuicSpdySession::ShouldKeepConnectionAlive() const {
   QUICHE_DCHECK(VersionUsesHttp3(transport_version()) ||
@@ -1176,6 +1188,9 @@
         h3_datagram_supported_ = !!value;
         break;
       }
+      case SETTINGS_WEBTRANS_DRAFT00:
+        peer_supports_webtransport_ = true;
+        break;
       default:
         QUIC_DVLOG(1) << ENDPOINT << "Unknown setting identifier " << id
                       << " received with value " << value;
@@ -1738,6 +1753,11 @@
   it->second->OnHttp3Datagram(flow_id, payload);
 }
 
+bool QuicSpdySession::SupportsWebTransport() {
+  return WillNegotiateWebTransport() && h3_datagram_supported_ &&
+         peer_supports_webtransport_;
+}
+
 #undef ENDPOINT  // undef for jumbo builds
 
 }  // namespace quic
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 6a47b5d..157b035 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -448,6 +448,9 @@
   // Override from QuicSession to support HTTP/3 datagrams.
   void OnMessageReceived(absl::string_view message) override;
 
+  // Indicates whether the HTTP/3 session supports WebTransport.
+  bool SupportsWebTransport();
+
  protected:
   // Override CreateIncomingStream(), CreateOutgoingBidirectionalStream() and
   // CreateOutgoingUnidirectionalStream() with QuicSpdyStream return type to
@@ -467,6 +470,11 @@
   virtual bool ShouldCreateOutgoingBidirectionalStream() = 0;
   virtual bool ShouldCreateOutgoingUnidirectionalStream() = 0;
 
+  // Indicates whether the underlying backend can accept and process
+  // WebTransport sessions over HTTP/3.
+  virtual bool ShouldNegotiateWebTransport();
+  bool WillNegotiateWebTransport();
+
   // Returns true if there are open HTTP requests.
   bool ShouldKeepConnectionAlive() const override;
 
@@ -658,6 +666,9 @@
   // Whether both this endpoint and our peer support HTTP/3 datagrams.
   bool h3_datagram_supported_ = false;
 
+  // Whether the peer has indicated WebTransport support.
+  bool peer_supports_webtransport_ = false;
+
   absl::flat_hash_map<QuicDatagramFlowId, Http3DatagramVisitor*>
       h3_datagram_registrations_;
 };
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 2cf98ab..7e34ac4 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -353,6 +353,9 @@
                       GetEncryptionLevelToSendApplicationData());
   }
 
+  bool ShouldNegotiateWebTransport() override { return supports_webtransport_; }
+  void set_supports_webtransport(bool value) { supports_webtransport_ = value; }
+
   MOCK_METHOD(void, OnAcceptChFrame, (const AcceptChFrame&), (override));
 
   using QuicSession::closed_streams;
@@ -364,6 +367,7 @@
   StrictMock<TestCryptoStream> crypto_stream_;
 
   bool writev_consumes_all_data_;
+  bool supports_webtransport_ = false;
 };
 
 class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> {
@@ -3388,6 +3392,36 @@
             MESSAGE_STATUS_SUCCESS);
 }
 
+TEST_P(QuicSpdySessionTestClient, WebTransportSetting) {
+  if (!version().UsesHttp3()) {
+    return;
+  }
+  SetQuicReloadableFlag(quic_h3_datagram, true);
+  session_.set_supports_webtransport(true);
+
+  EXPECT_FALSE(session_.SupportsWebTransport());
+
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  // Note that this does not actually fill out correct settings because the
+  // settings are filled in at the construction time.
+  EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_));
+  session_.set_debug_visitor(&debug_visitor);
+  CompleteHandshake();
+
+  SettingsFrame server_settings;
+  server_settings.values[SETTINGS_H3_DATAGRAM] = 1;
+  server_settings.values[SETTINGS_WEBTRANS_DRAFT00] = 1;
+  std::string data =
+      std::string(1, kControlStream) + EncodeSettings(server_settings);
+  QuicStreamId stream_id =
+      GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3);
+  QuicStreamFrame frame(stream_id, /*fin=*/false, /*offset=*/0, data);
+  EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(stream_id));
+  EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(server_settings));
+  session_.OnStreamFrame(frame);
+  EXPECT_TRUE(session_.SupportsWebTransport());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic