blob: e56c6fa2c55e9cbae5d87897e31cbb72bda53c08 [file] [log] [blame]
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +00001/*
2 * Copyright 2011 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Jonas Olssona4d87372019-07-05 19:08:33 +020011#include "p2p/base/pseudo_tcp.h"
12
Yves Gerey3e707812018-11-28 16:47:49 +010013#include <string.h>
Jonas Olssona4d87372019-07-05 19:08:33 +020014
andresp@webrtc.orgff689be2015-02-12 11:54:26 +000015#include <algorithm>
Yves Gerey3e707812018-11-28 16:47:49 +010016#include <cstddef>
Steve Anton5c8231c2017-12-06 10:39:22 -080017#include <string>
Danil Chapovalov4d715382022-08-19 10:28:40 +020018#include <utility>
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000019#include <vector>
20
Danil Chapovalov4d715382022-08-19 10:28:40 +020021#include "api/task_queue/pending_task_safety_flag.h"
22#include "api/task_queue/task_queue_base.h"
23#include "api/units/time_delta.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "rtc_base/gunit.h"
25#include "rtc_base/helpers.h"
Yves Gerey3e707812018-11-28 16:47:49 +010026#include "rtc_base/logging.h"
Niels Möllere7547d52018-11-01 09:33:08 +010027#include "rtc_base/memory_stream.h"
Steve Anton10542f22019-01-11 09:11:00 -080028#include "rtc_base/time_utils.h"
Yves Gerey3e707812018-11-28 16:47:49 +010029#include "test/gtest.h"
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000030
Danil Chapovalov4d715382022-08-19 10:28:40 +020031using ::cricket::PseudoTcp;
32using ::webrtc::ScopedTaskSafety;
33using ::webrtc::TaskQueueBase;
34using ::webrtc::TimeDelta;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000035
36static const int kConnectTimeoutMs = 10000; // ~3 * default RTO of 3000ms
37static const int kTransferTimeoutMs = 15000;
38static const int kBlockSize = 4096;
39
40class PseudoTcpForTest : public cricket::PseudoTcp {
41 public:
Peter Boström0c4e06b2015-10-07 12:23:21 +020042 PseudoTcpForTest(cricket::IPseudoTcpNotify* notify, uint32_t conv)
43 : PseudoTcp(notify, conv) {}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000044
Steve Antoncc65bd02017-11-29 10:19:58 -080045 bool isReceiveBufferFull() const { return PseudoTcp::isReceiveBufferFull(); }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000046
Steve Antoncc65bd02017-11-29 10:19:58 -080047 void disableWindowScale() { PseudoTcp::disableWindowScale(); }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000048};
49
Mirko Bonadei6a489f22019-04-09 15:11:12 +020050class PseudoTcpTestBase : public ::testing::Test,
Steve Antoncc65bd02017-11-29 10:19:58 -080051 public cricket::IPseudoTcpNotify {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000052 public:
53 PseudoTcpTestBase()
54 : local_(this, 1),
55 remote_(this, 1),
56 have_connected_(false),
57 have_disconnected_(false),
58 local_mtu_(65535),
59 remote_mtu_(65535),
60 delay_(0),
61 loss_(0) {
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -070062 // Set use of the test RNG to get predictable loss patterns. Otherwise,
63 // this test would occasionally get really unlucky loss and time out.
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000064 rtc::SetRandomTestMode(true);
65 }
66 ~PseudoTcpTestBase() {
67 // Put it back for the next test.
68 rtc::SetRandomTestMode(false);
69 }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -070070 // If true, both endpoints will send the "connect" segment simultaneously,
Artem Titov2dbb4c92021-07-26 15:12:41 +020071 // rather than `local_` sending it followed by a response from `remote_`.
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -070072 // Note that this is what chromoting ends up doing.
73 void SetSimultaneousOpen(bool enabled) { simultaneous_open_ = enabled; }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000074 void SetLocalMtu(int mtu) {
75 local_.NotifyMTU(mtu);
76 local_mtu_ = mtu;
77 }
78 void SetRemoteMtu(int mtu) {
79 remote_.NotifyMTU(mtu);
80 remote_mtu_ = mtu;
81 }
Steve Antoncc65bd02017-11-29 10:19:58 -080082 void SetDelay(int delay) { delay_ = delay; }
83 void SetLoss(int percent) { loss_ = percent; }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -070084 // Used to cause the initial "connect" segment to be lost, needed for a
85 // regression test.
86 void DropNextPacket() { drop_next_packet_ = true; }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000087 void SetOptNagling(bool enable_nagles) {
88 local_.SetOption(PseudoTcp::OPT_NODELAY, !enable_nagles);
89 remote_.SetOption(PseudoTcp::OPT_NODELAY, !enable_nagles);
90 }
91 void SetOptAckDelay(int ack_delay) {
92 local_.SetOption(PseudoTcp::OPT_ACKDELAY, ack_delay);
93 remote_.SetOption(PseudoTcp::OPT_ACKDELAY, ack_delay);
94 }
95 void SetOptSndBuf(int size) {
96 local_.SetOption(PseudoTcp::OPT_SNDBUF, size);
97 remote_.SetOption(PseudoTcp::OPT_SNDBUF, size);
98 }
99 void SetRemoteOptRcvBuf(int size) {
100 remote_.SetOption(PseudoTcp::OPT_RCVBUF, size);
101 }
102 void SetLocalOptRcvBuf(int size) {
103 local_.SetOption(PseudoTcp::OPT_RCVBUF, size);
104 }
Steve Antoncc65bd02017-11-29 10:19:58 -0800105 void DisableRemoteWindowScale() { remote_.disableWindowScale(); }
106 void DisableLocalWindowScale() { local_.disableWindowScale(); }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000107
108 protected:
109 int Connect() {
110 int ret = local_.Connect();
111 if (ret == 0) {
112 UpdateLocalClock();
113 }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700114 if (simultaneous_open_) {
115 ret = remote_.Connect();
116 if (ret == 0) {
117 UpdateRemoteClock();
118 }
119 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000120 return ret;
121 }
122 void Close() {
123 local_.Close(false);
124 UpdateLocalClock();
125 }
126
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000127 virtual void OnTcpOpen(PseudoTcp* tcp) {
128 // Consider ourselves connected when the local side gets OnTcpOpen.
129 // OnTcpWriteable isn't fired at open, so we trigger it now.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100130 RTC_LOG(LS_VERBOSE) << "Opened";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000131 if (tcp == &local_) {
132 have_connected_ = true;
133 OnTcpWriteable(tcp);
134 }
135 }
136 // Test derived from the base should override
137 // virtual void OnTcpReadable(PseudoTcp* tcp)
138 // and
139 // virtual void OnTcpWritable(PseudoTcp* tcp)
Peter Boström0c4e06b2015-10-07 12:23:21 +0200140 virtual void OnTcpClosed(PseudoTcp* tcp, uint32_t error) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000141 // Consider ourselves closed when the remote side gets OnTcpClosed.
Steve Anton5c8231c2017-12-06 10:39:22 -0800142 // TODO(?): OnTcpClosed is only ever notified in case of error in
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000143 // the current implementation. Solicited close is not (yet) supported.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100144 RTC_LOG(LS_VERBOSE) << "Closed";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000145 EXPECT_EQ(0U, error);
146 if (tcp == &remote_) {
147 have_disconnected_ = true;
148 }
149 }
150 virtual WriteResult TcpWritePacket(PseudoTcp* tcp,
Steve Antoncc65bd02017-11-29 10:19:58 -0800151 const char* buffer,
152 size_t len) {
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700153 // Drop a packet if the test called DropNextPacket.
154 if (drop_next_packet_) {
155 drop_next_packet_ = false;
156 RTC_LOG(LS_VERBOSE) << "Dropping packet due to DropNextPacket, size="
157 << len;
158 return WR_SUCCESS;
159 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000160 // Randomly drop the desired percentage of packets.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200161 if (rtc::CreateRandomId() % 100 < static_cast<uint32_t>(loss_)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100162 RTC_LOG(LS_VERBOSE) << "Randomly dropping packet, size=" << len;
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700163 return WR_SUCCESS;
164 }
165 // Also drop packets that are larger than the configured MTU.
166 if (len > static_cast<size_t>(std::min(local_mtu_, remote_mtu_))) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100167 RTC_LOG(LS_VERBOSE) << "Dropping packet that exceeds path MTU, size="
168 << len;
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700169 return WR_SUCCESS;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000170 }
Danil Chapovalov4d715382022-08-19 10:28:40 +0200171 PseudoTcp* other;
172 ScopedTaskSafety* timer;
173 if (tcp == &local_) {
174 other = &remote_;
175 timer = &remote_timer_;
176 } else {
177 other = &local_;
178 timer = &local_timer_;
179 }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700180 std::string packet(buffer, len);
Danil Chapovalov4d715382022-08-19 10:28:40 +0200181 ++packets_in_flight_;
182 TaskQueueBase::Current()->PostDelayedTask(
183 [other, timer, packet = std::move(packet), this] {
184 --packets_in_flight_;
185 other->NotifyPacket(packet.c_str(), packet.size());
186 UpdateClock(*other, *timer);
187 },
188 TimeDelta::Millis(delay_));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000189 return WR_SUCCESS;
190 }
191
Danil Chapovalov4d715382022-08-19 10:28:40 +0200192 void UpdateLocalClock() { UpdateClock(local_, local_timer_); }
193 void UpdateRemoteClock() { UpdateClock(remote_, remote_timer_); }
194 static void UpdateClock(PseudoTcp& tcp, ScopedTaskSafety& timer) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000195 long interval = 0; // NOLINT
Danil Chapovalov4d715382022-08-19 10:28:40 +0200196 tcp.GetNextClock(PseudoTcp::Now(), interval);
andresp@webrtc.orgff689be2015-02-12 11:54:26 +0000197 interval = std::max<int>(interval, 0L); // sometimes interval is < 0
Danil Chapovalov4d715382022-08-19 10:28:40 +0200198 timer.reset();
199 TaskQueueBase::Current()->PostDelayedTask(
200 SafeTask(timer.flag(),
201 [&tcp, &timer] {
202 tcp.NotifyClock(PseudoTcp::Now());
203 UpdateClock(tcp, timer);
204 }),
205 TimeDelta::Millis(interval));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000206 }
207
Niels Möller83830f32022-05-20 09:12:57 +0200208 rtc::AutoThread main_thread_;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000209 PseudoTcpForTest local_;
210 PseudoTcpForTest remote_;
Danil Chapovalov4d715382022-08-19 10:28:40 +0200211 ScopedTaskSafety local_timer_;
212 ScopedTaskSafety remote_timer_;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000213 rtc::MemoryStream send_stream_;
214 rtc::MemoryStream recv_stream_;
215 bool have_connected_;
216 bool have_disconnected_;
217 int local_mtu_;
218 int remote_mtu_;
219 int delay_;
220 int loss_;
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700221 bool drop_next_packet_ = false;
222 bool simultaneous_open_ = false;
Danil Chapovalov4d715382022-08-19 10:28:40 +0200223 int packets_in_flight_ = 0;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000224};
225
226class PseudoTcpTest : public PseudoTcpTestBase {
227 public:
228 void TestTransfer(int size) {
Honghai Zhang82d78622016-05-06 11:29:15 -0700229 uint32_t start;
230 int32_t elapsed;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000231 size_t received;
232 // Create some dummy data to send.
233 send_stream_.ReserveSize(size);
234 for (int i = 0; i < size; ++i) {
Harald Alvestranddd4c4062022-11-16 07:29:57 +0000235 uint8_t ch = static_cast<uint8_t>(i);
236 size_t written;
237 int error;
238 send_stream_.Write(rtc::MakeArrayView(&ch, 1), written, error);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000239 }
240 send_stream_.Rewind();
241 // Prepare the receive stream.
242 recv_stream_.ReserveSize(size);
243 // Connect and wait until connected.
Honghai Zhang82d78622016-05-06 11:29:15 -0700244 start = rtc::Time32();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000245 EXPECT_EQ(0, Connect());
246 EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
247 // Sending will start from OnTcpWriteable and complete when all data has
248 // been received.
249 EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
Honghai Zhang82d78622016-05-06 11:29:15 -0700250 elapsed = rtc::Time32() - start;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000251 recv_stream_.GetSize(&received);
252 // Ensure we closed down OK and we got the right data.
Steve Anton5c8231c2017-12-06 10:39:22 -0800253 // TODO(?): Ensure the errors are cleared properly.
Steve Antoncc65bd02017-11-29 10:19:58 -0800254 // EXPECT_EQ(0, local_.GetError());
255 // EXPECT_EQ(0, remote_.GetError());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000256 EXPECT_EQ(static_cast<size_t>(size), received);
Steve Antoncc65bd02017-11-29 10:19:58 -0800257 EXPECT_EQ(0,
258 memcmp(send_stream_.GetBuffer(), recv_stream_.GetBuffer(), size));
Mirko Bonadei675513b2017-11-09 11:09:25 +0100259 RTC_LOG(LS_INFO) << "Transferred " << received << " bytes in " << elapsed
260 << " ms (" << size * 8 / elapsed << " Kbps)";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000261 }
262
263 private:
264 // IPseudoTcpNotify interface
265
266 virtual void OnTcpReadable(PseudoTcp* tcp) {
267 // Stream bytes to the recv stream as they arrive.
268 if (tcp == &remote_) {
269 ReadData();
270
Steve Anton5c8231c2017-12-06 10:39:22 -0800271 // TODO(?): OnTcpClosed() is currently only notified on error -
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000272 // there is no on-the-wire equivalent of TCP FIN.
273 // So we fake the notification when all the data has been read.
274 size_t received, required;
275 recv_stream_.GetPosition(&received);
276 send_stream_.GetSize(&required);
277 if (received == required)
278 OnTcpClosed(&remote_, 0);
279 }
280 }
281 virtual void OnTcpWriteable(PseudoTcp* tcp) {
282 // Write bytes from the send stream when we can.
283 // Shut down when we've sent everything.
284 if (tcp == &local_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100285 RTC_LOG(LS_VERBOSE) << "Flow Control Lifted";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000286 bool done;
287 WriteData(&done);
288 if (done) {
289 Close();
290 }
291 }
292 }
293
294 void ReadData() {
295 char block[kBlockSize];
296 size_t position;
297 int rcvd;
298 do {
299 rcvd = remote_.Recv(block, sizeof(block));
300 if (rcvd != -1) {
Harald Alvestranddd4c4062022-11-16 07:29:57 +0000301 size_t written;
302 int error;
303 recv_stream_.Write(
304 rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block), rcvd),
305 written, error);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000306 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100307 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000308 }
309 } while (rcvd > 0);
310 }
311 void WriteData(bool* done) {
312 size_t position, tosend;
313 int sent;
314 char block[kBlockSize];
315 do {
316 send_stream_.GetPosition(&position);
Harald Alvestranddd4c4062022-11-16 07:29:57 +0000317 int error;
318 if (send_stream_.Read(
319 rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block), kBlockSize),
320 tosend, error) != rtc::SR_EOS) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000321 sent = local_.Send(block, tosend);
322 UpdateLocalClock();
323 if (sent != -1) {
324 send_stream_.SetPosition(position + sent);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100325 RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000326 } else {
327 send_stream_.SetPosition(position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100328 RTC_LOG(LS_VERBOSE) << "Flow Controlled";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000329 }
330 } else {
331 sent = static_cast<int>(tosend = 0);
332 }
333 } while (sent > 0);
334 *done = (tosend == 0);
335 }
336
337 private:
338 rtc::MemoryStream send_stream_;
339 rtc::MemoryStream recv_stream_;
340};
341
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000342class PseudoTcpTestPingPong : public PseudoTcpTestBase {
343 public:
344 PseudoTcpTestPingPong()
345 : iterations_remaining_(0),
Steve Antoncc65bd02017-11-29 10:19:58 -0800346 sender_(NULL),
347 receiver_(NULL),
348 bytes_per_send_(0) {}
349 void SetBytesPerSend(int bytes) { bytes_per_send_ = bytes; }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000350 void TestPingPong(int size, int iterations) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200351 uint32_t start, elapsed;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000352 iterations_remaining_ = iterations;
353 receiver_ = &remote_;
354 sender_ = &local_;
355 // Create some dummy data to send.
356 send_stream_.ReserveSize(size);
357 for (int i = 0; i < size; ++i) {
Harald Alvestranddd4c4062022-11-16 07:29:57 +0000358 uint8_t ch = static_cast<uint8_t>(i);
359 size_t written;
360 int error;
361 send_stream_.Write(rtc::MakeArrayView(&ch, 1), written, error);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000362 }
363 send_stream_.Rewind();
364 // Prepare the receive stream.
365 recv_stream_.ReserveSize(size);
366 // Connect and wait until connected.
Honghai Zhang82d78622016-05-06 11:29:15 -0700367 start = rtc::Time32();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000368 EXPECT_EQ(0, Connect());
369 EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
370 // Sending will start from OnTcpWriteable and stop when the required
371 // number of iterations have completed.
372 EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
373 elapsed = rtc::TimeSince(start);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100374 RTC_LOG(LS_INFO) << "Performed " << iterations << " pings in " << elapsed
375 << " ms";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000376 }
377
378 private:
379 // IPseudoTcpNotify interface
380
381 virtual void OnTcpReadable(PseudoTcp* tcp) {
382 if (tcp != receiver_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100383 RTC_LOG_F(LS_ERROR) << "unexpected OnTcpReadable";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000384 return;
385 }
386 // Stream bytes to the recv stream as they arrive.
387 ReadData();
388 // If we've received the desired amount of data, rewind things
389 // and send it back the other way!
390 size_t position, desired;
391 recv_stream_.GetPosition(&position);
392 send_stream_.GetSize(&desired);
393 if (position == desired) {
394 if (receiver_ == &local_ && --iterations_remaining_ == 0) {
395 Close();
Steve Anton5c8231c2017-12-06 10:39:22 -0800396 // TODO(?): Fake OnTcpClosed() on the receiver for now.
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000397 OnTcpClosed(&remote_, 0);
398 return;
399 }
400 PseudoTcp* tmp = receiver_;
401 receiver_ = sender_;
402 sender_ = tmp;
403 recv_stream_.Rewind();
404 send_stream_.Rewind();
405 OnTcpWriteable(sender_);
406 }
407 }
408 virtual void OnTcpWriteable(PseudoTcp* tcp) {
409 if (tcp != sender_)
410 return;
411 // Write bytes from the send stream when we can.
412 // Shut down when we've sent everything.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100413 RTC_LOG(LS_VERBOSE) << "Flow Control Lifted";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000414 WriteData();
415 }
416
417 void ReadData() {
418 char block[kBlockSize];
419 size_t position;
420 int rcvd;
421 do {
422 rcvd = receiver_->Recv(block, sizeof(block));
423 if (rcvd != -1) {
Harald Alvestranddd4c4062022-11-16 07:29:57 +0000424 size_t written;
425 int error;
426 recv_stream_.Write(
427 rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(block), rcvd),
428 written, error);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000429 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100430 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000431 }
432 } while (rcvd > 0);
433 }
434 void WriteData() {
435 size_t position, tosend;
436 int sent;
437 char block[kBlockSize];
438 do {
439 send_stream_.GetPosition(&position);
440 tosend = bytes_per_send_ ? bytes_per_send_ : sizeof(block);
Harald Alvestranddd4c4062022-11-16 07:29:57 +0000441 int error;
442 if (send_stream_.Read(
443 rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block), tosend),
444 tosend, error) != rtc::SR_EOS) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000445 sent = sender_->Send(block, tosend);
446 UpdateLocalClock();
447 if (sent != -1) {
448 send_stream_.SetPosition(position + sent);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100449 RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000450 } else {
451 send_stream_.SetPosition(position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100452 RTC_LOG(LS_VERBOSE) << "Flow Controlled";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000453 }
454 } else {
455 sent = static_cast<int>(tosend = 0);
456 }
457 } while (sent > 0);
458 }
459
460 private:
461 int iterations_remaining_;
462 PseudoTcp* sender_;
463 PseudoTcp* receiver_;
464 int bytes_per_send_;
465};
466
467// Fill the receiver window until it is full, drain it and then
468// fill it with the same amount. This is to test that receiver window
469// contracts and enlarges correctly.
470class PseudoTcpTestReceiveWindow : public PseudoTcpTestBase {
471 public:
Artem Titov2dbb4c92021-07-26 15:12:41 +0200472 // Not all the data are transfered, `size` just need to be big enough
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000473 // to fill up the receiver window twice.
474 void TestTransfer(int size) {
475 // Create some dummy data to send.
476 send_stream_.ReserveSize(size);
477 for (int i = 0; i < size; ++i) {
Harald Alvestranddd4c4062022-11-16 07:29:57 +0000478 uint8_t ch = static_cast<uint8_t>(i);
479 size_t written;
480 int error;
481 send_stream_.Write(rtc::MakeArrayView(&ch, 1), written, error);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000482 }
483 send_stream_.Rewind();
484
485 // Prepare the receive stream.
486 recv_stream_.ReserveSize(size);
487
488 // Connect and wait until connected.
489 EXPECT_EQ(0, Connect());
490 EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
491
Danil Chapovalov4d715382022-08-19 10:28:40 +0200492 TaskQueueBase::Current()->PostTask([this] { WriteData(); });
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000493 EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
494
495 ASSERT_EQ(2u, send_position_.size());
496 ASSERT_EQ(2u, recv_position_.size());
497
498 const size_t estimated_recv_window = EstimateReceiveWindowSize();
499
500 // The difference in consecutive send positions should equal the
501 // receive window size or match very closely. This verifies that receive
502 // window is open after receiver drained all the data.
503 const size_t send_position_diff = send_position_[1] - send_position_[0];
504 EXPECT_GE(1024u, estimated_recv_window - send_position_diff);
505
506 // Receiver drained the receive window twice.
507 EXPECT_EQ(2 * estimated_recv_window, recv_position_[1]);
508 }
509
Peter Boström0c4e06b2015-10-07 12:23:21 +0200510 uint32_t EstimateReceiveWindowSize() const {
511 return static_cast<uint32_t>(recv_position_[0]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000512 }
513
Peter Boström0c4e06b2015-10-07 12:23:21 +0200514 uint32_t EstimateSendWindowSize() const {
515 return static_cast<uint32_t>(send_position_[0] - recv_position_[0]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000516 }
517
518 private:
519 // IPseudoTcpNotify interface
Steve Antoncc65bd02017-11-29 10:19:58 -0800520 virtual void OnTcpReadable(PseudoTcp* tcp) {}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000521
Steve Antoncc65bd02017-11-29 10:19:58 -0800522 virtual void OnTcpWriteable(PseudoTcp* tcp) {}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000523
524 void ReadUntilIOPending() {
525 char block[kBlockSize];
526 size_t position;
527 int rcvd;
528
529 do {
530 rcvd = remote_.Recv(block, sizeof(block));
531 if (rcvd != -1) {
Harald Alvestranddd4c4062022-11-16 07:29:57 +0000532 size_t written;
533 int error;
534 recv_stream_.Write(
535 rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block), rcvd),
536 written, error);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000537 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100538 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000539 }
540 } while (rcvd > 0);
541
542 recv_stream_.GetPosition(&position);
543 recv_position_.push_back(position);
544
545 // Disconnect if we have done two transfers.
546 if (recv_position_.size() == 2u) {
547 Close();
548 OnTcpClosed(&remote_, 0);
549 } else {
550 WriteData();
551 }
552 }
553
554 void WriteData() {
555 size_t position, tosend;
556 int sent;
557 char block[kBlockSize];
558 do {
559 send_stream_.GetPosition(&position);
Harald Alvestranddd4c4062022-11-16 07:29:57 +0000560 int error;
561 if (send_stream_.Read(
562 rtc::MakeArrayView(reinterpret_cast<uint8_t*>(block),
563 sizeof(block)),
564 tosend, error) != rtc::SR_EOS) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000565 sent = local_.Send(block, tosend);
566 UpdateLocalClock();
567 if (sent != -1) {
568 send_stream_.SetPosition(position + sent);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100569 RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000570 } else {
571 send_stream_.SetPosition(position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100572 RTC_LOG(LS_VERBOSE) << "Flow Controlled";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000573 }
574 } else {
575 sent = static_cast<int>(tosend = 0);
576 }
577 } while (sent > 0);
578 // At this point, we've filled up the available space in the send queue.
579
Danil Chapovalov4d715382022-08-19 10:28:40 +0200580 if (packets_in_flight_ > 0) {
581 // If there are packet tasks, attempt to continue sending after giving
582 // those packets time to process, which should free up the send buffer.
583 rtc::Thread::Current()->PostDelayedTask([this] { WriteData(); },
584 TimeDelta::Millis(10));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000585 } else {
586 if (!remote_.isReceiveBufferFull()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100587 RTC_LOG(LS_ERROR) << "This shouldn't happen - the send buffer is full, "
Jonas Olssond7d762d2018-03-28 09:47:51 +0200588 "the receive buffer is not, and there are no "
589 "remaining messages to process.";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000590 }
591 send_stream_.GetPosition(&position);
592 send_position_.push_back(position);
593
594 // Drain the receiver buffer.
595 ReadUntilIOPending();
596 }
597 }
598
599 private:
600 rtc::MemoryStream send_stream_;
601 rtc::MemoryStream recv_stream_;
602
603 std::vector<size_t> send_position_;
604 std::vector<size_t> recv_position_;
605};
606
607// Basic end-to-end data transfer tests
608
609// Test the normal case of sending data from one side to the other.
610TEST_F(PseudoTcpTest, TestSend) {
611 SetLocalMtu(1500);
612 SetRemoteMtu(1500);
613 TestTransfer(1000000);
614}
615
616// Test sending data with a 50 ms RTT. Transmission should take longer due
617// to a slower ramp-up in send rate.
618TEST_F(PseudoTcpTest, TestSendWithDelay) {
619 SetLocalMtu(1500);
620 SetRemoteMtu(1500);
621 SetDelay(50);
622 TestTransfer(1000000);
623}
624
625// Test sending data with packet loss. Transmission should take much longer due
626// to send back-off when loss occurs.
627TEST_F(PseudoTcpTest, TestSendWithLoss) {
628 SetLocalMtu(1500);
629 SetRemoteMtu(1500);
630 SetLoss(10);
631 TestTransfer(100000); // less data so test runs faster
632}
633
634// Test sending data with a 50 ms RTT and 10% packet loss. Transmission should
635// take much longer due to send back-off and slower detection of loss.
636TEST_F(PseudoTcpTest, TestSendWithDelayAndLoss) {
637 SetLocalMtu(1500);
638 SetRemoteMtu(1500);
639 SetDelay(50);
640 SetLoss(10);
641 TestTransfer(100000); // less data so test runs faster
642}
643
644// Test sending data with 10% packet loss and Nagling disabled. Transmission
645// should take about the same time as with Nagling enabled.
646TEST_F(PseudoTcpTest, TestSendWithLossAndOptNaglingOff) {
647 SetLocalMtu(1500);
648 SetRemoteMtu(1500);
649 SetLoss(10);
650 SetOptNagling(false);
651 TestTransfer(100000); // less data so test runs faster
652}
653
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700654// Regression test for bugs.webrtc.org/9208.
655//
656// This bug resulted in corrupted data if a "connect" segment was received after
657// a data segment. This is only possible if:
658//
659// * The initial "connect" segment is lost, and retransmitted later.
660// * Both sides send "connect"s simultaneously, such that the local side thinks
661// a connection is established even before its "connect" has been
662// acknowledged.
663// * Nagle algorithm disabled, allowing a data segment to be sent before the
664// "connect" has been acknowledged.
665TEST_F(PseudoTcpTest,
666 TestSendWhenFirstPacketLostWithOptNaglingOffAndSimultaneousOpen) {
667 SetLocalMtu(1500);
668 SetRemoteMtu(1500);
669 DropNextPacket();
670 SetOptNagling(false);
671 SetSimultaneousOpen(true);
672 TestTransfer(10000);
673}
674
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000675// Test sending data with 10% packet loss and Delayed ACK disabled.
676// Transmission should be slightly faster than with it enabled.
677TEST_F(PseudoTcpTest, TestSendWithLossAndOptAckDelayOff) {
678 SetLocalMtu(1500);
679 SetRemoteMtu(1500);
680 SetLoss(10);
681 SetOptAckDelay(0);
682 TestTransfer(100000);
683}
684
685// Test sending data with 50ms delay and Nagling disabled.
686TEST_F(PseudoTcpTest, TestSendWithDelayAndOptNaglingOff) {
687 SetLocalMtu(1500);
688 SetRemoteMtu(1500);
689 SetDelay(50);
690 SetOptNagling(false);
691 TestTransfer(100000); // less data so test runs faster
692}
693
694// Test sending data with 50ms delay and Delayed ACK disabled.
695TEST_F(PseudoTcpTest, TestSendWithDelayAndOptAckDelayOff) {
696 SetLocalMtu(1500);
697 SetRemoteMtu(1500);
698 SetDelay(50);
699 SetOptAckDelay(0);
700 TestTransfer(100000); // less data so test runs faster
701}
702
703// Test a large receive buffer with a sender that doesn't support scaling.
704TEST_F(PseudoTcpTest, TestSendRemoteNoWindowScale) {
705 SetLocalMtu(1500);
706 SetRemoteMtu(1500);
707 SetLocalOptRcvBuf(100000);
708 DisableRemoteWindowScale();
709 TestTransfer(1000000);
710}
711
712// Test a large sender-side receive buffer with a receiver that doesn't support
713// scaling.
714TEST_F(PseudoTcpTest, TestSendLocalNoWindowScale) {
715 SetLocalMtu(1500);
716 SetRemoteMtu(1500);
717 SetRemoteOptRcvBuf(100000);
718 DisableLocalWindowScale();
719 TestTransfer(1000000);
720}
721
722// Test when both sides use window scaling.
723TEST_F(PseudoTcpTest, TestSendBothUseWindowScale) {
724 SetLocalMtu(1500);
725 SetRemoteMtu(1500);
726 SetRemoteOptRcvBuf(100000);
727 SetLocalOptRcvBuf(100000);
728 TestTransfer(1000000);
729}
730
731// Test using a large window scale value.
732TEST_F(PseudoTcpTest, TestSendLargeInFlight) {
733 SetLocalMtu(1500);
734 SetRemoteMtu(1500);
735 SetRemoteOptRcvBuf(100000);
736 SetLocalOptRcvBuf(100000);
737 SetOptSndBuf(150000);
738 TestTransfer(1000000);
739}
740
741TEST_F(PseudoTcpTest, TestSendBothUseLargeWindowScale) {
742 SetLocalMtu(1500);
743 SetRemoteMtu(1500);
744 SetRemoteOptRcvBuf(1000000);
745 SetLocalOptRcvBuf(1000000);
746 TestTransfer(10000000);
747}
748
749// Test using a small receive buffer.
750TEST_F(PseudoTcpTest, TestSendSmallReceiveBuffer) {
751 SetLocalMtu(1500);
752 SetRemoteMtu(1500);
753 SetRemoteOptRcvBuf(10000);
754 SetLocalOptRcvBuf(10000);
755 TestTransfer(1000000);
756}
757
758// Test using a very small receive buffer.
759TEST_F(PseudoTcpTest, TestSendVerySmallReceiveBuffer) {
760 SetLocalMtu(1500);
761 SetRemoteMtu(1500);
762 SetRemoteOptRcvBuf(100);
763 SetLocalOptRcvBuf(100);
764 TestTransfer(100000);
765}
766
767// Ping-pong (request/response) tests
768
769// Test sending <= 1x MTU of data in each ping/pong. Should take <10ms.
770TEST_F(PseudoTcpTestPingPong, TestPingPong1xMtu) {
771 SetLocalMtu(1500);
772 SetRemoteMtu(1500);
773 TestPingPong(100, 100);
774}
775
776// Test sending 2x-3x MTU of data in each ping/pong. Should take <10ms.
777TEST_F(PseudoTcpTestPingPong, TestPingPong3xMtu) {
778 SetLocalMtu(1500);
779 SetRemoteMtu(1500);
780 TestPingPong(400, 100);
781}
782
783// Test sending 1x-2x MTU of data in each ping/pong.
784// Should take ~1s, due to interaction between Nagling and Delayed ACK.
785TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtu) {
786 SetLocalMtu(1500);
787 SetRemoteMtu(1500);
788 TestPingPong(2000, 5);
789}
790
791// Test sending 1x-2x MTU of data in each ping/pong with Delayed ACK off.
792// Should take <10ms.
793TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtuWithAckDelayOff) {
794 SetLocalMtu(1500);
795 SetRemoteMtu(1500);
796 SetOptAckDelay(0);
797 TestPingPong(2000, 100);
798}
799
800// Test sending 1x-2x MTU of data in each ping/pong with Nagling off.
801// Should take <10ms.
802TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtuWithNaglingOff) {
803 SetLocalMtu(1500);
804 SetRemoteMtu(1500);
805 SetOptNagling(false);
806 TestPingPong(2000, 5);
807}
808
809// Test sending a ping as pair of short (non-full) segments.
810// Should take ~1s, due to Delayed ACK interaction with Nagling.
811TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegments) {
812 SetLocalMtu(1500);
813 SetRemoteMtu(1500);
814 SetOptAckDelay(5000);
Steve Antoncc65bd02017-11-29 10:19:58 -0800815 SetBytesPerSend(50); // i.e. two Send calls per payload
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000816 TestPingPong(100, 5);
817}
818
819// Test sending ping as a pair of short (non-full) segments, with Nagling off.
820// Should take <10ms.
821TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegmentsWithNaglingOff) {
822 SetLocalMtu(1500);
823 SetRemoteMtu(1500);
824 SetOptNagling(false);
Steve Antoncc65bd02017-11-29 10:19:58 -0800825 SetBytesPerSend(50); // i.e. two Send calls per payload
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000826 TestPingPong(100, 5);
827}
828
829// Test sending <= 1x MTU of data ping/pong, in two segments, no Delayed ACK.
830// Should take ~1s.
831TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegmentsWithAckDelayOff) {
832 SetLocalMtu(1500);
833 SetRemoteMtu(1500);
Steve Antoncc65bd02017-11-29 10:19:58 -0800834 SetBytesPerSend(50); // i.e. two Send calls per payload
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000835 SetOptAckDelay(0);
836 TestPingPong(100, 5);
837}
838
839// Test that receive window expands and contract correctly.
840TEST_F(PseudoTcpTestReceiveWindow, TestReceiveWindow) {
841 SetLocalMtu(1500);
842 SetRemoteMtu(1500);
843 SetOptNagling(false);
844 SetOptAckDelay(0);
845 TestTransfer(1024 * 1000);
846}
847
848// Test setting send window size to a very small value.
849TEST_F(PseudoTcpTestReceiveWindow, TestSetVerySmallSendWindowSize) {
850 SetLocalMtu(1500);
851 SetRemoteMtu(1500);
852 SetOptNagling(false);
853 SetOptAckDelay(0);
854 SetOptSndBuf(900);
855 TestTransfer(1024 * 1000);
856 EXPECT_EQ(900u, EstimateSendWindowSize());
857}
858
859// Test setting receive window size to a value other than default.
860TEST_F(PseudoTcpTestReceiveWindow, TestSetReceiveWindowSize) {
861 SetLocalMtu(1500);
862 SetRemoteMtu(1500);
863 SetOptNagling(false);
864 SetOptAckDelay(0);
865 SetRemoteOptRcvBuf(100000);
866 SetLocalOptRcvBuf(100000);
867 TestTransfer(1024 * 1000);
868 EXPECT_EQ(100000u, EstimateReceiveWindowSize());
869}
870
871/* Test sending data with mismatched MTUs. We should detect this and reduce
872// our packet size accordingly.
Steve Anton5c8231c2017-12-06 10:39:22 -0800873// TODO(?): This doesn't actually work right now. The current code
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000874// doesn't detect if the MTU is set too high on either side.
875TEST_F(PseudoTcpTest, TestSendWithMismatchedMtus) {
876 SetLocalMtu(1500);
877 SetRemoteMtu(1280);
878 TestTransfer(1000000);
879}
880*/