Introduce support for basic WebTransport stats.
PiperOrigin-RevId: 564426331
diff --git a/build/source_list.bzl b/build/source_list.bzl
index 8b4383f..10603ec 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -347,6 +347,7 @@
"quic/core/uber_quic_stream_id_manager.h",
"quic/core/uber_received_packet_manager.h",
"quic/core/web_transport_interface.h",
+ "quic/core/web_transport_stats.h",
"quic/platform/api/quic_bug_tracker.h",
"quic/platform/api/quic_client_stats.h",
"quic/platform/api/quic_export.h",
@@ -659,6 +660,7 @@
"quic/core/tls_server_handshaker.cc",
"quic/core/uber_quic_stream_id_manager.cc",
"quic/core/uber_received_packet_manager.cc",
+ "quic/core/web_transport_stats.cc",
"quic/platform/api/quic_socket_address.cc",
"spdy/core/array_output_buffer.cc",
"spdy/core/hpack/hpack_constants.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index 35dfa26..2cd0ddd 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -347,6 +347,7 @@
"src/quiche/quic/core/uber_quic_stream_id_manager.h",
"src/quiche/quic/core/uber_received_packet_manager.h",
"src/quiche/quic/core/web_transport_interface.h",
+ "src/quiche/quic/core/web_transport_stats.h",
"src/quiche/quic/platform/api/quic_bug_tracker.h",
"src/quiche/quic/platform/api/quic_client_stats.h",
"src/quiche/quic/platform/api/quic_export.h",
@@ -659,6 +660,7 @@
"src/quiche/quic/core/tls_server_handshaker.cc",
"src/quiche/quic/core/uber_quic_stream_id_manager.cc",
"src/quiche/quic/core/uber_received_packet_manager.cc",
+ "src/quiche/quic/core/web_transport_stats.cc",
"src/quiche/quic/platform/api/quic_socket_address.cc",
"src/quiche/spdy/core/array_output_buffer.cc",
"src/quiche/spdy/core/hpack/hpack_constants.cc",
diff --git a/build/source_list.json b/build/source_list.json
index 22ece80..f41f490 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -346,6 +346,7 @@
"quiche/quic/core/uber_quic_stream_id_manager.h",
"quiche/quic/core/uber_received_packet_manager.h",
"quiche/quic/core/web_transport_interface.h",
+ "quiche/quic/core/web_transport_stats.h",
"quiche/quic/platform/api/quic_bug_tracker.h",
"quiche/quic/platform/api/quic_client_stats.h",
"quiche/quic/platform/api/quic_export.h",
@@ -658,6 +659,7 @@
"quiche/quic/core/tls_server_handshaker.cc",
"quiche/quic/core/uber_quic_stream_id_manager.cc",
"quiche/quic/core/uber_received_packet_manager.cc",
+ "quiche/quic/core/web_transport_stats.cc",
"quiche/quic/platform/api/quic_socket_address.cc",
"quiche/spdy/core/array_output_buffer.cc",
"quiche/spdy/core/hpack/hpack_constants.cc",
diff --git a/quiche/quic/core/http/web_transport_http3.h b/quiche/quic/core/http/web_transport_http3.h
index bb5444a..14b4a1c 100644
--- a/quiche/quic/core/http/web_transport_http3.h
+++ b/quiche/quic/core/http/web_transport_http3.h
@@ -17,6 +17,7 @@
#include "quiche/quic/core/quic_stream.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/web_transport_interface.h"
+#include "quiche/quic/core/web_transport_stats.h"
#include "quiche/common/platform/api/quiche_mem_slice.h"
#include "quiche/common/quiche_callbacks.h"
#include "quiche/web_transport/web_transport.h"
@@ -90,6 +91,13 @@
QuicByteCount GetMaxDatagramSize() const override;
void SetDatagramMaxTimeInQueue(absl::Duration max_time_in_queue) override;
+ webtransport::DatagramStats GetDatagramStats() override {
+ return WebTransportDatagramStatsForQuicSession(*session_);
+ }
+ webtransport::SessionStats GetSessionStats() override {
+ return WebTransportStatsForQuicSession(*session_);
+ }
+
void NotifySessionDraining() override;
void SetOnDraining(quiche::SingleUseCallback<void()> callback) override {
drain_callback_ = std::move(callback);
diff --git a/quiche/quic/core/quic_datagram_queue.cc b/quiche/quic/core/quic_datagram_queue.cc
index 9796587..0b7b3f5 100644
--- a/quiche/quic/core/quic_datagram_queue.cc
+++ b/quiche/quic/core/quic_datagram_queue.cc
@@ -22,8 +22,7 @@
std::unique_ptr<Observer> observer)
: session_(session),
clock_(session->connection()->clock()),
- observer_(std::move(observer)),
- force_flush_(false) {}
+ observer_(std::move(observer)) {}
MessageStatus QuicDatagramQueue::SendOrQueueDatagram(
quiche::QuicheMemSlice datagram) {
@@ -92,6 +91,7 @@
void QuicDatagramQueue::RemoveExpiredDatagrams() {
QuicTime now = clock_->ApproximateNow();
while (!queue_.empty() && queue_.front().expiry <= now) {
+ ++expired_datagram_count_;
queue_.pop_front();
if (observer_) {
observer_->OnDatagramProcessed(absl::nullopt);
diff --git a/quiche/quic/core/quic_datagram_queue.h b/quiche/quic/core/quic_datagram_queue.h
index 834ae54..ca79265 100644
--- a/quiche/quic/core/quic_datagram_queue.h
+++ b/quiche/quic/core/quic_datagram_queue.h
@@ -5,6 +5,7 @@
#ifndef QUICHE_QUIC_CORE_QUIC_DATAGRAM_QUEUE_H_
#define QUICHE_QUIC_CORE_QUIC_DATAGRAM_QUEUE_H_
+#include <cstdint>
#include <memory>
#include "absl/types/optional.h"
@@ -69,8 +70,8 @@
void SetForceFlush(bool force_flush) { force_flush_ = force_flush; }
size_t queue_size() { return queue_.size(); }
-
bool empty() { return queue_.empty(); }
+ uint64_t expired_datagram_count() const { return expired_datagram_count_; }
private:
struct QUICHE_EXPORT Datagram {
@@ -87,7 +88,8 @@
QuicTime::Delta max_time_in_queue_ = QuicTime::Delta::Zero();
quiche::QuicheCircularDeque<Datagram> queue_;
std::unique_ptr<Observer> observer_;
- bool force_flush_;
+ uint64_t expired_datagram_count_ = 0;
+ bool force_flush_ = false;
};
} // namespace quic
diff --git a/quiche/quic/core/quic_generic_session.h b/quiche/quic/core/quic_generic_session.h
index 5588320..773f170 100644
--- a/quiche/quic/core/quic_generic_session.h
+++ b/quiche/quic/core/quic_generic_session.h
@@ -23,6 +23,7 @@
#include "quiche/quic/core/quic_stream.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_versions.h"
+#include "quiche/quic/core/web_transport_stats.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
#include "quiche/common/quiche_callbacks.h"
#include "quiche/web_transport/web_transport.h"
@@ -95,6 +96,12 @@
void SetDatagramMaxTimeInQueue(absl::Duration max_time_in_queue) override {
datagram_queue()->SetMaxTimeInQueue(QuicTimeDelta(max_time_in_queue));
}
+ webtransport::DatagramStats GetDatagramStats() override {
+ return WebTransportDatagramStatsForQuicSession(*this);
+ }
+ webtransport::SessionStats GetSessionStats() override {
+ return WebTransportStatsForQuicSession(*this);
+ }
void NotifySessionDraining() override {}
void SetOnDraining(quiche::SingleUseCallback<void()>) override {}
diff --git a/quiche/quic/core/quic_generic_session_test.cc b/quiche/quic/core/quic_generic_session_test.cc
index fced0ec..87c9176 100644
--- a/quiche/quic/core/quic_generic_session_test.cc
+++ b/quiche/quic/core/quic_generic_session_test.cc
@@ -7,16 +7,24 @@
#include "quiche/quic/core/quic_generic_session.h"
+#include <cstddef>
+#include <cstring>
#include <memory>
+#include <string>
#include <vector>
#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "quiche/quic/core/crypto/quic_compressed_certs_cache.h"
#include "quiche/quic/core/crypto/quic_crypto_client_config.h"
#include "quiche/quic/core/crypto/quic_crypto_server_config.h"
+#include "quiche/quic/core/crypto/quic_random.h"
#include "quiche/quic/core/quic_connection.h"
+#include "quiche/quic/core/quic_constants.h"
#include "quiche/quic/core/quic_datagram_queue.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/core/web_transport_interface.h"
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/test_tools/crypto_test_utils.h"
#include "quiche/quic/test_tools/quic_session_peer.h"
@@ -321,5 +329,67 @@
EXPECT_TRUE(client_->session()->CanOpenNextOutgoingUnidirectionalStream());
}
+TEST_F(QuicGenericSessionTest, ExpireDatagrams) {
+ CreateDefaultEndpoints(kEchoServer);
+ WireUpEndpoints();
+ RunHandshake();
+
+ // Set the datagrams to expire very soon.
+ client_->session()->SetDatagramMaxTimeInQueue(
+ (0.2 * simulator::TestHarness::kRtt).ToAbsl());
+ for (int i = 0; i < 1000; i++) {
+ client_->session()->SendOrQueueDatagram(std::string(
+ client_->session()->GetGuaranteedLargestMessagePayload(), 'a'));
+ }
+
+ size_t received = 0;
+ EXPECT_CALL(*client_->visitor(), OnDatagramReceived(_))
+ .WillRepeatedly(
+ [&received](absl::string_view /*datagram*/) { received++; });
+ ASSERT_TRUE(test_harness_.simulator().RunUntilOrTimeout(
+ [this]() { return client_->total_datagrams_processed() >= 1000; },
+ 3 * simulator::TestHarness::kServerBandwidth.TransferTime(
+ 1000 * kMaxOutgoingPacketSize)));
+ // Allow extra round-trips for the final flight of datagrams to arrive back.
+ test_harness_.simulator().RunFor(2 * simulator::TestHarness::kRtt);
+ EXPECT_LT(received, 500);
+ EXPECT_EQ(received + client_->session()->GetDatagramStats().expired_outgoing,
+ 1000);
+}
+
+TEST_F(QuicGenericSessionTest, LoseDatagrams) {
+ CreateDefaultEndpoints(kEchoServer);
+ test_harness_.WireUpEndpointsWithLoss(/*lose_every_n=*/4);
+ RunHandshake();
+
+ // Set the datagrams to effectively never expire.
+ client_->session()->SetDatagramMaxTimeInQueue(
+ (10000 * simulator::TestHarness::kRtt).ToAbsl());
+ for (int i = 0; i < 1000; i++) {
+ client_->session()->SendOrQueueDatagram(std::string(
+ client_->session()->GetGuaranteedLargestMessagePayload(), 'a'));
+ }
+
+ size_t received = 0;
+ EXPECT_CALL(*client_->visitor(), OnDatagramReceived(_))
+ .WillRepeatedly(
+ [&received](absl::string_view /*datagram*/) { received++; });
+ ASSERT_TRUE(test_harness_.simulator().RunUntilOrTimeout(
+ [this]() { return client_->total_datagrams_processed() >= 1000; },
+ 3 * simulator::TestHarness::kServerBandwidth.TransferTime(
+ 1000 * kMaxOutgoingPacketSize)));
+ // Allow extra round-trips for the final flight of datagrams to arrive back.
+ test_harness_.simulator().RunFor(2 * simulator::TestHarness::kRtt);
+
+ QuicPacketCount client_lost =
+ client_->session()->GetDatagramStats().lost_outgoing;
+ QuicPacketCount server_lost =
+ server_->session()->GetDatagramStats().lost_outgoing;
+ EXPECT_LT(received, 800u);
+ EXPECT_GT(client_lost, 100u);
+ EXPECT_GT(server_lost, 100u);
+ EXPECT_EQ(received + client_lost + server_lost, 1000u);
+}
+
} // namespace
} // namespace quic::test
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc
index e140bb4..f86e722 100644
--- a/quiche/quic/core/quic_session.cc
+++ b/quiche/quic/core/quic_session.cc
@@ -2310,6 +2310,7 @@
void QuicSession::OnFrameLost(const QuicFrame& frame) {
if (frame.type == MESSAGE_FRAME) {
+ ++total_datagrams_lost_;
OnMessageLost(frame.message_frame->message_id);
return;
}
diff --git a/quiche/quic/core/quic_session.h b/quiche/quic/core/quic_session.h
index 79b7396..db9475a 100644
--- a/quiche/quic/core/quic_session.h
+++ b/quiche/quic/core/quic_session.h
@@ -656,6 +656,15 @@
datagram_queue_.SetForceFlush(force_flush);
}
+ // Returns the total number of expired datagrams dropped in the default
+ // datagram queue.
+ uint64_t expired_datagrams_in_default_queue() const {
+ return datagram_queue_.expired_datagram_count();
+ }
+
+ // Returns the total datagrams ever declared lost within the session.
+ uint64_t total_datagrams_lost() const { return total_datagrams_lost_; }
+
// Find stream with |id|, returns nullptr if the stream does not exist or
// closed. static streams and zombie streams are not considered active
// streams.
@@ -1005,6 +1014,9 @@
// The buffer used to queue the DATAGRAM frames.
QuicDatagramQueue datagram_queue_;
+ // Total number of datagram frames declared lost within the session.
+ uint64_t total_datagrams_lost_ = 0;
+
// TODO(fayang): switch to linked_hash_set when chromium supports it. The bool
// is not used here.
// List of streams with pending retransmissions.
diff --git a/quiche/quic/core/web_transport_stats.cc b/quiche/quic/core/web_transport_stats.cc
new file mode 100644
index 0000000..15797b2
--- /dev/null
+++ b/quiche/quic/core/web_transport_stats.cc
@@ -0,0 +1,38 @@
+// Copyright 2023 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quiche/quic/core/web_transport_stats.h"
+
+#include "absl/time/time.h"
+#include "quiche/quic/core/congestion_control/rtt_stats.h"
+#include "quiche/quic/core/quic_session.h"
+#include "quiche/web_transport/web_transport.h"
+
+namespace quic {
+
+webtransport::DatagramStats WebTransportDatagramStatsForQuicSession(
+ const QuicSession& session) {
+ webtransport::DatagramStats result;
+ result.expired_outgoing = session.expired_datagrams_in_default_queue();
+ result.lost_outgoing = session.total_datagrams_lost();
+ return result;
+}
+
+webtransport::SessionStats WebTransportStatsForQuicSession(
+ const QuicSession& session) {
+ const RttStats* rtt_stats =
+ session.connection()->sent_packet_manager().GetRttStats();
+ webtransport::SessionStats result;
+ result.min_rtt = rtt_stats->min_rtt().ToAbsl();
+ result.smoothed_rtt = rtt_stats->smoothed_rtt().ToAbsl();
+ result.rtt_variation = rtt_stats->mean_deviation().ToAbsl();
+ result.estimated_send_rate_bps = session.connection()
+ ->sent_packet_manager()
+ .BandwidthEstimate()
+ .ToBitsPerSecond();
+ result.datagram_stats = WebTransportDatagramStatsForQuicSession(session);
+ return result;
+}
+
+} // namespace quic
diff --git a/quiche/quic/core/web_transport_stats.h b/quiche/quic/core/web_transport_stats.h
new file mode 100644
index 0000000..5e39c28
--- /dev/null
+++ b/quiche/quic/core/web_transport_stats.h
@@ -0,0 +1,22 @@
+// Copyright 2023 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_WEB_TRANSPORT_STATS_H_
+#define QUICHE_QUIC_CORE_WEB_TRANSPORT_STATS_H_
+
+#include "quiche/quic/core/quic_session.h"
+#include "quiche/common/platform/api/quiche_export.h"
+#include "quiche/web_transport/web_transport.h"
+
+namespace quic {
+
+QUICHE_EXPORT webtransport::DatagramStats
+WebTransportDatagramStatsForQuicSession(const QuicSession& session);
+
+QUICHE_EXPORT webtransport::SessionStats WebTransportStatsForQuicSession(
+ const QuicSession& session);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_WEB_TRANSPORT_STATS_H_
diff --git a/quiche/quic/test_tools/simulator/test_harness.cc b/quiche/quic/test_tools/simulator/test_harness.cc
index 1dfc8a2..1b58c2e 100644
--- a/quiche/quic/test_tools/simulator/test_harness.cc
+++ b/quiche/quic/test_tools/simulator/test_harness.cc
@@ -4,13 +4,39 @@
#include "quiche/quic/test_tools/simulator/test_harness.h"
+#include <memory>
+#include <string>
+
+#include "absl/strings/str_cat.h"
#include "quiche/quic/core/quic_connection.h"
+#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/quic/test_tools/simulator/packet_filter.h"
+#include "quiche/quic/test_tools/simulator/port.h"
#include "quiche/quic/test_tools/simulator/quic_endpoint_base.h"
+#include "quiche/quic/test_tools/simulator/simulator.h"
namespace quic::simulator {
+class LoseEveryNFilter : public PacketFilter {
+ public:
+ LoseEveryNFilter(Endpoint* input, int n)
+ : PacketFilter(input->simulator(),
+ absl::StrCat(input->name(), " (loss filter)"), input),
+ n_(n) {}
+
+ protected:
+ bool FilterPacket(const Packet& /*packet*/) {
+ ++counter_;
+ return (counter_ % n_) != 0;
+ }
+
+ private:
+ int n_;
+ int counter_ = 0;
+};
+
QuicEndpointWithConnection::QuicEndpointWithConnection(
Simulator* simulator, const std::string& name, const std::string& peer_name,
Perspective perspective, const ParsedQuicVersionVector& supported_versions)
@@ -32,4 +58,13 @@
kServerPropagationDelay);
}
+void TestHarness::WireUpEndpointsWithLoss(int lose_every_n) {
+ client_filter_ = std::make_unique<LoseEveryNFilter>(client_, lose_every_n);
+ server_filter_ = std::make_unique<LoseEveryNFilter>(server_, lose_every_n);
+ client_link_.emplace(client_filter_.get(), switch_.port(1), kClientBandwidth,
+ kClientPropagationDelay);
+ server_link_.emplace(server_filter_.get(), switch_.port(2), kServerBandwidth,
+ kServerPropagationDelay);
+}
+
} // namespace quic::simulator
diff --git a/quiche/quic/test_tools/simulator/test_harness.h b/quiche/quic/test_tools/simulator/test_harness.h
index 681cfa3..135d744 100644
--- a/quiche/quic/test_tools/simulator/test_harness.h
+++ b/quiche/quic/test_tools/simulator/test_harness.h
@@ -6,11 +6,16 @@
#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_TEST_HARNESS_H_
#include <memory>
+#include <string>
#include "absl/types/optional.h"
+#include "quiche/quic/core/quic_bandwidth.h"
#include "quiche/quic/core/quic_constants.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/test_tools/simulator/link.h"
+#include "quiche/quic/test_tools/simulator/packet_filter.h"
#include "quiche/quic/test_tools/simulator/port.h"
#include "quiche/quic/test_tools/simulator/quic_endpoint_base.h"
#include "quiche/quic/test_tools/simulator/simulator.h"
@@ -61,6 +66,9 @@
// set_client/set_server are called.
void WireUpEndpoints();
+ // Same as above, except triggers loss of every Nth packet in both directions.
+ void WireUpEndpointsWithLoss(int lose_every_n);
+
// A convenience wrapper around Simulator::RunUntilOrTimeout().
template <class TerminationPredicate>
bool RunUntilWithDefaultTimeout(TerminationPredicate termination_predicate) {
@@ -73,6 +81,8 @@
Switch switch_;
absl::optional<SymmetricLink> client_link_;
absl::optional<SymmetricLink> server_link_;
+ std::unique_ptr<PacketFilter> client_filter_;
+ std::unique_ptr<PacketFilter> server_filter_;
Endpoint* client_;
Endpoint* server_;
diff --git a/quiche/web_transport/test_tools/mock_web_transport.h b/quiche/web_transport/test_tools/mock_web_transport.h
index e102a08..8855c9c 100644
--- a/quiche/web_transport/test_tools/mock_web_transport.h
+++ b/quiche/web_transport/test_tools/mock_web_transport.h
@@ -7,8 +7,19 @@
#ifndef QUICHE_WEB_TRANSPORT_TEST_TOOLS_MOCK_WEB_TRANSPORT_H_
#define QUICHE_WEB_TRANSPORT_TEST_TOOLS_MOCK_WEB_TRANSPORT_H_
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "absl/status/status.h"
+#include "absl/strings/string_view.h"
+#include "absl/time/time.h"
+#include "absl/types/span.h"
+#include "quiche/common/platform/api/quiche_export.h"
#include "quiche/common/platform/api/quiche_test.h"
#include "quiche/common/quiche_callbacks.h"
+#include "quiche/common/quiche_stream.h"
#include "quiche/web_transport/web_transport.h"
namespace webtransport {
@@ -75,6 +86,8 @@
MOCK_METHOD(uint64_t, GetMaxDatagramSize, (), (const, override));
MOCK_METHOD(void, SetDatagramMaxTimeInQueue,
(absl::Duration max_time_in_queue), (override));
+ MOCK_METHOD(DatagramStats, GetDatagramStats, (), (override));
+ MOCK_METHOD(SessionStats, GetSessionStats, (), (override));
MOCK_METHOD(void, NotifySessionDraining, (), (override));
MOCK_METHOD(void, SetOnDraining, (quiche::SingleUseCallback<void()>),
(override));
diff --git a/quiche/web_transport/web_transport.h b/quiche/web_transport/web_transport.h
index 6cb7811..741be73 100644
--- a/quiche/web_transport/web_transport.h
+++ b/quiche/web_transport/web_transport.h
@@ -9,6 +9,7 @@
#define QUICHE_WEB_TRANSPORT_WEB_TRANSPORT_H_
#include <cstddef>
+#include <cstdint>
#include <memory>
#include <string>
@@ -63,6 +64,31 @@
kBidirectional,
};
+// Based on
+// https://w3c.github.io/webtransport/#dictdef-webtransportdatagramstats.
+struct QUICHE_EXPORT DatagramStats {
+ uint64_t expired_outgoing;
+ uint64_t lost_outgoing;
+
+ // droppedIncoming is not present, since in the C++ API, we immediately
+ // deliver datagrams via callback, meaning there is no queue where things
+ // would be dropped.
+};
+
+// Based on https://w3c.github.io/webtransport/#web-transport-stats
+// Note that this is currently not a complete implementation of that API, as
+// some of those still need to be clarified in
+// https://github.com/w3c/webtransport/issues/537
+struct QUICHE_EXPORT SessionStats {
+ absl::Duration min_rtt;
+ absl::Duration smoothed_rtt;
+ absl::Duration rtt_variation;
+
+ uint64_t estimated_send_rate_bps; // In bits per second.
+
+ DatagramStats datagram_stats;
+};
+
// The stream visitor is an application-provided object that gets notified about
// events related to a WebTransport stream. The visitor object is owned by the
// stream itself, meaning that if the stream is ever fully closed, the visitor
@@ -215,6 +241,10 @@
// being silently dropped.
virtual void SetDatagramMaxTimeInQueue(absl::Duration max_time_in_queue) = 0;
+ // Returns stats that generally follow the semantics of W3C WebTransport API.
+ virtual DatagramStats GetDatagramStats() = 0;
+ virtual SessionStats GetSessionStats() = 0;
+
// Sends a DRAIN_WEBTRANSPORT_SESSION capsule or an equivalent signal to the
// peer indicating that the session is draining.
virtual void NotifySessionDraining() = 0;