blob: c0f4a892d661ec667bc5b9fb5edd1da2696a379e [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
Yves Gerey3e707812018-11-28 16:47:49 +010011#include <string.h>
andresp@webrtc.orgff689be2015-02-12 11:54:26 +000012#include <algorithm>
Yves Gerey3e707812018-11-28 16:47:49 +010013#include <cstddef>
Steve Anton5c8231c2017-12-06 10:39:22 -080014#include <string>
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000015#include <vector>
16
Steve Anton10542f22019-01-11 09:11:00 -080017#include "p2p/base/pseudo_tcp.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "rtc_base/gunit.h"
19#include "rtc_base/helpers.h"
Yves Gerey3e707812018-11-28 16:47:49 +010020#include "rtc_base/location.h"
21#include "rtc_base/logging.h"
Niels Möllere7547d52018-11-01 09:33:08 +010022#include "rtc_base/memory_stream.h"
Steve Anton10542f22019-01-11 09:11:00 -080023#include "rtc_base/message_handler.h"
24#include "rtc_base/message_queue.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025#include "rtc_base/thread.h"
Steve Anton10542f22019-01-11 09:11:00 -080026#include "rtc_base/time_utils.h"
Yves Gerey3e707812018-11-28 16:47:49 +010027#include "test/gtest.h"
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000028
29using cricket::PseudoTcp;
30
31static const int kConnectTimeoutMs = 10000; // ~3 * default RTO of 3000ms
32static const int kTransferTimeoutMs = 15000;
33static const int kBlockSize = 4096;
34
35class PseudoTcpForTest : public cricket::PseudoTcp {
36 public:
Peter Boström0c4e06b2015-10-07 12:23:21 +020037 PseudoTcpForTest(cricket::IPseudoTcpNotify* notify, uint32_t conv)
38 : PseudoTcp(notify, conv) {}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000039
Steve Antoncc65bd02017-11-29 10:19:58 -080040 bool isReceiveBufferFull() const { return PseudoTcp::isReceiveBufferFull(); }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000041
Steve Antoncc65bd02017-11-29 10:19:58 -080042 void disableWindowScale() { PseudoTcp::disableWindowScale(); }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000043};
44
Mirko Bonadei6a489f22019-04-09 15:11:12 +020045class PseudoTcpTestBase : public ::testing::Test,
Steve Antoncc65bd02017-11-29 10:19:58 -080046 public rtc::MessageHandler,
47 public cricket::IPseudoTcpNotify {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000048 public:
49 PseudoTcpTestBase()
50 : local_(this, 1),
51 remote_(this, 1),
52 have_connected_(false),
53 have_disconnected_(false),
54 local_mtu_(65535),
55 remote_mtu_(65535),
56 delay_(0),
57 loss_(0) {
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -070058 // Set use of the test RNG to get predictable loss patterns. Otherwise,
59 // this test would occasionally get really unlucky loss and time out.
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000060 rtc::SetRandomTestMode(true);
61 }
62 ~PseudoTcpTestBase() {
63 // Put it back for the next test.
64 rtc::SetRandomTestMode(false);
65 }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -070066 // If true, both endpoints will send the "connect" segment simultaneously,
67 // rather than |local_| sending it followed by a response from |remote_|.
68 // Note that this is what chromoting ends up doing.
69 void SetSimultaneousOpen(bool enabled) { simultaneous_open_ = enabled; }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000070 void SetLocalMtu(int mtu) {
71 local_.NotifyMTU(mtu);
72 local_mtu_ = mtu;
73 }
74 void SetRemoteMtu(int mtu) {
75 remote_.NotifyMTU(mtu);
76 remote_mtu_ = mtu;
77 }
Steve Antoncc65bd02017-11-29 10:19:58 -080078 void SetDelay(int delay) { delay_ = delay; }
79 void SetLoss(int percent) { loss_ = percent; }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -070080 // Used to cause the initial "connect" segment to be lost, needed for a
81 // regression test.
82 void DropNextPacket() { drop_next_packet_ = true; }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000083 void SetOptNagling(bool enable_nagles) {
84 local_.SetOption(PseudoTcp::OPT_NODELAY, !enable_nagles);
85 remote_.SetOption(PseudoTcp::OPT_NODELAY, !enable_nagles);
86 }
87 void SetOptAckDelay(int ack_delay) {
88 local_.SetOption(PseudoTcp::OPT_ACKDELAY, ack_delay);
89 remote_.SetOption(PseudoTcp::OPT_ACKDELAY, ack_delay);
90 }
91 void SetOptSndBuf(int size) {
92 local_.SetOption(PseudoTcp::OPT_SNDBUF, size);
93 remote_.SetOption(PseudoTcp::OPT_SNDBUF, size);
94 }
95 void SetRemoteOptRcvBuf(int size) {
96 remote_.SetOption(PseudoTcp::OPT_RCVBUF, size);
97 }
98 void SetLocalOptRcvBuf(int size) {
99 local_.SetOption(PseudoTcp::OPT_RCVBUF, size);
100 }
Steve Antoncc65bd02017-11-29 10:19:58 -0800101 void DisableRemoteWindowScale() { remote_.disableWindowScale(); }
102 void DisableLocalWindowScale() { local_.disableWindowScale(); }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000103
104 protected:
105 int Connect() {
106 int ret = local_.Connect();
107 if (ret == 0) {
108 UpdateLocalClock();
109 }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700110 if (simultaneous_open_) {
111 ret = remote_.Connect();
112 if (ret == 0) {
113 UpdateRemoteClock();
114 }
115 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000116 return ret;
117 }
118 void Close() {
119 local_.Close(false);
120 UpdateLocalClock();
121 }
122
Steve Antoncc65bd02017-11-29 10:19:58 -0800123 enum {
124 MSG_LPACKET,
125 MSG_RPACKET,
126 MSG_LCLOCK,
127 MSG_RCLOCK,
128 MSG_IOCOMPLETE,
129 MSG_WRITE
130 };
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000131 virtual void OnTcpOpen(PseudoTcp* tcp) {
132 // Consider ourselves connected when the local side gets OnTcpOpen.
133 // OnTcpWriteable isn't fired at open, so we trigger it now.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100134 RTC_LOG(LS_VERBOSE) << "Opened";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000135 if (tcp == &local_) {
136 have_connected_ = true;
137 OnTcpWriteable(tcp);
138 }
139 }
140 // Test derived from the base should override
141 // virtual void OnTcpReadable(PseudoTcp* tcp)
142 // and
143 // virtual void OnTcpWritable(PseudoTcp* tcp)
Peter Boström0c4e06b2015-10-07 12:23:21 +0200144 virtual void OnTcpClosed(PseudoTcp* tcp, uint32_t error) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000145 // Consider ourselves closed when the remote side gets OnTcpClosed.
Steve Anton5c8231c2017-12-06 10:39:22 -0800146 // TODO(?): OnTcpClosed is only ever notified in case of error in
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000147 // the current implementation. Solicited close is not (yet) supported.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100148 RTC_LOG(LS_VERBOSE) << "Closed";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000149 EXPECT_EQ(0U, error);
150 if (tcp == &remote_) {
151 have_disconnected_ = true;
152 }
153 }
154 virtual WriteResult TcpWritePacket(PseudoTcp* tcp,
Steve Antoncc65bd02017-11-29 10:19:58 -0800155 const char* buffer,
156 size_t len) {
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700157 // Drop a packet if the test called DropNextPacket.
158 if (drop_next_packet_) {
159 drop_next_packet_ = false;
160 RTC_LOG(LS_VERBOSE) << "Dropping packet due to DropNextPacket, size="
161 << len;
162 return WR_SUCCESS;
163 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000164 // Randomly drop the desired percentage of packets.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200165 if (rtc::CreateRandomId() % 100 < static_cast<uint32_t>(loss_)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100166 RTC_LOG(LS_VERBOSE) << "Randomly dropping packet, size=" << len;
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700167 return WR_SUCCESS;
168 }
169 // Also drop packets that are larger than the configured MTU.
170 if (len > static_cast<size_t>(std::min(local_mtu_, remote_mtu_))) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100171 RTC_LOG(LS_VERBOSE) << "Dropping packet that exceeds path MTU, size="
172 << len;
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700173 return WR_SUCCESS;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000174 }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700175 int id = (tcp == &local_) ? MSG_RPACKET : MSG_LPACKET;
176 std::string packet(buffer, len);
177 rtc::Thread::Current()->PostDelayed(RTC_FROM_HERE, delay_, this, id,
178 rtc::WrapMessageData(packet));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000179 return WR_SUCCESS;
180 }
181
182 void UpdateLocalClock() { UpdateClock(&local_, MSG_LCLOCK); }
183 void UpdateRemoteClock() { UpdateClock(&remote_, MSG_RCLOCK); }
Peter Boström0c4e06b2015-10-07 12:23:21 +0200184 void UpdateClock(PseudoTcp* tcp, uint32_t message) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000185 long interval = 0; // NOLINT
186 tcp->GetNextClock(PseudoTcp::Now(), interval);
andresp@webrtc.orgff689be2015-02-12 11:54:26 +0000187 interval = std::max<int>(interval, 0L); // sometimes interval is < 0
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000188 rtc::Thread::Current()->Clear(this, message);
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700189 rtc::Thread::Current()->PostDelayed(RTC_FROM_HERE, interval, this, message);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000190 }
191
192 virtual void OnMessage(rtc::Message* message) {
193 switch (message->message_id) {
194 case MSG_LPACKET: {
Steve Antoncc65bd02017-11-29 10:19:58 -0800195 const std::string& s(rtc::UseMessageData<std::string>(message->pdata));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000196 local_.NotifyPacket(s.c_str(), s.size());
197 UpdateLocalClock();
198 break;
199 }
200 case MSG_RPACKET: {
Steve Antoncc65bd02017-11-29 10:19:58 -0800201 const std::string& s(rtc::UseMessageData<std::string>(message->pdata));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000202 remote_.NotifyPacket(s.c_str(), s.size());
203 UpdateRemoteClock();
204 break;
205 }
206 case MSG_LCLOCK:
207 local_.NotifyClock(PseudoTcp::Now());
208 UpdateLocalClock();
209 break;
210 case MSG_RCLOCK:
211 remote_.NotifyClock(PseudoTcp::Now());
212 UpdateRemoteClock();
213 break;
214 default:
215 break;
216 }
217 delete message->pdata;
218 }
219
220 PseudoTcpForTest local_;
221 PseudoTcpForTest remote_;
222 rtc::MemoryStream send_stream_;
223 rtc::MemoryStream recv_stream_;
224 bool have_connected_;
225 bool have_disconnected_;
226 int local_mtu_;
227 int remote_mtu_;
228 int delay_;
229 int loss_;
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700230 bool drop_next_packet_ = false;
231 bool simultaneous_open_ = false;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000232};
233
234class PseudoTcpTest : public PseudoTcpTestBase {
235 public:
236 void TestTransfer(int size) {
Honghai Zhang82d78622016-05-06 11:29:15 -0700237 uint32_t start;
238 int32_t elapsed;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000239 size_t received;
240 // Create some dummy data to send.
241 send_stream_.ReserveSize(size);
242 for (int i = 0; i < size; ++i) {
243 char ch = static_cast<char>(i);
244 send_stream_.Write(&ch, 1, NULL, NULL);
245 }
246 send_stream_.Rewind();
247 // Prepare the receive stream.
248 recv_stream_.ReserveSize(size);
249 // Connect and wait until connected.
Honghai Zhang82d78622016-05-06 11:29:15 -0700250 start = rtc::Time32();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000251 EXPECT_EQ(0, Connect());
252 EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
253 // Sending will start from OnTcpWriteable and complete when all data has
254 // been received.
255 EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
Honghai Zhang82d78622016-05-06 11:29:15 -0700256 elapsed = rtc::Time32() - start;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000257 recv_stream_.GetSize(&received);
258 // Ensure we closed down OK and we got the right data.
Steve Anton5c8231c2017-12-06 10:39:22 -0800259 // TODO(?): Ensure the errors are cleared properly.
Steve Antoncc65bd02017-11-29 10:19:58 -0800260 // EXPECT_EQ(0, local_.GetError());
261 // EXPECT_EQ(0, remote_.GetError());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000262 EXPECT_EQ(static_cast<size_t>(size), received);
Steve Antoncc65bd02017-11-29 10:19:58 -0800263 EXPECT_EQ(0,
264 memcmp(send_stream_.GetBuffer(), recv_stream_.GetBuffer(), size));
Mirko Bonadei675513b2017-11-09 11:09:25 +0100265 RTC_LOG(LS_INFO) << "Transferred " << received << " bytes in " << elapsed
266 << " ms (" << size * 8 / elapsed << " Kbps)";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000267 }
268
269 private:
270 // IPseudoTcpNotify interface
271
272 virtual void OnTcpReadable(PseudoTcp* tcp) {
273 // Stream bytes to the recv stream as they arrive.
274 if (tcp == &remote_) {
275 ReadData();
276
Steve Anton5c8231c2017-12-06 10:39:22 -0800277 // TODO(?): OnTcpClosed() is currently only notified on error -
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000278 // there is no on-the-wire equivalent of TCP FIN.
279 // So we fake the notification when all the data has been read.
280 size_t received, required;
281 recv_stream_.GetPosition(&received);
282 send_stream_.GetSize(&required);
283 if (received == required)
284 OnTcpClosed(&remote_, 0);
285 }
286 }
287 virtual void OnTcpWriteable(PseudoTcp* tcp) {
288 // Write bytes from the send stream when we can.
289 // Shut down when we've sent everything.
290 if (tcp == &local_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100291 RTC_LOG(LS_VERBOSE) << "Flow Control Lifted";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000292 bool done;
293 WriteData(&done);
294 if (done) {
295 Close();
296 }
297 }
298 }
299
300 void ReadData() {
301 char block[kBlockSize];
302 size_t position;
303 int rcvd;
304 do {
305 rcvd = remote_.Recv(block, sizeof(block));
306 if (rcvd != -1) {
307 recv_stream_.Write(block, rcvd, NULL, NULL);
308 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100309 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000310 }
311 } while (rcvd > 0);
312 }
313 void WriteData(bool* done) {
314 size_t position, tosend;
315 int sent;
316 char block[kBlockSize];
317 do {
318 send_stream_.GetPosition(&position);
319 if (send_stream_.Read(block, sizeof(block), &tosend, NULL) !=
320 rtc::SR_EOS) {
321 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) {
358 char ch = static_cast<char>(i);
359 send_stream_.Write(&ch, 1, NULL, NULL);
360 }
361 send_stream_.Rewind();
362 // Prepare the receive stream.
363 recv_stream_.ReserveSize(size);
364 // Connect and wait until connected.
Honghai Zhang82d78622016-05-06 11:29:15 -0700365 start = rtc::Time32();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000366 EXPECT_EQ(0, Connect());
367 EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
368 // Sending will start from OnTcpWriteable and stop when the required
369 // number of iterations have completed.
370 EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
371 elapsed = rtc::TimeSince(start);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100372 RTC_LOG(LS_INFO) << "Performed " << iterations << " pings in " << elapsed
373 << " ms";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000374 }
375
376 private:
377 // IPseudoTcpNotify interface
378
379 virtual void OnTcpReadable(PseudoTcp* tcp) {
380 if (tcp != receiver_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100381 RTC_LOG_F(LS_ERROR) << "unexpected OnTcpReadable";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000382 return;
383 }
384 // Stream bytes to the recv stream as they arrive.
385 ReadData();
386 // If we've received the desired amount of data, rewind things
387 // and send it back the other way!
388 size_t position, desired;
389 recv_stream_.GetPosition(&position);
390 send_stream_.GetSize(&desired);
391 if (position == desired) {
392 if (receiver_ == &local_ && --iterations_remaining_ == 0) {
393 Close();
Steve Anton5c8231c2017-12-06 10:39:22 -0800394 // TODO(?): Fake OnTcpClosed() on the receiver for now.
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000395 OnTcpClosed(&remote_, 0);
396 return;
397 }
398 PseudoTcp* tmp = receiver_;
399 receiver_ = sender_;
400 sender_ = tmp;
401 recv_stream_.Rewind();
402 send_stream_.Rewind();
403 OnTcpWriteable(sender_);
404 }
405 }
406 virtual void OnTcpWriteable(PseudoTcp* tcp) {
407 if (tcp != sender_)
408 return;
409 // Write bytes from the send stream when we can.
410 // Shut down when we've sent everything.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100411 RTC_LOG(LS_VERBOSE) << "Flow Control Lifted";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000412 WriteData();
413 }
414
415 void ReadData() {
416 char block[kBlockSize];
417 size_t position;
418 int rcvd;
419 do {
420 rcvd = receiver_->Recv(block, sizeof(block));
421 if (rcvd != -1) {
422 recv_stream_.Write(block, rcvd, NULL, NULL);
423 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100424 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000425 }
426 } while (rcvd > 0);
427 }
428 void WriteData() {
429 size_t position, tosend;
430 int sent;
431 char block[kBlockSize];
432 do {
433 send_stream_.GetPosition(&position);
434 tosend = bytes_per_send_ ? bytes_per_send_ : sizeof(block);
Steve Antoncc65bd02017-11-29 10:19:58 -0800435 if (send_stream_.Read(block, tosend, &tosend, NULL) != rtc::SR_EOS) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000436 sent = sender_->Send(block, tosend);
437 UpdateLocalClock();
438 if (sent != -1) {
439 send_stream_.SetPosition(position + sent);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100440 RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000441 } else {
442 send_stream_.SetPosition(position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100443 RTC_LOG(LS_VERBOSE) << "Flow Controlled";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000444 }
445 } else {
446 sent = static_cast<int>(tosend = 0);
447 }
448 } while (sent > 0);
449 }
450
451 private:
452 int iterations_remaining_;
453 PseudoTcp* sender_;
454 PseudoTcp* receiver_;
455 int bytes_per_send_;
456};
457
458// Fill the receiver window until it is full, drain it and then
459// fill it with the same amount. This is to test that receiver window
460// contracts and enlarges correctly.
461class PseudoTcpTestReceiveWindow : public PseudoTcpTestBase {
462 public:
463 // Not all the data are transfered, |size| just need to be big enough
464 // to fill up the receiver window twice.
465 void TestTransfer(int size) {
466 // Create some dummy data to send.
467 send_stream_.ReserveSize(size);
468 for (int i = 0; i < size; ++i) {
469 char ch = static_cast<char>(i);
470 send_stream_.Write(&ch, 1, NULL, NULL);
471 }
472 send_stream_.Rewind();
473
474 // Prepare the receive stream.
475 recv_stream_.ReserveSize(size);
476
477 // Connect and wait until connected.
478 EXPECT_EQ(0, Connect());
479 EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
480
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700481 rtc::Thread::Current()->Post(RTC_FROM_HERE, this, MSG_WRITE);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000482 EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
483
484 ASSERT_EQ(2u, send_position_.size());
485 ASSERT_EQ(2u, recv_position_.size());
486
487 const size_t estimated_recv_window = EstimateReceiveWindowSize();
488
489 // The difference in consecutive send positions should equal the
490 // receive window size or match very closely. This verifies that receive
491 // window is open after receiver drained all the data.
492 const size_t send_position_diff = send_position_[1] - send_position_[0];
493 EXPECT_GE(1024u, estimated_recv_window - send_position_diff);
494
495 // Receiver drained the receive window twice.
496 EXPECT_EQ(2 * estimated_recv_window, recv_position_[1]);
497 }
498
499 virtual void OnMessage(rtc::Message* message) {
500 int message_id = message->message_id;
501 PseudoTcpTestBase::OnMessage(message);
502
503 switch (message_id) {
504 case MSG_WRITE: {
505 WriteData();
506 break;
507 }
508 default:
509 break;
510 }
511 }
512
Peter Boström0c4e06b2015-10-07 12:23:21 +0200513 uint32_t EstimateReceiveWindowSize() const {
514 return static_cast<uint32_t>(recv_position_[0]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000515 }
516
Peter Boström0c4e06b2015-10-07 12:23:21 +0200517 uint32_t EstimateSendWindowSize() const {
518 return static_cast<uint32_t>(send_position_[0] - recv_position_[0]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000519 }
520
521 private:
522 // IPseudoTcpNotify interface
Steve Antoncc65bd02017-11-29 10:19:58 -0800523 virtual void OnTcpReadable(PseudoTcp* tcp) {}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000524
Steve Antoncc65bd02017-11-29 10:19:58 -0800525 virtual void OnTcpWriteable(PseudoTcp* tcp) {}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000526
527 void ReadUntilIOPending() {
528 char block[kBlockSize];
529 size_t position;
530 int rcvd;
531
532 do {
533 rcvd = remote_.Recv(block, sizeof(block));
534 if (rcvd != -1) {
535 recv_stream_.Write(block, rcvd, NULL, NULL);
536 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100537 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000538 }
539 } while (rcvd > 0);
540
541 recv_stream_.GetPosition(&position);
542 recv_position_.push_back(position);
543
544 // Disconnect if we have done two transfers.
545 if (recv_position_.size() == 2u) {
546 Close();
547 OnTcpClosed(&remote_, 0);
548 } else {
549 WriteData();
550 }
551 }
552
553 void WriteData() {
554 size_t position, tosend;
555 int sent;
556 char block[kBlockSize];
557 do {
558 send_stream_.GetPosition(&position);
559 if (send_stream_.Read(block, sizeof(block), &tosend, NULL) !=
560 rtc::SR_EOS) {
561 sent = local_.Send(block, tosend);
562 UpdateLocalClock();
563 if (sent != -1) {
564 send_stream_.SetPosition(position + sent);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100565 RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000566 } else {
567 send_stream_.SetPosition(position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100568 RTC_LOG(LS_VERBOSE) << "Flow Controlled";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000569 }
570 } else {
571 sent = static_cast<int>(tosend = 0);
572 }
573 } while (sent > 0);
574 // At this point, we've filled up the available space in the send queue.
575
Steve Antoncc65bd02017-11-29 10:19:58 -0800576 int message_queue_size = static_cast<int>(rtc::Thread::Current()->size());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000577 // The message queue will always have at least 2 messages, an RCLOCK and
578 // an LCLOCK, since they are added back on the delay queue at the same time
579 // they are pulled off and therefore are never really removed.
580 if (message_queue_size > 2) {
581 // If there are non-clock messages remaining, attempt to continue sending
582 // after giving those messages time to process, which should free up the
583 // send buffer.
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700584 rtc::Thread::Current()->PostDelayed(RTC_FROM_HERE, 10, this, MSG_WRITE);
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*/