network: Add new network emulation options for WebRTC
Three new options are added to the protocol and NetworkConditions to
control the packet loss (expressed as a percent from 0 to 100),
the packet queue length and if packet reordering is allowed.
Those are optional and should allow to keep compatibility with older
versions of the protocol.
Bug: 490143
Change-Id: Ibdbde23db1a7f73e5aae9ad8864cb3cceac0bfba
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5349261
Commit-Queue: Florent Castelli <orphis@chromium.org>
Reviewed-by: Alex Rudenko <alexrudenko@chromium.org>
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: Adam Rice <ricea@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1270884}
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 3047b26..b6266b27 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -1802,7 +1802,10 @@
double latency,
double download_throughput,
double upload_throughput,
- Maybe<protocol::Network::ConnectionType>) {
+ Maybe<protocol::Network::ConnectionType>,
+ Maybe<double> packet_loss,
+ Maybe<int> packet_queue_length,
+ Maybe<bool> packet_reordering) {
network::mojom::NetworkConditionsPtr network_conditions;
bool throttling_enabled = offline || latency > 0 || download_throughput > 0 ||
upload_throughput > 0;
@@ -1812,6 +1815,9 @@
network_conditions->latency = base::Milliseconds(latency);
network_conditions->download_throughput = download_throughput;
network_conditions->upload_throughput = upload_throughput;
+ network_conditions->packet_loss = packet_loss.value_or(0.);
+ network_conditions->packet_queue_length = packet_queue_length.value_or(0);
+ network_conditions->packet_reordering = packet_reordering.value_or(false);
}
SetNetworkConditions(std::move(network_conditions));
return Response::FallThrough();
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h
index 4480e436..418b91d 100644
--- a/content/browser/devtools/protocol/network_handler.h
+++ b/content/browser/devtools/protocol/network_handler.h
@@ -170,7 +170,10 @@
double latency,
double download_throughput,
double upload_throughput,
- Maybe<protocol::Network::ConnectionType> connection_type) override;
+ Maybe<protocol::Network::ConnectionType> connection_type,
+ Maybe<double> packet_loss,
+ Maybe<int> packet_queue_length,
+ Maybe<bool> packet_reordering) override;
Response SetBypassServiceWorker(bool bypass) override;
DispatchResponse SetRequestInterception(
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index ced5854..32dce17 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -1503,7 +1503,9 @@
if (conditions) {
network_conditions = std::make_unique<NetworkConditions>(
conditions->offline, conditions->latency.InMillisecondsF(),
- conditions->download_throughput, conditions->upload_throughput);
+ conditions->download_throughput, conditions->upload_throughput,
+ conditions->packet_loss, conditions->packet_queue_length,
+ conditions->packet_reordering);
}
ThrottlingController::SetConditions(throttling_profile_id,
std::move(network_conditions));
diff --git a/services/network/p2p/socket_udp_unittest.cc b/services/network/p2p/socket_udp_unittest.cc
index af0030af..c36a8fb 100644
--- a/services/network/p2p/socket_udp_unittest.cc
+++ b/services/network/p2p/socket_udp_unittest.cc
@@ -959,7 +959,7 @@
devtools_token_)) {}
void SetUp() override {
- SetNetworkState(false, 1000000, 1000000, base::Milliseconds(0));
+ SetNetworkState({});
P2PSocketUdpTest::SetUp();
}
@@ -968,12 +968,17 @@
P2PSocketUdpTest::TearDown();
}
- void SetNetworkState(bool offline,
- double download,
- double upload,
- base::TimeDelta latency) {
+ struct NetworkState {
+ bool offline = false;
+ base::TimeDelta latency;
+ double packet_loss = 0.0;
+ int packet_queue_length = 0;
+ };
+
+ void SetNetworkState(NetworkState state) {
std::unique_ptr<NetworkConditions> conditions(new NetworkConditions(
- offline, latency.InMillisecondsF(), download, upload));
+ state.offline, state.latency.InMillisecondsF(), 0.0, 0.0,
+ state.packet_loss, state.packet_queue_length, false));
ThrottlingController::SetConditions(*devtools_token_,
std::move(conditions));
}
@@ -1032,12 +1037,12 @@
std::vector<uint8_t> packet;
CreateRandomPacket(&packet);
- SetNetworkState(true, 1000000, 1000000, base::Milliseconds(0));
+ SetNetworkState({.offline = true});
socket_impl_->Send(packet, P2PPacketInfo(dest1_, options, 0));
AdvanceClock(base::Milliseconds(100));
EXPECT_EQ(0U, sent_packets_.size());
- SetNetworkState(false, 1000000, 1000000, base::Milliseconds(0));
+ SetNetworkState({});
socket_impl_->Send(packet, P2PPacketInfo(dest1_, options, 1));
AdvanceClock(base::Milliseconds(100));
EXPECT_EQ(1U, sent_packets_.size());
@@ -1059,7 +1064,7 @@
std::vector<uint8_t> packet;
CreateRandomPacket(&packet);
- SetNetworkState(false, 1000000, 1000000, base::Milliseconds(1000));
+ SetNetworkState({.latency = base::Milliseconds(1000)});
socket_impl_->Send(packet, P2PPacketInfo(dest1_, options, 0));
AdvanceClock(base::Milliseconds(100));
@@ -1068,7 +1073,7 @@
AdvanceClock(base::Milliseconds(2000));
EXPECT_EQ(1U, sent_packets_.size());
- SetNetworkState(false, 1000000, 1000000, base::Milliseconds(0));
+ SetNetworkState({});
socket_impl_->Send(packet, P2PPacketInfo(dest1_, options, 1));
AdvanceClock(base::Milliseconds(100));
@@ -1105,9 +1110,8 @@
}
TEST_F(P2PSocketUdpWithInterceptorTest, SendPacketDropsLongQueue) {
- // Hardcoded value that matches the details in ThrottlingP2PNetworkInterceptor
- // until we can configure it.
- constexpr size_t kMaxQueueLength = 300;
+ constexpr size_t kMaxQueueLength = 100;
+ SetNetworkState({.packet_queue_length = kMaxQueueLength});
// Receive packet from |dest1_|.
std::vector<uint8_t> request_packet;
@@ -1132,6 +1136,33 @@
EXPECT_EQ(kMaxQueueLength, sent_packets_.size());
}
+TEST_F(P2PSocketUdpWithInterceptorTest, SendPacketWithPacketDrop) {
+ // Receive packet from |dest1_|.
+ std::vector<uint8_t> request_packet;
+ CreateStunRequest(&request_packet);
+
+ EXPECT_CALL(*fake_client_.get(), DataReceived(_)).Times(1);
+ EXPECT_CALL(*this, SinglePacketReceptionHelper(_, SpanEq(request_packet), _));
+ EXPECT_CALL(*fake_client_.get(), SendComplete(_)).Times(2);
+
+ socket_->ReceivePacket(dest1_, request_packet);
+ AdvanceClock(base::Milliseconds(100));
+
+ rtc::PacketOptions options;
+ std::vector<uint8_t> packet;
+ CreateRandomPacket(&packet);
+
+ SetNetworkState({.packet_loss = 100.0});
+ socket_impl_->Send(packet, P2PPacketInfo(dest1_, options, 0));
+ AdvanceClock(base::Milliseconds(100));
+ EXPECT_EQ(0U, sent_packets_.size());
+
+ SetNetworkState({});
+ socket_impl_->Send(packet, P2PPacketInfo(dest1_, options, 1));
+ AdvanceClock(base::Milliseconds(100));
+ EXPECT_EQ(1U, sent_packets_.size());
+}
+
TEST_F(P2PSocketUdpWithInterceptorTest, ReceivePackets) {
// Receive STUN request from |dest1_|.
std::vector<uint8_t> request_packet;
@@ -1177,7 +1208,7 @@
AdvanceClock(base::Milliseconds(100));
- SetNetworkState(false, 1000000, 1000000, base::Milliseconds(1000));
+ SetNetworkState({.latency = base::Milliseconds(1000)});
EXPECT_CALL(*fake_client_.get(), DataReceived(_)).Times(0);
std::vector<uint8_t> packet;
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 8495a3c..2fe535e15 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -623,6 +623,17 @@
// Maximal aggregated upload throughput (bytes/sec). <=0 disables upload
// throttling.
double upload_throughput;
+
+ // Packet loss for WebRTC connections (percent, 0 to 100).<= 0 disables
+ // packet loss.
+ double packet_loss;
+
+ // Packet queue length for WebRTC connections (packet count). <=0 allows for
+ // an infinite queue.
+ int32 packet_queue_length;
+
+ // Allow packet reordering for WebRTC connections.
+ bool packet_reordering;
};
// Represents a shared dictionary.
diff --git a/services/network/throttling/network_conditions.cc b/services/network/throttling/network_conditions.cc
index b894168c..bc052d5 100644
--- a/services/network/throttling/network_conditions.cc
+++ b/services/network/throttling/network_conditions.cc
@@ -14,22 +14,41 @@
default;
NetworkConditions::NetworkConditions(bool offline)
- : NetworkConditions(offline, 0, 0, 0) {}
+ : NetworkConditions(offline, 0, 0, 0, 0.0, 0, false) {}
NetworkConditions::NetworkConditions(bool offline,
double latency,
double download_throughput,
double upload_throughput)
+ : NetworkConditions(offline,
+ latency,
+ download_throughput,
+ upload_throughput,
+ 0.0,
+ 0,
+ false) {}
+
+NetworkConditions::NetworkConditions(bool offline,
+ double latency,
+ double download_throughput,
+ double upload_throughput,
+ double packet_loss,
+ int packet_queue_length,
+ bool packet_reordering)
: offline_(offline),
latency_(std::max(latency, 0.0)),
download_throughput_(std::max(download_throughput, 0.0)),
- upload_throughput_(std::max(upload_throughput, 0.0)) {}
+ upload_throughput_(std::max(upload_throughput, 0.0)),
+ packet_loss_(std::max(packet_loss, 0.0)),
+ packet_queue_length_(packet_queue_length),
+ packet_reordering_(packet_reordering) {}
NetworkConditions::~NetworkConditions() {}
bool NetworkConditions::IsThrottling() const {
return !offline_ && ((latency_ != 0) || (download_throughput_ != 0.0) ||
- (upload_throughput_ != 0));
+ (upload_throughput_ != 0) || (packet_loss_ != 0.0) ||
+ (packet_queue_length_ != 0) || packet_reordering_);
}
} // namespace network
diff --git a/services/network/throttling/network_conditions.h b/services/network/throttling/network_conditions.h
index f1524ba..1dd9012 100644
--- a/services/network/throttling/network_conditions.h
+++ b/services/network/throttling/network_conditions.h
@@ -24,6 +24,13 @@
double latency,
double download_throughput,
double upload_throughput);
+ NetworkConditions(bool offline,
+ double latency,
+ double download_throughput,
+ double upload_throughput,
+ double packet_loss,
+ int packet_queue_length,
+ bool packet_reordering);
bool IsThrottling() const;
@@ -33,12 +40,18 @@
double latency() const { return latency_; }
double download_throughput() const { return download_throughput_; }
double upload_throughput() const { return upload_throughput_; }
+ double packet_loss() const { return packet_loss_; }
+ int packet_queue_length() const { return packet_queue_length_; }
+ bool packet_reordering() const { return packet_reordering_; }
private:
bool offline_;
double latency_;
double download_throughput_;
double upload_throughput_;
+ double packet_loss_;
+ int packet_queue_length_;
+ bool packet_reordering_;
};
} // namespace network
diff --git a/services/network/throttling/throttling_network_interceptor.cc b/services/network/throttling/throttling_network_interceptor.cc
index 777ec67..675081d 100644
--- a/services/network/throttling/throttling_network_interceptor.cc
+++ b/services/network/throttling/throttling_network_interceptor.cc
@@ -74,8 +74,6 @@
}
// Throttling.
- DCHECK(conditions_.download_throughput() != 0 ||
- conditions_.upload_throughput() != 0);
offset_ = now;
download_last_tick_ = 0;
diff --git a/services/network/throttling/throttling_p2p_network_interceptor.cc b/services/network/throttling/throttling_p2p_network_interceptor.cc
index 8b9a195..347789b 100644
--- a/services/network/throttling/throttling_p2p_network_interceptor.cc
+++ b/services/network/throttling/throttling_p2p_network_interceptor.cc
@@ -80,19 +80,25 @@
<< static_cast<uint64_t>(conditions.download_throughput() *
kBitsPerByte)
<< "kbps, latency: " << static_cast<uint64_t>(conditions.latency())
- << "ms";
+ << "ms, packet drop: " << conditions.packet_loss()
+ << "%, packet queue: " << conditions.packet_queue_length()
+ << ", packet reordering: " << conditions.packet_reordering();
webrtc::SimulatedNetwork::Config send_config;
- send_config.queue_length_packets = 300;
send_config.link_capacity_kbps =
(conditions.upload_throughput() * kBitsPerByte) / 1000;
send_config.queue_delay_ms = conditions.latency();
+ send_config.allow_reordering = conditions.packet_reordering();
+ send_config.loss_percent = conditions.packet_loss();
+ send_config.queue_length_packets = conditions.packet_queue_length();
send_network_.SetConfig(send_config);
webrtc::SimulatedNetwork::Config receive_config;
receive_config.link_capacity_kbps =
(conditions.download_throughput() * kBitsPerByte) / 1000;
receive_config.queue_delay_ms = conditions.latency();
+ receive_config.allow_reordering = conditions.packet_reordering();
+ receive_config.loss_percent = conditions.packet_loss();
receive_network_.SetConfig(receive_config);
}
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 1fd197e..aba51cd 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -6356,6 +6356,12 @@
number uploadThroughput
# Connection type if known.
optional ConnectionType connectionType
+ # WebRTC packet loss (percent, 0-100). 0 disables packet loss emulation, 100 drops all the packets.
+ experimental optional number packetLoss
+ # WebRTC packet queue length (packet). 0 removes any queue length limitations.
+ experimental optional integer packetQueueLength
+ # WebRTC packetReordering feature.
+ experimental optional boolean packetReordering
# Enables network tracking, network events will now be delivered to the client.
command enable
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index 6681081..89eacc5 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -2219,7 +2219,10 @@
double latency,
double download_throughput,
double upload_throughput,
- Maybe<String> connection_type) {
+ Maybe<String> connection_type,
+ Maybe<double> packet_loss,
+ Maybe<int> packet_queue_length,
+ Maybe<bool> packet_reordering) {
WebConnectionType type = kWebConnectionTypeUnknown;
if (connection_type.has_value()) {
type = ToWebConnectionType(connection_type.value());
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.h b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
index 7ae06124..360bb3a 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
@@ -245,7 +245,10 @@
double latency,
double download_throughput,
double upload_throughput,
- Maybe<String> connection_type) override;
+ Maybe<String> connection_type,
+ Maybe<double> packet_loss,
+ Maybe<int> packet_queue_length,
+ Maybe<bool> packet_reordering) override;
protocol::Response setCacheDisabled(bool) override;
protocol::Response setBypassServiceWorker(bool) override;
protocol::Response getCertificate(