blob: 3afe955783a4b32308e560320e9986357708dda9 [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>
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000018#include <vector>
19
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "rtc_base/gunit.h"
21#include "rtc_base/helpers.h"
Yves Gerey3e707812018-11-28 16:47:49 +010022#include "rtc_base/location.h"
23#include "rtc_base/logging.h"
Niels Möllere7547d52018-11-01 09:33:08 +010024#include "rtc_base/memory_stream.h"
Steve Anton10542f22019-01-11 09:11:00 -080025#include "rtc_base/message_handler.h"
26#include "rtc_base/message_queue.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020027#include "rtc_base/thread.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
31using cricket::PseudoTcp;
32
33static const int kConnectTimeoutMs = 10000; // ~3 * default RTO of 3000ms
34static const int kTransferTimeoutMs = 15000;
35static const int kBlockSize = 4096;
36
37class PseudoTcpForTest : public cricket::PseudoTcp {
38 public:
Peter Boström0c4e06b2015-10-07 12:23:21 +020039 PseudoTcpForTest(cricket::IPseudoTcpNotify* notify, uint32_t conv)
40 : PseudoTcp(notify, conv) {}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000041
Steve Antoncc65bd02017-11-29 10:19:58 -080042 bool isReceiveBufferFull() const { return PseudoTcp::isReceiveBufferFull(); }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000043
Steve Antoncc65bd02017-11-29 10:19:58 -080044 void disableWindowScale() { PseudoTcp::disableWindowScale(); }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000045};
46
Mirko Bonadei6a489f22019-04-09 15:11:12 +020047class PseudoTcpTestBase : public ::testing::Test,
Steve Antoncc65bd02017-11-29 10:19:58 -080048 public rtc::MessageHandler,
49 public cricket::IPseudoTcpNotify {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000050 public:
51 PseudoTcpTestBase()
52 : local_(this, 1),
53 remote_(this, 1),
54 have_connected_(false),
55 have_disconnected_(false),
56 local_mtu_(65535),
57 remote_mtu_(65535),
58 delay_(0),
59 loss_(0) {
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -070060 // Set use of the test RNG to get predictable loss patterns. Otherwise,
61 // this test would occasionally get really unlucky loss and time out.
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000062 rtc::SetRandomTestMode(true);
63 }
64 ~PseudoTcpTestBase() {
65 // Put it back for the next test.
66 rtc::SetRandomTestMode(false);
67 }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -070068 // If true, both endpoints will send the "connect" segment simultaneously,
69 // rather than |local_| sending it followed by a response from |remote_|.
70 // Note that this is what chromoting ends up doing.
71 void SetSimultaneousOpen(bool enabled) { simultaneous_open_ = enabled; }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000072 void SetLocalMtu(int mtu) {
73 local_.NotifyMTU(mtu);
74 local_mtu_ = mtu;
75 }
76 void SetRemoteMtu(int mtu) {
77 remote_.NotifyMTU(mtu);
78 remote_mtu_ = mtu;
79 }
Steve Antoncc65bd02017-11-29 10:19:58 -080080 void SetDelay(int delay) { delay_ = delay; }
81 void SetLoss(int percent) { loss_ = percent; }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -070082 // Used to cause the initial "connect" segment to be lost, needed for a
83 // regression test.
84 void DropNextPacket() { drop_next_packet_ = true; }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000085 void SetOptNagling(bool enable_nagles) {
86 local_.SetOption(PseudoTcp::OPT_NODELAY, !enable_nagles);
87 remote_.SetOption(PseudoTcp::OPT_NODELAY, !enable_nagles);
88 }
89 void SetOptAckDelay(int ack_delay) {
90 local_.SetOption(PseudoTcp::OPT_ACKDELAY, ack_delay);
91 remote_.SetOption(PseudoTcp::OPT_ACKDELAY, ack_delay);
92 }
93 void SetOptSndBuf(int size) {
94 local_.SetOption(PseudoTcp::OPT_SNDBUF, size);
95 remote_.SetOption(PseudoTcp::OPT_SNDBUF, size);
96 }
97 void SetRemoteOptRcvBuf(int size) {
98 remote_.SetOption(PseudoTcp::OPT_RCVBUF, size);
99 }
100 void SetLocalOptRcvBuf(int size) {
101 local_.SetOption(PseudoTcp::OPT_RCVBUF, size);
102 }
Steve Antoncc65bd02017-11-29 10:19:58 -0800103 void DisableRemoteWindowScale() { remote_.disableWindowScale(); }
104 void DisableLocalWindowScale() { local_.disableWindowScale(); }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000105
106 protected:
107 int Connect() {
108 int ret = local_.Connect();
109 if (ret == 0) {
110 UpdateLocalClock();
111 }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700112 if (simultaneous_open_) {
113 ret = remote_.Connect();
114 if (ret == 0) {
115 UpdateRemoteClock();
116 }
117 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000118 return ret;
119 }
120 void Close() {
121 local_.Close(false);
122 UpdateLocalClock();
123 }
124
Steve Antoncc65bd02017-11-29 10:19:58 -0800125 enum {
126 MSG_LPACKET,
127 MSG_RPACKET,
128 MSG_LCLOCK,
129 MSG_RCLOCK,
130 MSG_IOCOMPLETE,
131 MSG_WRITE
132 };
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000133 virtual void OnTcpOpen(PseudoTcp* tcp) {
134 // Consider ourselves connected when the local side gets OnTcpOpen.
135 // OnTcpWriteable isn't fired at open, so we trigger it now.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100136 RTC_LOG(LS_VERBOSE) << "Opened";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000137 if (tcp == &local_) {
138 have_connected_ = true;
139 OnTcpWriteable(tcp);
140 }
141 }
142 // Test derived from the base should override
143 // virtual void OnTcpReadable(PseudoTcp* tcp)
144 // and
145 // virtual void OnTcpWritable(PseudoTcp* tcp)
Peter Boström0c4e06b2015-10-07 12:23:21 +0200146 virtual void OnTcpClosed(PseudoTcp* tcp, uint32_t error) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000147 // Consider ourselves closed when the remote side gets OnTcpClosed.
Steve Anton5c8231c2017-12-06 10:39:22 -0800148 // TODO(?): OnTcpClosed is only ever notified in case of error in
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000149 // the current implementation. Solicited close is not (yet) supported.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100150 RTC_LOG(LS_VERBOSE) << "Closed";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000151 EXPECT_EQ(0U, error);
152 if (tcp == &remote_) {
153 have_disconnected_ = true;
154 }
155 }
156 virtual WriteResult TcpWritePacket(PseudoTcp* tcp,
Steve Antoncc65bd02017-11-29 10:19:58 -0800157 const char* buffer,
158 size_t len) {
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700159 // Drop a packet if the test called DropNextPacket.
160 if (drop_next_packet_) {
161 drop_next_packet_ = false;
162 RTC_LOG(LS_VERBOSE) << "Dropping packet due to DropNextPacket, size="
163 << len;
164 return WR_SUCCESS;
165 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000166 // Randomly drop the desired percentage of packets.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200167 if (rtc::CreateRandomId() % 100 < static_cast<uint32_t>(loss_)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100168 RTC_LOG(LS_VERBOSE) << "Randomly dropping packet, size=" << len;
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700169 return WR_SUCCESS;
170 }
171 // Also drop packets that are larger than the configured MTU.
172 if (len > static_cast<size_t>(std::min(local_mtu_, remote_mtu_))) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100173 RTC_LOG(LS_VERBOSE) << "Dropping packet that exceeds path MTU, size="
174 << len;
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700175 return WR_SUCCESS;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000176 }
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700177 int id = (tcp == &local_) ? MSG_RPACKET : MSG_LPACKET;
178 std::string packet(buffer, len);
179 rtc::Thread::Current()->PostDelayed(RTC_FROM_HERE, delay_, this, id,
180 rtc::WrapMessageData(packet));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000181 return WR_SUCCESS;
182 }
183
184 void UpdateLocalClock() { UpdateClock(&local_, MSG_LCLOCK); }
185 void UpdateRemoteClock() { UpdateClock(&remote_, MSG_RCLOCK); }
Peter Boström0c4e06b2015-10-07 12:23:21 +0200186 void UpdateClock(PseudoTcp* tcp, uint32_t message) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000187 long interval = 0; // NOLINT
188 tcp->GetNextClock(PseudoTcp::Now(), interval);
andresp@webrtc.orgff689be2015-02-12 11:54:26 +0000189 interval = std::max<int>(interval, 0L); // sometimes interval is < 0
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000190 rtc::Thread::Current()->Clear(this, message);
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700191 rtc::Thread::Current()->PostDelayed(RTC_FROM_HERE, interval, this, message);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000192 }
193
194 virtual void OnMessage(rtc::Message* message) {
195 switch (message->message_id) {
196 case MSG_LPACKET: {
Steve Antoncc65bd02017-11-29 10:19:58 -0800197 const std::string& s(rtc::UseMessageData<std::string>(message->pdata));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000198 local_.NotifyPacket(s.c_str(), s.size());
199 UpdateLocalClock();
200 break;
201 }
202 case MSG_RPACKET: {
Steve Antoncc65bd02017-11-29 10:19:58 -0800203 const std::string& s(rtc::UseMessageData<std::string>(message->pdata));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000204 remote_.NotifyPacket(s.c_str(), s.size());
205 UpdateRemoteClock();
206 break;
207 }
208 case MSG_LCLOCK:
209 local_.NotifyClock(PseudoTcp::Now());
210 UpdateLocalClock();
211 break;
212 case MSG_RCLOCK:
213 remote_.NotifyClock(PseudoTcp::Now());
214 UpdateRemoteClock();
215 break;
216 default:
217 break;
218 }
219 delete message->pdata;
220 }
221
222 PseudoTcpForTest local_;
223 PseudoTcpForTest remote_;
224 rtc::MemoryStream send_stream_;
225 rtc::MemoryStream recv_stream_;
226 bool have_connected_;
227 bool have_disconnected_;
228 int local_mtu_;
229 int remote_mtu_;
230 int delay_;
231 int loss_;
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700232 bool drop_next_packet_ = false;
233 bool simultaneous_open_ = false;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000234};
235
236class PseudoTcpTest : public PseudoTcpTestBase {
237 public:
238 void TestTransfer(int size) {
Honghai Zhang82d78622016-05-06 11:29:15 -0700239 uint32_t start;
240 int32_t elapsed;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000241 size_t received;
242 // Create some dummy data to send.
243 send_stream_.ReserveSize(size);
244 for (int i = 0; i < size; ++i) {
245 char ch = static_cast<char>(i);
246 send_stream_.Write(&ch, 1, NULL, NULL);
247 }
248 send_stream_.Rewind();
249 // Prepare the receive stream.
250 recv_stream_.ReserveSize(size);
251 // Connect and wait until connected.
Honghai Zhang82d78622016-05-06 11:29:15 -0700252 start = rtc::Time32();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000253 EXPECT_EQ(0, Connect());
254 EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
255 // Sending will start from OnTcpWriteable and complete when all data has
256 // been received.
257 EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
Honghai Zhang82d78622016-05-06 11:29:15 -0700258 elapsed = rtc::Time32() - start;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000259 recv_stream_.GetSize(&received);
260 // Ensure we closed down OK and we got the right data.
Steve Anton5c8231c2017-12-06 10:39:22 -0800261 // TODO(?): Ensure the errors are cleared properly.
Steve Antoncc65bd02017-11-29 10:19:58 -0800262 // EXPECT_EQ(0, local_.GetError());
263 // EXPECT_EQ(0, remote_.GetError());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000264 EXPECT_EQ(static_cast<size_t>(size), received);
Steve Antoncc65bd02017-11-29 10:19:58 -0800265 EXPECT_EQ(0,
266 memcmp(send_stream_.GetBuffer(), recv_stream_.GetBuffer(), size));
Mirko Bonadei675513b2017-11-09 11:09:25 +0100267 RTC_LOG(LS_INFO) << "Transferred " << received << " bytes in " << elapsed
268 << " ms (" << size * 8 / elapsed << " Kbps)";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000269 }
270
271 private:
272 // IPseudoTcpNotify interface
273
274 virtual void OnTcpReadable(PseudoTcp* tcp) {
275 // Stream bytes to the recv stream as they arrive.
276 if (tcp == &remote_) {
277 ReadData();
278
Steve Anton5c8231c2017-12-06 10:39:22 -0800279 // TODO(?): OnTcpClosed() is currently only notified on error -
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000280 // there is no on-the-wire equivalent of TCP FIN.
281 // So we fake the notification when all the data has been read.
282 size_t received, required;
283 recv_stream_.GetPosition(&received);
284 send_stream_.GetSize(&required);
285 if (received == required)
286 OnTcpClosed(&remote_, 0);
287 }
288 }
289 virtual void OnTcpWriteable(PseudoTcp* tcp) {
290 // Write bytes from the send stream when we can.
291 // Shut down when we've sent everything.
292 if (tcp == &local_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100293 RTC_LOG(LS_VERBOSE) << "Flow Control Lifted";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000294 bool done;
295 WriteData(&done);
296 if (done) {
297 Close();
298 }
299 }
300 }
301
302 void ReadData() {
303 char block[kBlockSize];
304 size_t position;
305 int rcvd;
306 do {
307 rcvd = remote_.Recv(block, sizeof(block));
308 if (rcvd != -1) {
309 recv_stream_.Write(block, rcvd, NULL, NULL);
310 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100311 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000312 }
313 } while (rcvd > 0);
314 }
315 void WriteData(bool* done) {
316 size_t position, tosend;
317 int sent;
318 char block[kBlockSize];
319 do {
320 send_stream_.GetPosition(&position);
321 if (send_stream_.Read(block, sizeof(block), &tosend, NULL) !=
322 rtc::SR_EOS) {
323 sent = local_.Send(block, tosend);
324 UpdateLocalClock();
325 if (sent != -1) {
326 send_stream_.SetPosition(position + sent);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100327 RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000328 } else {
329 send_stream_.SetPosition(position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100330 RTC_LOG(LS_VERBOSE) << "Flow Controlled";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000331 }
332 } else {
333 sent = static_cast<int>(tosend = 0);
334 }
335 } while (sent > 0);
336 *done = (tosend == 0);
337 }
338
339 private:
340 rtc::MemoryStream send_stream_;
341 rtc::MemoryStream recv_stream_;
342};
343
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000344class PseudoTcpTestPingPong : public PseudoTcpTestBase {
345 public:
346 PseudoTcpTestPingPong()
347 : iterations_remaining_(0),
Steve Antoncc65bd02017-11-29 10:19:58 -0800348 sender_(NULL),
349 receiver_(NULL),
350 bytes_per_send_(0) {}
351 void SetBytesPerSend(int bytes) { bytes_per_send_ = bytes; }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000352 void TestPingPong(int size, int iterations) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200353 uint32_t start, elapsed;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000354 iterations_remaining_ = iterations;
355 receiver_ = &remote_;
356 sender_ = &local_;
357 // Create some dummy data to send.
358 send_stream_.ReserveSize(size);
359 for (int i = 0; i < size; ++i) {
360 char ch = static_cast<char>(i);
361 send_stream_.Write(&ch, 1, NULL, NULL);
362 }
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) {
424 recv_stream_.Write(block, rcvd, NULL, NULL);
425 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100426 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000427 }
428 } while (rcvd > 0);
429 }
430 void WriteData() {
431 size_t position, tosend;
432 int sent;
433 char block[kBlockSize];
434 do {
435 send_stream_.GetPosition(&position);
436 tosend = bytes_per_send_ ? bytes_per_send_ : sizeof(block);
Steve Antoncc65bd02017-11-29 10:19:58 -0800437 if (send_stream_.Read(block, tosend, &tosend, NULL) != rtc::SR_EOS) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000438 sent = sender_->Send(block, tosend);
439 UpdateLocalClock();
440 if (sent != -1) {
441 send_stream_.SetPosition(position + sent);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100442 RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000443 } else {
444 send_stream_.SetPosition(position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100445 RTC_LOG(LS_VERBOSE) << "Flow Controlled";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000446 }
447 } else {
448 sent = static_cast<int>(tosend = 0);
449 }
450 } while (sent > 0);
451 }
452
453 private:
454 int iterations_remaining_;
455 PseudoTcp* sender_;
456 PseudoTcp* receiver_;
457 int bytes_per_send_;
458};
459
460// Fill the receiver window until it is full, drain it and then
461// fill it with the same amount. This is to test that receiver window
462// contracts and enlarges correctly.
463class PseudoTcpTestReceiveWindow : public PseudoTcpTestBase {
464 public:
465 // Not all the data are transfered, |size| just need to be big enough
466 // to fill up the receiver window twice.
467 void TestTransfer(int size) {
468 // Create some dummy data to send.
469 send_stream_.ReserveSize(size);
470 for (int i = 0; i < size; ++i) {
471 char ch = static_cast<char>(i);
472 send_stream_.Write(&ch, 1, NULL, NULL);
473 }
474 send_stream_.Rewind();
475
476 // Prepare the receive stream.
477 recv_stream_.ReserveSize(size);
478
479 // Connect and wait until connected.
480 EXPECT_EQ(0, Connect());
481 EXPECT_TRUE_WAIT(have_connected_, kConnectTimeoutMs);
482
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700483 rtc::Thread::Current()->Post(RTC_FROM_HERE, this, MSG_WRITE);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000484 EXPECT_TRUE_WAIT(have_disconnected_, kTransferTimeoutMs);
485
486 ASSERT_EQ(2u, send_position_.size());
487 ASSERT_EQ(2u, recv_position_.size());
488
489 const size_t estimated_recv_window = EstimateReceiveWindowSize();
490
491 // The difference in consecutive send positions should equal the
492 // receive window size or match very closely. This verifies that receive
493 // window is open after receiver drained all the data.
494 const size_t send_position_diff = send_position_[1] - send_position_[0];
495 EXPECT_GE(1024u, estimated_recv_window - send_position_diff);
496
497 // Receiver drained the receive window twice.
498 EXPECT_EQ(2 * estimated_recv_window, recv_position_[1]);
499 }
500
501 virtual void OnMessage(rtc::Message* message) {
502 int message_id = message->message_id;
503 PseudoTcpTestBase::OnMessage(message);
504
505 switch (message_id) {
506 case MSG_WRITE: {
507 WriteData();
508 break;
509 }
510 default:
511 break;
512 }
513 }
514
Peter Boström0c4e06b2015-10-07 12:23:21 +0200515 uint32_t EstimateReceiveWindowSize() const {
516 return static_cast<uint32_t>(recv_position_[0]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000517 }
518
Peter Boström0c4e06b2015-10-07 12:23:21 +0200519 uint32_t EstimateSendWindowSize() const {
520 return static_cast<uint32_t>(send_position_[0] - recv_position_[0]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000521 }
522
523 private:
524 // IPseudoTcpNotify interface
Steve Antoncc65bd02017-11-29 10:19:58 -0800525 virtual void OnTcpReadable(PseudoTcp* tcp) {}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000526
Steve Antoncc65bd02017-11-29 10:19:58 -0800527 virtual void OnTcpWriteable(PseudoTcp* tcp) {}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000528
529 void ReadUntilIOPending() {
530 char block[kBlockSize];
531 size_t position;
532 int rcvd;
533
534 do {
535 rcvd = remote_.Recv(block, sizeof(block));
536 if (rcvd != -1) {
537 recv_stream_.Write(block, rcvd, NULL, NULL);
538 recv_stream_.GetPosition(&position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100539 RTC_LOG(LS_VERBOSE) << "Received: " << position;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000540 }
541 } while (rcvd > 0);
542
543 recv_stream_.GetPosition(&position);
544 recv_position_.push_back(position);
545
546 // Disconnect if we have done two transfers.
547 if (recv_position_.size() == 2u) {
548 Close();
549 OnTcpClosed(&remote_, 0);
550 } else {
551 WriteData();
552 }
553 }
554
555 void WriteData() {
556 size_t position, tosend;
557 int sent;
558 char block[kBlockSize];
559 do {
560 send_stream_.GetPosition(&position);
561 if (send_stream_.Read(block, sizeof(block), &tosend, NULL) !=
562 rtc::SR_EOS) {
563 sent = local_.Send(block, tosend);
564 UpdateLocalClock();
565 if (sent != -1) {
566 send_stream_.SetPosition(position + sent);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100567 RTC_LOG(LS_VERBOSE) << "Sent: " << position + sent;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000568 } else {
569 send_stream_.SetPosition(position);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100570 RTC_LOG(LS_VERBOSE) << "Flow Controlled";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000571 }
572 } else {
573 sent = static_cast<int>(tosend = 0);
574 }
575 } while (sent > 0);
576 // At this point, we've filled up the available space in the send queue.
577
Steve Antoncc65bd02017-11-29 10:19:58 -0800578 int message_queue_size = static_cast<int>(rtc::Thread::Current()->size());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000579 // The message queue will always have at least 2 messages, an RCLOCK and
580 // an LCLOCK, since they are added back on the delay queue at the same time
581 // they are pulled off and therefore are never really removed.
582 if (message_queue_size > 2) {
583 // If there are non-clock messages remaining, attempt to continue sending
584 // after giving those messages time to process, which should free up the
585 // send buffer.
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700586 rtc::Thread::Current()->PostDelayed(RTC_FROM_HERE, 10, this, MSG_WRITE);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000587 } else {
588 if (!remote_.isReceiveBufferFull()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100589 RTC_LOG(LS_ERROR) << "This shouldn't happen - the send buffer is full, "
Jonas Olssond7d762d2018-03-28 09:47:51 +0200590 "the receive buffer is not, and there are no "
591 "remaining messages to process.";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000592 }
593 send_stream_.GetPosition(&position);
594 send_position_.push_back(position);
595
596 // Drain the receiver buffer.
597 ReadUntilIOPending();
598 }
599 }
600
601 private:
602 rtc::MemoryStream send_stream_;
603 rtc::MemoryStream recv_stream_;
604
605 std::vector<size_t> send_position_;
606 std::vector<size_t> recv_position_;
607};
608
609// Basic end-to-end data transfer tests
610
611// Test the normal case of sending data from one side to the other.
612TEST_F(PseudoTcpTest, TestSend) {
613 SetLocalMtu(1500);
614 SetRemoteMtu(1500);
615 TestTransfer(1000000);
616}
617
618// Test sending data with a 50 ms RTT. Transmission should take longer due
619// to a slower ramp-up in send rate.
620TEST_F(PseudoTcpTest, TestSendWithDelay) {
621 SetLocalMtu(1500);
622 SetRemoteMtu(1500);
623 SetDelay(50);
624 TestTransfer(1000000);
625}
626
627// Test sending data with packet loss. Transmission should take much longer due
628// to send back-off when loss occurs.
629TEST_F(PseudoTcpTest, TestSendWithLoss) {
630 SetLocalMtu(1500);
631 SetRemoteMtu(1500);
632 SetLoss(10);
633 TestTransfer(100000); // less data so test runs faster
634}
635
636// Test sending data with a 50 ms RTT and 10% packet loss. Transmission should
637// take much longer due to send back-off and slower detection of loss.
638TEST_F(PseudoTcpTest, TestSendWithDelayAndLoss) {
639 SetLocalMtu(1500);
640 SetRemoteMtu(1500);
641 SetDelay(50);
642 SetLoss(10);
643 TestTransfer(100000); // less data so test runs faster
644}
645
646// Test sending data with 10% packet loss and Nagling disabled. Transmission
647// should take about the same time as with Nagling enabled.
648TEST_F(PseudoTcpTest, TestSendWithLossAndOptNaglingOff) {
649 SetLocalMtu(1500);
650 SetRemoteMtu(1500);
651 SetLoss(10);
652 SetOptNagling(false);
653 TestTransfer(100000); // less data so test runs faster
654}
655
Taylor Brandstetter20e8cfb2018-05-24 16:43:02 -0700656// Regression test for bugs.webrtc.org/9208.
657//
658// This bug resulted in corrupted data if a "connect" segment was received after
659// a data segment. This is only possible if:
660//
661// * The initial "connect" segment is lost, and retransmitted later.
662// * Both sides send "connect"s simultaneously, such that the local side thinks
663// a connection is established even before its "connect" has been
664// acknowledged.
665// * Nagle algorithm disabled, allowing a data segment to be sent before the
666// "connect" has been acknowledged.
667TEST_F(PseudoTcpTest,
668 TestSendWhenFirstPacketLostWithOptNaglingOffAndSimultaneousOpen) {
669 SetLocalMtu(1500);
670 SetRemoteMtu(1500);
671 DropNextPacket();
672 SetOptNagling(false);
673 SetSimultaneousOpen(true);
674 TestTransfer(10000);
675}
676
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000677// Test sending data with 10% packet loss and Delayed ACK disabled.
678// Transmission should be slightly faster than with it enabled.
679TEST_F(PseudoTcpTest, TestSendWithLossAndOptAckDelayOff) {
680 SetLocalMtu(1500);
681 SetRemoteMtu(1500);
682 SetLoss(10);
683 SetOptAckDelay(0);
684 TestTransfer(100000);
685}
686
687// Test sending data with 50ms delay and Nagling disabled.
688TEST_F(PseudoTcpTest, TestSendWithDelayAndOptNaglingOff) {
689 SetLocalMtu(1500);
690 SetRemoteMtu(1500);
691 SetDelay(50);
692 SetOptNagling(false);
693 TestTransfer(100000); // less data so test runs faster
694}
695
696// Test sending data with 50ms delay and Delayed ACK disabled.
697TEST_F(PseudoTcpTest, TestSendWithDelayAndOptAckDelayOff) {
698 SetLocalMtu(1500);
699 SetRemoteMtu(1500);
700 SetDelay(50);
701 SetOptAckDelay(0);
702 TestTransfer(100000); // less data so test runs faster
703}
704
705// Test a large receive buffer with a sender that doesn't support scaling.
706TEST_F(PseudoTcpTest, TestSendRemoteNoWindowScale) {
707 SetLocalMtu(1500);
708 SetRemoteMtu(1500);
709 SetLocalOptRcvBuf(100000);
710 DisableRemoteWindowScale();
711 TestTransfer(1000000);
712}
713
714// Test a large sender-side receive buffer with a receiver that doesn't support
715// scaling.
716TEST_F(PseudoTcpTest, TestSendLocalNoWindowScale) {
717 SetLocalMtu(1500);
718 SetRemoteMtu(1500);
719 SetRemoteOptRcvBuf(100000);
720 DisableLocalWindowScale();
721 TestTransfer(1000000);
722}
723
724// Test when both sides use window scaling.
725TEST_F(PseudoTcpTest, TestSendBothUseWindowScale) {
726 SetLocalMtu(1500);
727 SetRemoteMtu(1500);
728 SetRemoteOptRcvBuf(100000);
729 SetLocalOptRcvBuf(100000);
730 TestTransfer(1000000);
731}
732
733// Test using a large window scale value.
734TEST_F(PseudoTcpTest, TestSendLargeInFlight) {
735 SetLocalMtu(1500);
736 SetRemoteMtu(1500);
737 SetRemoteOptRcvBuf(100000);
738 SetLocalOptRcvBuf(100000);
739 SetOptSndBuf(150000);
740 TestTransfer(1000000);
741}
742
743TEST_F(PseudoTcpTest, TestSendBothUseLargeWindowScale) {
744 SetLocalMtu(1500);
745 SetRemoteMtu(1500);
746 SetRemoteOptRcvBuf(1000000);
747 SetLocalOptRcvBuf(1000000);
748 TestTransfer(10000000);
749}
750
751// Test using a small receive buffer.
752TEST_F(PseudoTcpTest, TestSendSmallReceiveBuffer) {
753 SetLocalMtu(1500);
754 SetRemoteMtu(1500);
755 SetRemoteOptRcvBuf(10000);
756 SetLocalOptRcvBuf(10000);
757 TestTransfer(1000000);
758}
759
760// Test using a very small receive buffer.
761TEST_F(PseudoTcpTest, TestSendVerySmallReceiveBuffer) {
762 SetLocalMtu(1500);
763 SetRemoteMtu(1500);
764 SetRemoteOptRcvBuf(100);
765 SetLocalOptRcvBuf(100);
766 TestTransfer(100000);
767}
768
769// Ping-pong (request/response) tests
770
771// Test sending <= 1x MTU of data in each ping/pong. Should take <10ms.
772TEST_F(PseudoTcpTestPingPong, TestPingPong1xMtu) {
773 SetLocalMtu(1500);
774 SetRemoteMtu(1500);
775 TestPingPong(100, 100);
776}
777
778// Test sending 2x-3x MTU of data in each ping/pong. Should take <10ms.
779TEST_F(PseudoTcpTestPingPong, TestPingPong3xMtu) {
780 SetLocalMtu(1500);
781 SetRemoteMtu(1500);
782 TestPingPong(400, 100);
783}
784
785// Test sending 1x-2x MTU of data in each ping/pong.
786// Should take ~1s, due to interaction between Nagling and Delayed ACK.
787TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtu) {
788 SetLocalMtu(1500);
789 SetRemoteMtu(1500);
790 TestPingPong(2000, 5);
791}
792
793// Test sending 1x-2x MTU of data in each ping/pong with Delayed ACK off.
794// Should take <10ms.
795TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtuWithAckDelayOff) {
796 SetLocalMtu(1500);
797 SetRemoteMtu(1500);
798 SetOptAckDelay(0);
799 TestPingPong(2000, 100);
800}
801
802// Test sending 1x-2x MTU of data in each ping/pong with Nagling off.
803// Should take <10ms.
804TEST_F(PseudoTcpTestPingPong, TestPingPong2xMtuWithNaglingOff) {
805 SetLocalMtu(1500);
806 SetRemoteMtu(1500);
807 SetOptNagling(false);
808 TestPingPong(2000, 5);
809}
810
811// Test sending a ping as pair of short (non-full) segments.
812// Should take ~1s, due to Delayed ACK interaction with Nagling.
813TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegments) {
814 SetLocalMtu(1500);
815 SetRemoteMtu(1500);
816 SetOptAckDelay(5000);
Steve Antoncc65bd02017-11-29 10:19:58 -0800817 SetBytesPerSend(50); // i.e. two Send calls per payload
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000818 TestPingPong(100, 5);
819}
820
821// Test sending ping as a pair of short (non-full) segments, with Nagling off.
822// Should take <10ms.
823TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegmentsWithNaglingOff) {
824 SetLocalMtu(1500);
825 SetRemoteMtu(1500);
826 SetOptNagling(false);
Steve Antoncc65bd02017-11-29 10:19:58 -0800827 SetBytesPerSend(50); // i.e. two Send calls per payload
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000828 TestPingPong(100, 5);
829}
830
831// Test sending <= 1x MTU of data ping/pong, in two segments, no Delayed ACK.
832// Should take ~1s.
833TEST_F(PseudoTcpTestPingPong, TestPingPongShortSegmentsWithAckDelayOff) {
834 SetLocalMtu(1500);
835 SetRemoteMtu(1500);
Steve Antoncc65bd02017-11-29 10:19:58 -0800836 SetBytesPerSend(50); // i.e. two Send calls per payload
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000837 SetOptAckDelay(0);
838 TestPingPong(100, 5);
839}
840
841// Test that receive window expands and contract correctly.
842TEST_F(PseudoTcpTestReceiveWindow, TestReceiveWindow) {
843 SetLocalMtu(1500);
844 SetRemoteMtu(1500);
845 SetOptNagling(false);
846 SetOptAckDelay(0);
847 TestTransfer(1024 * 1000);
848}
849
850// Test setting send window size to a very small value.
851TEST_F(PseudoTcpTestReceiveWindow, TestSetVerySmallSendWindowSize) {
852 SetLocalMtu(1500);
853 SetRemoteMtu(1500);
854 SetOptNagling(false);
855 SetOptAckDelay(0);
856 SetOptSndBuf(900);
857 TestTransfer(1024 * 1000);
858 EXPECT_EQ(900u, EstimateSendWindowSize());
859}
860
861// Test setting receive window size to a value other than default.
862TEST_F(PseudoTcpTestReceiveWindow, TestSetReceiveWindowSize) {
863 SetLocalMtu(1500);
864 SetRemoteMtu(1500);
865 SetOptNagling(false);
866 SetOptAckDelay(0);
867 SetRemoteOptRcvBuf(100000);
868 SetLocalOptRcvBuf(100000);
869 TestTransfer(1024 * 1000);
870 EXPECT_EQ(100000u, EstimateReceiveWindowSize());
871}
872
873/* Test sending data with mismatched MTUs. We should detect this and reduce
874// our packet size accordingly.
Steve Anton5c8231c2017-12-06 10:39:22 -0800875// TODO(?): This doesn't actually work right now. The current code
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000876// doesn't detect if the MTU is set too high on either side.
877TEST_F(PseudoTcpTest, TestSendWithMismatchedMtus) {
878 SetLocalMtu(1500);
879 SetRemoteMtu(1280);
880 TestTransfer(1000000);
881}
882*/