1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/tools/quic/test_tools/packet_dropping_test_writer.h"
6
7#include <limits>
8
9#include "base/rand_util.h"
10#include "net/tools/quic/quic_epoll_connection_helper.h"
11#include "net/tools/quic/quic_socket_utils.h"
12
13namespace net {
14namespace tools {
15namespace test {
16
17// An alarm that is scheduled if a blocked socket is simulated to indicate
18// it's writable again.
19class WriteUnblockedAlarm : public QuicAlarm::Delegate {
20 public:
21  explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer)
22      : writer_(writer) {}
23
24  virtual QuicTime OnAlarm() OVERRIDE {
25    DVLOG(1) << "Unblocking socket.";
26    writer_->OnCanWrite();
27    return QuicTime::Zero();
28  }
29
30 private:
31  PacketDroppingTestWriter* writer_;
32};
33
34// An alarm that is scheduled every time a new packet is to be written at a
35// later point.
36class DelayAlarm : public QuicAlarm::Delegate {
37 public:
38  explicit DelayAlarm(PacketDroppingTestWriter* writer) : writer_(writer) {}
39
40  virtual QuicTime OnAlarm() OVERRIDE {
41    return writer_->ReleaseOldPackets();
42  }
43
44 private:
45  PacketDroppingTestWriter* writer_;
46};
47
48PacketDroppingTestWriter::PacketDroppingTestWriter()
49    : clock_(NULL),
50      cur_buffer_size_(0),
51      config_mutex_(),
52      fake_packet_loss_percentage_(0),
53      fake_blocked_socket_percentage_(0),
54      fake_packet_reorder_percentage_(0),
55      fake_packet_delay_(QuicTime::Delta::Zero()),
56      fake_bandwidth_(QuicBandwidth::Zero()),
57      buffer_size_(0) {
58  uint32 seed = base::RandInt(0, std::numeric_limits<int32>::max());
59  VLOG(1) << "Seeding packet loss with " << seed;
60  simple_random_.set_seed(seed);
61}
62
63PacketDroppingTestWriter::~PacketDroppingTestWriter() {}
64
65void PacketDroppingTestWriter::Initialize(
66    QuicEpollConnectionHelper* helper,
67    Delegate* on_can_write) {
68  clock_ = helper->GetClock();
69  write_unblocked_alarm_.reset(
70      helper->CreateAlarm(new WriteUnblockedAlarm(this)));
71  delay_alarm_.reset(
72        helper->CreateAlarm(new DelayAlarm(this)));
73  on_can_write_.reset(on_can_write);
74}
75
76WriteResult PacketDroppingTestWriter::WritePacket(
77    const char* buffer,
78    size_t buf_len,
79    const net::IPAddressNumber& self_address,
80    const net::IPEndPoint& peer_address) {
81  ReleaseOldPackets();
82
83  base::AutoLock locked(config_mutex_);
84  if (fake_packet_loss_percentage_ > 0 &&
85      simple_random_.RandUint64() % 100 <
86          static_cast<uint64>(fake_packet_loss_percentage_)) {
87    DVLOG(1) << "Dropping packet.";
88    return WriteResult(WRITE_STATUS_OK, buf_len);
89  }
90  if (fake_blocked_socket_percentage_ > 0 &&
91      simple_random_.RandUint64() % 100 <
92          static_cast<uint64>(fake_blocked_socket_percentage_)) {
93    CHECK(on_can_write_.get() != NULL);
94    DVLOG(1) << "Blocking socket.";
95    if (!write_unblocked_alarm_->IsSet()) {
96      // Set the alarm to fire immediately.
97      write_unblocked_alarm_->Set(clock_->ApproximateNow());
98    }
99    return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
100  }
101
102  if (!fake_packet_delay_.IsZero() || !fake_bandwidth_.IsZero()) {
103    if (buffer_size_ > 0 && buf_len + cur_buffer_size_ > buffer_size_) {
104      // Drop packets which do not fit into the buffer.
105      DVLOG(1) << "Dropping packet because the buffer is full.";
106      return WriteResult(WRITE_STATUS_OK, buf_len);
107    }
108
109    // Queue it to be sent.
110    QuicTime send_time = clock_->ApproximateNow().Add(fake_packet_delay_);
111    if (!fake_bandwidth_.IsZero()) {
112      // Calculate a time the bandwidth limit would impose.
113      QuicTime::Delta bandwidth_delay = QuicTime::Delta::FromMicroseconds(
114          (buf_len * kNumMicrosPerSecond) /
115          fake_bandwidth_.ToBytesPerSecond());
116      send_time = delayed_packets_.empty() ?
117          send_time.Add(bandwidth_delay) :
118          delayed_packets_.back().send_time.Add(bandwidth_delay);
119    }
120    delayed_packets_.push_back(DelayedWrite(buffer, buf_len, self_address,
121                                            peer_address, send_time));
122    cur_buffer_size_ += buf_len;
123
124    // Set the alarm if it's not yet set.
125    if (!delay_alarm_->IsSet()) {
126      delay_alarm_->Set(send_time);
127    }
128
129    return WriteResult(WRITE_STATUS_OK, buf_len);
130  }
131
132  return QuicPacketWriterWrapper::WritePacket(
133      buffer, buf_len, self_address, peer_address);
134}
135
136bool PacketDroppingTestWriter::IsWriteBlocked() const {
137  if (write_unblocked_alarm_.get() != NULL && write_unblocked_alarm_->IsSet()) {
138    return true;
139  }
140  return QuicPacketWriterWrapper::IsWriteBlocked();
141}
142
143void PacketDroppingTestWriter::SetWritable() {
144  if (write_unblocked_alarm_.get() != NULL && write_unblocked_alarm_->IsSet()) {
145    write_unblocked_alarm_->Cancel();
146  }
147  QuicPacketWriterWrapper::SetWritable();
148}
149
150QuicTime PacketDroppingTestWriter::ReleaseNextPacket() {
151  if (delayed_packets_.empty()) {
152    return QuicTime::Zero();
153  }
154  base::AutoLock locked(config_mutex_);
155  DelayedPacketList::iterator iter = delayed_packets_.begin();
156  // Determine if we should re-order.
157  if (delayed_packets_.size() > 1 && fake_packet_reorder_percentage_ > 0 &&
158      simple_random_.RandUint64() % 100 <
159          static_cast<uint64>(fake_packet_reorder_percentage_)) {
160    DVLOG(1) << "Reordering packets.";
161    ++iter;
162    // Swap the send times when re-ordering packets.
163    delayed_packets_.begin()->send_time = iter->send_time;
164  }
165
166  DVLOG(1) << "Releasing packet.  " << (delayed_packets_.size() - 1)
167           << " remaining.";
168  // Grab the next one off the queue and send it.
169  QuicPacketWriterWrapper::WritePacket(
170      iter->buffer.data(), iter->buffer.length(),
171      iter->self_address, iter->peer_address);
172  DCHECK_GE(cur_buffer_size_, iter->buffer.length());
173  cur_buffer_size_ -= iter->buffer.length();
174  delayed_packets_.erase(iter);
175
176  // If there are others, find the time for the next to be sent.
177  if (delayed_packets_.empty()) {
178    return QuicTime::Zero();
179  }
180  return delayed_packets_.begin()->send_time;
181}
182
183QuicTime PacketDroppingTestWriter::ReleaseOldPackets() {
184  while (!delayed_packets_.empty()) {
185    QuicTime next_send_time = delayed_packets_.front().send_time;
186    if (next_send_time > clock_->Now()) {
187      return next_send_time;
188    }
189    ReleaseNextPacket();
190  }
191  return QuicTime::Zero();
192}
193
194void PacketDroppingTestWriter::OnCanWrite() {
195  on_can_write_->OnCanWrite();
196}
197
198PacketDroppingTestWriter::DelayedWrite::DelayedWrite(
199    const char* buffer,
200    size_t buf_len,
201    const net::IPAddressNumber& self_address,
202    const net::IPEndPoint& peer_address,
203    QuicTime send_time)
204    : buffer(buffer, buf_len),
205      self_address(self_address),
206      peer_address(peer_address),
207      send_time(send_time) {}
208
209PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() {}
210
211}  // namespace test
212}  // namespace tools
213}  // namespace net
214