blob: 84bfcfafdcfd27369181ffe7a7c131fe4dfaf008 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef QUICHE_QUIC_TEST_TOOLS_PACKET_DROPPING_TEST_WRITER_H_
#define QUICHE_QUIC_TEST_TOOLS_PACKET_DROPPING_TEST_WRITER_H_
#include <cstdint>
#include <list>
#include <memory>
#include "absl/base/attributes.h"
#include "quiche/quic/core/quic_alarm.h"
#include "quiche/quic/core/quic_alarm_factory.h"
#include "quiche/quic/core/quic_clock.h"
#include "quiche/quic/core/quic_connection.h"
#include "quiche/quic/core/quic_packet_writer_wrapper.h"
#include "quiche/quic/test_tools/quic_test_utils.h"
#include "quiche/common/platform/api/quiche_mutex.h"
namespace quic {
namespace test {
// Simulates a connection that drops packets a configured percentage of the time
// and has a blocked socket a configured percentage of the time. Also provides
// the options to delay packets and reorder packets if delay is enabled.
class PacketDroppingTestWriter : public QuicPacketWriterWrapper {
public:
class Delegate {
public:
virtual ~Delegate() {}
virtual void OnCanWrite() = 0;
};
PacketDroppingTestWriter();
PacketDroppingTestWriter(const PacketDroppingTestWriter&) = delete;
PacketDroppingTestWriter& operator=(const PacketDroppingTestWriter&) = delete;
~PacketDroppingTestWriter() override;
// Must be called before blocking, reordering or delaying (loss is OK). May be
// called after connecting if the helper is not available before.
// |on_can_write| will be triggered when fake-unblocking.
void Initialize(QuicConnectionHelperInterface* helper,
QuicAlarmFactory* alarm_factory,
std::unique_ptr<Delegate> on_can_write);
// QuicPacketWriter methods:
WriteResult WritePacket(const char* buffer, size_t buf_len,
const QuicIpAddress& self_address,
const QuicSocketAddress& peer_address,
PerPacketOptions* options,
const QuicPacketWriterParams& params) override;
bool IsWriteBlocked() const override;
void SetWritable() override;
QuicPacketBuffer GetNextWriteLocation(
const QuicIpAddress& /*self_address*/,
const QuicSocketAddress& /*peer_address*/) override {
// If the wrapped writer supports zero-copy, disable it, because it is not
// compatible with delayed writes in this class.
return {nullptr, nullptr};
}
// Writes out any packet which should have been sent by now
// to the contained writer and returns the time
// for the next delayed packet to be written.
QuicTime ReleaseOldPackets();
// Sets |delay_alarm_| to fire at |new_deadline|.
void SetDelayAlarm(QuicTime new_deadline);
void OnCanWrite();
// The percent of time a packet is simulated as being lost.
// If |fake_packet_loss_percentage| is 100, then all packages are lost.
// Otherwise actual percentage will be lower than
// |fake_packet_loss_percentage|, because every dropped package is followed by
// a minimum number of successfully written packets.
void set_fake_packet_loss_percentage(int32_t fake_packet_loss_percentage) {
quiche::QuicheWriterMutexLock lock(&config_mutex_);
fake_packet_loss_percentage_ = fake_packet_loss_percentage;
}
// Once called, the next |passthrough_for_next_n_packets_| WritePacket() calls
// will always send the packets immediately, without being affected by the
// simulated error conditions.
void set_passthrough_for_next_n_packets(
uint32_t passthrough_for_next_n_packets) {
quiche::QuicheWriterMutexLock lock(&config_mutex_);
passthrough_for_next_n_packets_ = passthrough_for_next_n_packets;
}
// Simulate dropping the first n packets unconditionally.
// Subsequent packets will be lost at fake_packet_loss_percentage_ if set.
void set_fake_drop_first_n_packets(int32_t fake_drop_first_n_packets) {
quiche::QuicheWriterMutexLock lock(&config_mutex_);
fake_drop_first_n_packets_ = fake_drop_first_n_packets;
}
// The percent of time WritePacket will block and set WriteResult's status
// to WRITE_STATUS_BLOCKED.
void set_fake_blocked_socket_percentage(
int32_t fake_blocked_socket_percentage) {
QUICHE_DCHECK(clock_);
quiche::QuicheWriterMutexLock lock(&config_mutex_);
fake_blocked_socket_percentage_ = fake_blocked_socket_percentage;
}
// The percent of time a packet is simulated as being reordered.
void set_fake_reorder_percentage(int32_t fake_packet_reorder_percentage) {
QUICHE_DCHECK(clock_);
quiche::QuicheWriterMutexLock lock(&config_mutex_);
QUICHE_DCHECK(!fake_packet_delay_.IsZero());
fake_packet_reorder_percentage_ = fake_packet_reorder_percentage;
}
// The delay before writing this packet.
void set_fake_packet_delay(QuicTime::Delta fake_packet_delay) {
QUICHE_DCHECK(clock_);
quiche::QuicheWriterMutexLock lock(&config_mutex_);
fake_packet_delay_ = fake_packet_delay;
}
// The maximum bandwidth and buffer size of the connection. When these are
// set, packets will be delayed until a connection with that bandwidth would
// transmit it. Once the |buffer_size| is reached, all new packets are
// dropped.
void set_max_bandwidth_and_buffer_size(QuicBandwidth fake_bandwidth,
QuicByteCount buffer_size) {
QUICHE_DCHECK(clock_);
quiche::QuicheWriterMutexLock lock(&config_mutex_);
fake_bandwidth_ = fake_bandwidth;
buffer_size_ = buffer_size;
}
// Useful for reproducing very flaky issues.
ABSL_ATTRIBUTE_UNUSED void set_seed(uint64_t seed) {
simple_random_.set_seed(seed);
}
private:
// Writes out the next packet to the contained writer and returns the time
// for the next delayed packet to be written.
QuicTime ReleaseNextPacket();
// A single packet which will be sent at the supplied send_time.
struct DelayedWrite {
public:
DelayedWrite(const char* buffer, size_t buf_len,
const QuicIpAddress& self_address,
const QuicSocketAddress& peer_address,
std::unique_ptr<PerPacketOptions> options,
const QuicPacketWriterParams& params, QuicTime send_time);
DelayedWrite(const DelayedWrite&) = delete;
DelayedWrite(DelayedWrite&&) = default;
DelayedWrite& operator=(const DelayedWrite&) = delete;
DelayedWrite& operator=(DelayedWrite&&) = default;
~DelayedWrite();
std::string buffer;
QuicIpAddress self_address;
QuicSocketAddress peer_address;
std::unique_ptr<PerPacketOptions> options;
QuicPacketWriterParams params;
QuicTime send_time;
};
using DelayedPacketList = std::list<DelayedWrite>;
const QuicClock* clock_;
std::unique_ptr<QuicAlarm> write_unblocked_alarm_;
std::unique_ptr<QuicAlarm> delay_alarm_;
std::unique_ptr<Delegate> on_can_write_;
SimpleRandom simple_random_;
// Stored packets delayed by fake packet delay or bandwidth restrictions.
DelayedPacketList delayed_packets_;
QuicByteCount cur_buffer_size_;
uint64_t num_calls_to_write_;
uint32_t passthrough_for_next_n_packets_ QUICHE_GUARDED_BY(config_mutex_);
int32_t num_consecutive_succesful_writes_;
quiche::QuicheMutex config_mutex_;
int32_t fake_packet_loss_percentage_ QUICHE_GUARDED_BY(config_mutex_);
int32_t fake_drop_first_n_packets_ QUICHE_GUARDED_BY(config_mutex_);
int32_t fake_blocked_socket_percentage_ QUICHE_GUARDED_BY(config_mutex_);
int32_t fake_packet_reorder_percentage_ QUICHE_GUARDED_BY(config_mutex_);
QuicTime::Delta fake_packet_delay_ QUICHE_GUARDED_BY(config_mutex_);
QuicBandwidth fake_bandwidth_ QUICHE_GUARDED_BY(config_mutex_);
QuicByteCount buffer_size_ QUICHE_GUARDED_BY(config_mutex_);
};
} // namespace test
} // namespace quic
#endif // QUICHE_QUIC_TEST_TOOLS_PACKET_DROPPING_TEST_WRITER_H_