blob: debddb217ed119fbffac078c6ecefc77fb106cc9 [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) {
235 char ch = static_cast<char>(i);
236 send_stream_.Write(&ch, 1, NULL, NULL);
237 }
238 send_stream_.Rewind();
239 // Prepare the receive stream.
240 recv_stream_.ReserveSize(size);
241 // Connect and wait until connected.
Honghai Zhang82d78622016-05-06 11:29:15 -0700242 start = rtc::Time32();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000243 EXPECT_EQ(0, Connect());
244 EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
245 // Sending will start from OnTcpWriteable and complete when all data has
246 // been received.
247 EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
Honghai Zhang82d78622016-05-06 11:29:15 -0700248 elapsed = rtc::Time32() - start;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000249 recv_stream_.GetSize(&received);
250 // Ensure we closed down OK and we got the right data.
Steve Anton5c8231c2017-12-06 10:39:22 -0800251 // TODO(?): Ensure the errors are cleared properly.
Steve Antoncc65bd02017-11-29 10:19:58 -0800252 // EXPECT_EQ(0, local_.GetError());
253 // EXPECT_EQ(0, remote_.GetError());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000254 EXPECT_EQ(static_cast<size_t>(size), received);
Steve Antoncc65bd02017-11-29 10:19:58 -0800255 EXPECT_EQ(0,
256 memcmp(send_stream_.GetBuffer(), recv_stream_.GetBuffer(), size));
Mirko Bonadei675513b2017-11-09 11:09:25 +0100257 RTC_LOG(LS_INFO) << "Transferred " << received << " bytes in " << elapsed
258 << " ms (" << size * 8 / elapsed << " Kbps)";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000259 }
260
261 private:
262 // IPseudoTcpNotify interface
263
264 virtual void OnTcpReadable(PseudoTcp* tcp) {
265 // Stream bytes to the recv stream as they arrive.
266 if (tcp == &remote_) {
267 ReadData();
268
Steve Anton5c8231c2017-12-06 10:39:22 -0800269 // TODO(?): OnTcpClosed() is currently only notified on error -
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000270 // there is no on-the-wire equivalent of TCP FIN.
271 // So we fake the notification when all the data has been read.
272 size_t received, required;
273 recv_stream_.GetPosition(&received);
274 send_stream_.GetSize(&required);
275 if (received == required)
276 OnTcpClosed(&remote_, 0);
277 }
278 }
279 virtual void OnTcpWriteable(PseudoTcp* tcp) {
280 // Write bytes from the send stream when we can.
281 // Shut down when we've sent everything.
282 if (tcp == &local_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100283 RTC_LOG(LS_VERBOSE) << "Flow Control Lifted";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000284 bool done;
285 WriteData(&done);
286 if (done) {
287 Close();
288 }
289 }
290 }
291
292 void ReadData() {
293 char block[kBlockSize];
294 size_t position;
295 int rcvd;
296 do {
297 rcvd = remote_.Recv(block, sizeof(block));
298 if (rcvd != -1) {
299 recv_stream_.Write(block, rcvd, NULL, NULL);
300 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100301 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000302 }
303 } while (rcvd > 0);
304 }
305 void WriteData(bool* done) {
306 size_t position, tosend;
307 int sent;
308 char block[kBlockSize];
309 do {
310 send_stream_.GetPosition(&position);
311 if (send_stream_.Read(block, sizeof(block), &tosend, NULL) !=
312 rtc::SR_EOS) {
313 sent = local_.Send(block, tosend);
314 UpdateLocalClock();
315 if (sent != -1) {
316 send_stream_.SetPosition(position + sent);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100317 RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000318 } else {
319 send_stream_.SetPosition(position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100320 RTC_LOG(LS_VERBOSE) << "Flow Controlled";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000321 }
322 } else {
323 sent = static_cast<int>(tosend = 0);
324 }
325 } while (sent > 0);
326 *done = (tosend == 0);
327 }
328
329 private:
330 rtc::MemoryStream send_stream_;
331 rtc::MemoryStream recv_stream_;
332};
333
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000334class PseudoTcpTestPingPong : public PseudoTcpTestBase {
335 public:
336 PseudoTcpTestPingPong()
337 : iterations_remaining_(0),
Steve Antoncc65bd02017-11-29 10:19:58 -0800338 sender_(NULL),
339 receiver_(NULL),
340 bytes_per_send_(0) {}
341 void SetBytesPerSend(int bytes) { bytes_per_send_ = bytes; }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000342 void TestPingPong(int size, int iterations) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200343 uint32_t start, elapsed;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000344 iterations_remaining_ = iterations;
345 receiver_ = &remote_;
346 sender_ = &local_;
347 // Create some dummy data to send.
348 send_stream_.ReserveSize(size);
349 for (int i = 0; i < size; ++i) {
350 char ch = static_cast<char>(i);
351 send_stream_.Write(&ch, 1, NULL, NULL);
352 }
353 send_stream_.Rewind();
354 // Prepare the receive stream.
355 recv_stream_.ReserveSize(size);
356 // Connect and wait until connected.
Honghai Zhang82d78622016-05-06 11:29:15 -0700357 start = rtc::Time32();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000358 EXPECT_EQ(0, Connect());
359 EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
360 // Sending will start from OnTcpWriteable and stop when the required
361 // number of iterations have completed.
362 EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
363 elapsed = rtc::TimeSince(start);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100364 RTC_LOG(LS_INFO) << "Performed " << iterations << " pings in " << elapsed
365 << " ms";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000366 }
367
368 private:
369 // IPseudoTcpNotify interface
370
371 virtual void OnTcpReadable(PseudoTcp* tcp) {
372 if (tcp != receiver_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100373 RTC_LOG_F(LS_ERROR) << "unexpected OnTcpReadable";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000374 return;
375 }
376 // Stream bytes to the recv stream as they arrive.
377 ReadData();
378 // If we've received the desired amount of data, rewind things
379 // and send it back the other way!
380 size_t position, desired;
381 recv_stream_.GetPosition(&position);
382 send_stream_.GetSize(&desired);
383 if (position == desired) {
384 if (receiver_ == &local_ && --iterations_remaining_ == 0) {
385 Close();
Steve Anton5c8231c2017-12-06 10:39:22 -0800386 // TODO(?): Fake OnTcpClosed() on the receiver for now.
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000387 OnTcpClosed(&remote_, 0);
388 return;
389 }
390 PseudoTcp* tmp = receiver_;
391 receiver_ = sender_;
392 sender_ = tmp;
393 recv_stream_.Rewind();
394 send_stream_.Rewind();
395 OnTcpWriteable(sender_);
396 }
397 }
398 virtual void OnTcpWriteable(PseudoTcp* tcp) {
399 if (tcp != sender_)
400 return;
401 // Write bytes from the send stream when we can.
402 // Shut down when we've sent everything.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100403 RTC_LOG(LS_VERBOSE) << "Flow Control Lifted";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000404 WriteData();
405 }
406
407 void ReadData() {
408 char block[kBlockSize];
409 size_t position;
410 int rcvd;
411 do {
412 rcvd = receiver_->Recv(block, sizeof(block));
413 if (rcvd != -1) {
414 recv_stream_.Write(block, rcvd, NULL, NULL);
415 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100416 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000417 }
418 } while (rcvd > 0);
419 }
420 void WriteData() {
421 size_t position, tosend;
422 int sent;
423 char block[kBlockSize];
424 do {
425 send_stream_.GetPosition(&position);
426 tosend = bytes_per_send_ ? bytes_per_send_ : sizeof(block);
Steve Antoncc65bd02017-11-29 10:19:58 -0800427 if (send_stream_.Read(block, tosend, &tosend, NULL) != rtc::SR_EOS) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000428 sent = sender_->Send(block, tosend);
429 UpdateLocalClock();
430 if (sent != -1) {
431 send_stream_.SetPosition(position + sent);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100432 RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000433 } else {
434 send_stream_.SetPosition(position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100435 RTC_LOG(LS_VERBOSE) << "Flow Controlled";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000436 }
437 } else {
438 sent = static_cast<int>(tosend = 0);
439 }
440 } while (sent > 0);
441 }
442
443 private:
444 int iterations_remaining_;
445 PseudoTcp* sender_;
446 PseudoTcp* receiver_;
447 int bytes_per_send_;
448};
449
450// Fill the receiver window until it is full, drain it and then
451// fill it with the same amount. This is to test that receiver window
452// contracts and enlarges correctly.
453class PseudoTcpTestReceiveWindow : public PseudoTcpTestBase {
454 public:
Artem Titov2dbb4c92021-07-26 15:12:41 +0200455 // Not all the data are transfered, `size` just need to be big enough
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000456 // to fill up the receiver window twice.
457 void TestTransfer(int size) {
458 // Create some dummy data to send.
459 send_stream_.ReserveSize(size);
460 for (int i = 0; i < size; ++i) {
461 char ch = static_cast<char>(i);
462 send_stream_.Write(&ch, 1, NULL, NULL);
463 }
464 send_stream_.Rewind();
465
466 // Prepare the receive stream.
467 recv_stream_.ReserveSize(size);
468
469 // Connect and wait until connected.
470 EXPECT_EQ(0, Connect());
471 EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
472
Danil Chapovalov4d715382022-08-19 10:28:40 +0200473 TaskQueueBase::Current()->PostTask([this] { WriteData(); });
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000474 EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
475
476 ASSERT_EQ(2u, send_position_.size());
477 ASSERT_EQ(2u, recv_position_.size());
478
479 const size_t estimated_recv_window = EstimateReceiveWindowSize();
480
481 // The difference in consecutive send positions should equal the
482 // receive window size or match very closely. This verifies that receive
483 // window is open after receiver drained all the data.
484 const size_t send_position_diff = send_position_[1] - send_position_[0];
485 EXPECT_GE(1024u, estimated_recv_window - send_position_diff);
486
487 // Receiver drained the receive window twice.
488 EXPECT_EQ(2 * estimated_recv_window, recv_position_[1]);
489 }
490
Peter Boström0c4e06b2015-10-07 12:23:21 +0200491 uint32_t EstimateReceiveWindowSize() const {
492 return static_cast<uint32_t>(recv_position_[0]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000493 }
494
Peter Boström0c4e06b2015-10-07 12:23:21 +0200495 uint32_t EstimateSendWindowSize() const {
496 return static_cast<uint32_t>(send_position_[0] - recv_position_[0]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000497 }
498
499 private:
500 // IPseudoTcpNotify interface
Steve Antoncc65bd02017-11-29 10:19:58 -0800501 virtual void OnTcpReadable(PseudoTcp* tcp) {}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000502
Steve Antoncc65bd02017-11-29 10:19:58 -0800503 virtual void OnTcpWriteable(PseudoTcp* tcp) {}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000504
505 void ReadUntilIOPending() {
506 char block[kBlockSize];
507 size_t position;
508 int rcvd;
509
510 do {
511 rcvd = remote_.Recv(block, sizeof(block));
512 if (rcvd != -1) {
513 recv_stream_.Write(block, rcvd, NULL, NULL);
514 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100515 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000516 }
517 } while (rcvd > 0);
518
519 recv_stream_.GetPosition(&position);
520 recv_position_.push_back(position);
521
522 // Disconnect if we have done two transfers.
523 if (recv_position_.size() == 2u) {
524 Close();
525 OnTcpClosed(&remote_, 0);
526 } else {
527 WriteData();
528 }
529 }
530
531 void WriteData() {
532 size_t position, tosend;
533 int sent;
534 char block[kBlockSize];
535 do {
536 send_stream_.GetPosition(&position);
537 if (send_stream_.Read(block, sizeof(block), &tosend, NULL) !=
538 rtc::SR_EOS) {
539 sent = local_.Send(block, tosend);
540 UpdateLocalClock();
541 if (sent != -1) {
542 send_stream_.SetPosition(position + sent);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100543 RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000544 } else {
545 send_stream_.SetPosition(position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100546 RTC_LOG(LS_VERBOSE) << "Flow Controlled";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000547 }
548 } else {
549 sent = static_cast<int>(tosend = 0);
550 }
551 } while (sent > 0);
552 // At this point, we've filled up the available space in the send queue.
553
Danil Chapovalov4d715382022-08-19 10:28:40 +0200554 if (packets_in_flight_ > 0) {
555 // If there are packet tasks, attempt to continue sending after giving
556 // those packets time to process, which should free up the send buffer.
557 rtc::Thread::Current()->PostDelayedTask([this] { WriteData(); },
558 TimeDelta::Millis(10));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000559 } else {
560 if (!remote_.isReceiveBufferFull()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100561 RTC_LOG(LS_ERROR) << "This shouldn't happen - the send buffer is full, "
Jonas Olssond7d762d2018-03-28 09:47:51 +0200562 "the receive buffer is not, and there are no "
563 "remaining messages to process.";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000564 }
565 send_stream_.GetPosition(&position);
566 send_position_.push_back(position);
567
568 // Drain the receiver buffer.
569 ReadUntilIOPending();
570 }
571 }
572
573 private:
574 rtc::MemoryStream send_stream_;
575 rtc::MemoryStream recv_stream_;
576
577 std::vector<size_t> send_position_;
578 std::vector<size_t> recv_position_;
579};
580
581// Basic end-to-end data transfer tests
582
583// Test the normal case of sending data from one side to the other.
584TEST_F(PseudoTcpTest, TestSend) {
585 SetLocalMtu(1500);
586 SetRemoteMtu(1500);
587 TestTransfer(1000000);
588}
589
590// Test sending data with a 50 ms RTT. Transmission should take longer due
591// to a slower ramp-up in send rate.
592TEST_F(PseudoTcpTest, TestSendWithDelay) {
593 SetLocalMtu(1500);
594 SetRemoteMtu(1500);
595 SetDelay(50);
596 TestTransfer(1000000);
597}
598
599// Test sending data with packet loss. Transmission should take much longer due
600// to send back-off when loss occurs.
601TEST_F(PseudoTcpTest, TestSendWithLoss) {
602 SetLocalMtu(1500);
603 SetRemoteMtu(1500);
604 SetLoss(10);
605 TestTransfer(100000); // less data so test runs faster
606}
607
608// Test sending data with a 50 ms RTT and 10% packet loss. Transmission should
609// take much longer due to send back-off and slower detection of loss.
610TEST_F(PseudoTcpTest, TestSendWithDelayAndLoss) {
611 SetLocalMtu(1500);
612 SetRemoteMtu(1500);
613 SetDelay(50);
614 SetLoss(10);
615 TestTransfer(100000); // less data so test runs faster
616}
617
618// Test sending data with 10% packet loss and Nagling disabled. Transmission
619// should take about the same time as with Nagling enabled.
620TEST_F(PseudoTcpTest, TestSendWithLossAndOptNaglingOff) {
621 SetLocalMtu(1500);
622 SetRemoteMtu(1500);
623 SetLoss(10);
624 SetOptNagling(false);
625 TestTransfer(100000); // less data so test runs faster
626}
627
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700628// Regression test for bugs.webrtc.org/9208.
629//
630// This bug resulted in corrupted data if a "connect" segment was received after
631// a data segment. This is only possible if:
632//
633// * The initial "connect" segment is lost, and retransmitted later.
634// * Both sides send "connect"s simultaneously, such that the local side thinks
635// a connection is established even before its "connect" has been
636// acknowledged.
637// * Nagle algorithm disabled, allowing a data segment to be sent before the
638// "connect" has been acknowledged.
639TEST_F(PseudoTcpTest,
640 TestSendWhenFirstPacketLostWithOptNaglingOffAndSimultaneousOpen) {
641 SetLocalMtu(1500);
642 SetRemoteMtu(1500);
643 DropNextPacket();
644 SetOptNagling(false);
645 SetSimultaneousOpen(true);
646 TestTransfer(10000);
647}
648
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000649// Test sending data with 10% packet loss and Delayed ACK disabled.
650// Transmission should be slightly faster than with it enabled.
651TEST_F(PseudoTcpTest, TestSendWithLossAndOptAckDelayOff) {
652 SetLocalMtu(1500);
653 SetRemoteMtu(1500);
654 SetLoss(10);
655 SetOptAckDelay(0);
656 TestTransfer(100000);
657}
658
659// Test sending data with 50ms delay and Nagling disabled.
660TEST_F(PseudoTcpTest, TestSendWithDelayAndOptNaglingOff) {
661 SetLocalMtu(1500);
662 SetRemoteMtu(1500);
663 SetDelay(50);
664 SetOptNagling(false);
665 TestTransfer(100000); // less data so test runs faster
666}
667
668// Test sending data with 50ms delay and Delayed ACK disabled.
669TEST_F(PseudoTcpTest, TestSendWithDelayAndOptAckDelayOff) {
670 SetLocalMtu(1500);
671 SetRemoteMtu(1500);
672 SetDelay(50);
673 SetOptAckDelay(0);
674 TestTransfer(100000); // less data so test runs faster
675}
676
677// Test a large receive buffer with a sender that doesn't support scaling.
678TEST_F(PseudoTcpTest, TestSendRemoteNoWindowScale) {
679 SetLocalMtu(1500);
680 SetRemoteMtu(1500);
681 SetLocalOptRcvBuf(100000);
682 DisableRemoteWindowScale();
683 TestTransfer(1000000);
684}
685
686// Test a large sender-side receive buffer with a receiver that doesn't support
687// scaling.
688TEST_F(PseudoTcpTest, TestSendLocalNoWindowScale) {
689 SetLocalMtu(1500);
690 SetRemoteMtu(1500);
691 SetRemoteOptRcvBuf(100000);
692 DisableLocalWindowScale();
693 TestTransfer(1000000);
694}
695
696// Test when both sides use window scaling.
697TEST_F(PseudoTcpTest, TestSendBothUseWindowScale) {
698 SetLocalMtu(1500);
699 SetRemoteMtu(1500);
700 SetRemoteOptRcvBuf(100000);
701 SetLocalOptRcvBuf(100000);
702 TestTransfer(1000000);
703}
704
705// Test using a large window scale value.
706TEST_F(PseudoTcpTest, TestSendLargeInFlight) {
707 SetLocalMtu(1500);
708 SetRemoteMtu(1500);
709 SetRemoteOptRcvBuf(100000);
710 SetLocalOptRcvBuf(100000);
711 SetOptSndBuf(150000);
712 TestTransfer(1000000);
713}
714
715TEST_F(PseudoTcpTest, TestSendBothUseLargeWindowScale) {
716 SetLocalMtu(1500);
717 SetRemoteMtu(1500);
718 SetRemoteOptRcvBuf(1000000);
719 SetLocalOptRcvBuf(1000000);
720 TestTransfer(10000000);
721}
722
723// Test using a small receive buffer.
724TEST_F(PseudoTcpTest, TestSendSmallReceiveBuffer) {
725 SetLocalMtu(1500);
726 SetRemoteMtu(1500);
727 SetRemoteOptRcvBuf(10000);
728 SetLocalOptRcvBuf(10000);
729 TestTransfer(1000000);
730}
731
732// Test using a very small receive buffer.
733TEST_F(PseudoTcpTest, TestSendVerySmallReceiveBuffer) {
734 SetLocalMtu(1500);
735 SetRemoteMtu(1500);
736 SetRemoteOptRcvBuf(100);
737 SetLocalOptRcvBuf(100);
738 TestTransfer(100000);
739}
740
741// Ping-pong (request/response) tests
742
743// Test sending <= 1x MTU of data in each ping/pong. Should take <10ms.
744TEST_F(PseudoTcpTestPingPong, TestPingPong1xMtu) {
745 SetLocalMtu(1500);
746 SetRemoteMtu(1500);
747 TestPingPong(100, 100);
748}
749
750// Test sending 2x-3x MTU of data in each ping/pong. Should take <10ms.
751TEST_F(PseudoTcpTestPingPong, TestPingPong3xMtu) {
752 SetLocalMtu(1500);
753 SetRemoteMtu(1500);
754 TestPingPong(400, 100);
755}
756
757// Test sending 1x-2x MTU of data in each ping/pong.
758// Should take ~1s, due to interaction between Nagling and Delayed ACK.
759TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtu) {
760 SetLocalMtu(1500);
761 SetRemoteMtu(1500);
762 TestPingPong(2000, 5);
763}
764
765// Test sending 1x-2x MTU of data in each ping/pong with Delayed ACK off.
766// Should take <10ms.
767TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtuWithAckDelayOff) {
768 SetLocalMtu(1500);
769 SetRemoteMtu(1500);
770 SetOptAckDelay(0);
771 TestPingPong(2000, 100);
772}
773
774// Test sending 1x-2x MTU of data in each ping/pong with Nagling off.
775// Should take <10ms.
776TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtuWithNaglingOff) {
777 SetLocalMtu(1500);
778 SetRemoteMtu(1500);
779 SetOptNagling(false);
780 TestPingPong(2000, 5);
781}
782
783// Test sending a ping as pair of short (non-full) segments.
784// Should take ~1s, due to Delayed ACK interaction with Nagling.
785TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegments) {
786 SetLocalMtu(1500);
787 SetRemoteMtu(1500);
788 SetOptAckDelay(5000);
Steve Antoncc65bd02017-11-29 10:19:58 -0800789 SetBytesPerSend(50); // i.e. two Send calls per payload
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000790 TestPingPong(100, 5);
791}
792
793// Test sending ping as a pair of short (non-full) segments, with Nagling off.
794// Should take <10ms.
795TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegmentsWithNaglingOff) {
796 SetLocalMtu(1500);
797 SetRemoteMtu(1500);
798 SetOptNagling(false);
Steve Antoncc65bd02017-11-29 10:19:58 -0800799 SetBytesPerSend(50); // i.e. two Send calls per payload
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000800 TestPingPong(100, 5);
801}
802
803// Test sending <= 1x MTU of data ping/pong, in two segments, no Delayed ACK.
804// Should take ~1s.
805TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegmentsWithAckDelayOff) {
806 SetLocalMtu(1500);
807 SetRemoteMtu(1500);
Steve Antoncc65bd02017-11-29 10:19:58 -0800808 SetBytesPerSend(50); // i.e. two Send calls per payload
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000809 SetOptAckDelay(0);
810 TestPingPong(100, 5);
811}
812
813// Test that receive window expands and contract correctly.
814TEST_F(PseudoTcpTestReceiveWindow, TestReceiveWindow) {
815 SetLocalMtu(1500);
816 SetRemoteMtu(1500);
817 SetOptNagling(false);
818 SetOptAckDelay(0);
819 TestTransfer(1024 * 1000);
820}
821
822// Test setting send window size to a very small value.
823TEST_F(PseudoTcpTestReceiveWindow, TestSetVerySmallSendWindowSize) {
824 SetLocalMtu(1500);
825 SetRemoteMtu(1500);
826 SetOptNagling(false);
827 SetOptAckDelay(0);
828 SetOptSndBuf(900);
829 TestTransfer(1024 * 1000);
830 EXPECT_EQ(900u, EstimateSendWindowSize());
831}
832
833// Test setting receive window size to a value other than default.
834TEST_F(PseudoTcpTestReceiveWindow, TestSetReceiveWindowSize) {
835 SetLocalMtu(1500);
836 SetRemoteMtu(1500);
837 SetOptNagling(false);
838 SetOptAckDelay(0);
839 SetRemoteOptRcvBuf(100000);
840 SetLocalOptRcvBuf(100000);
841 TestTransfer(1024 * 1000);
842 EXPECT_EQ(100000u, EstimateReceiveWindowSize());
843}
844
845/* Test sending data with mismatched MTUs. We should detect this and reduce
846// our packet size accordingly.
Steve Anton5c8231c2017-12-06 10:39:22 -0800847// TODO(?): This doesn't actually work right now. The current code
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000848// doesn't detect if the MTU is set too high on either side.
849TEST_F(PseudoTcpTest, TestSendWithMismatchedMtus) {
850 SetLocalMtu(1500);
851 SetRemoteMtu(1280);
852 TestTransfer(1000000);
853}
854*/