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