blob: d4d580985cf28b28b957310f6a4852000214997b [file] [log] [blame]
// Copyright (c) 2012 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.
#include "quiche/quic/test_tools/simulator/quic_endpoint.h"
#include <memory>
#include <utility>
#include "quiche/quic/core/crypto/crypto_handshake_message.h"
#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/core/quic_data_writer.h"
#include "quiche/quic/platform/api/quic_test_output.h"
#include "quiche/quic/test_tools/quic_config_peer.h"
#include "quiche/quic/test_tools/quic_connection_peer.h"
#include "quiche/quic/test_tools/quic_test_utils.h"
#include "quiche/quic/test_tools/simulator/simulator.h"
namespace quic {
namespace simulator {
const QuicStreamId kDataStream = 3;
const QuicByteCount kWriteChunkSize = 128 * 1024;
const char kStreamDataContents = 'Q';
QuicEndpoint::QuicEndpoint(Simulator* simulator, std::string name,
std::string peer_name, Perspective perspective,
QuicConnectionId connection_id)
: QuicEndpointBase(simulator, name, peer_name),
bytes_to_transfer_(0),
bytes_transferred_(0),
wrong_data_received_(false),
notifier_(nullptr) {
connection_ = std::make_unique<QuicConnection>(
connection_id, GetAddressFromName(name), GetAddressFromName(peer_name),
simulator, simulator->GetAlarmFactory(), &writer_, false, perspective,
ParsedVersionOfIndex(CurrentSupportedVersions(), 0),
connection_id_generator_);
connection_->set_visitor(this);
connection_->SetEncrypter(ENCRYPTION_FORWARD_SECURE,
std::make_unique<quic::test::TaggingEncrypter>(
ENCRYPTION_FORWARD_SECURE));
connection_->SetEncrypter(ENCRYPTION_INITIAL, nullptr);
if (connection_->version().KnowsWhichDecrypterToUse()) {
connection_->InstallDecrypter(
ENCRYPTION_FORWARD_SECURE,
std::make_unique<quic::test::StrictTaggingDecrypter>(
ENCRYPTION_FORWARD_SECURE));
connection_->RemoveDecrypter(ENCRYPTION_INITIAL);
} else {
connection_->SetDecrypter(
ENCRYPTION_FORWARD_SECURE,
std::make_unique<quic::test::StrictTaggingDecrypter>(
ENCRYPTION_FORWARD_SECURE));
}
connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
connection_->OnHandshakeComplete();
if (perspective == Perspective::IS_SERVER) {
// Skip version negotiation.
test::QuicConnectionPeer::SetNegotiatedVersion(connection_.get());
}
test::QuicConnectionPeer::SetAddressValidated(connection_.get());
connection_->SetDataProducer(&producer_);
connection_->SetSessionNotifier(this);
notifier_ = std::make_unique<test::SimpleSessionNotifier>(connection_.get());
// Configure the connection as if it received a handshake. This is important
// primarily because
// - this enables pacing, and
// - this sets the non-handshake timeouts.
std::string error;
CryptoHandshakeMessage peer_hello;
peer_hello.SetValue(kICSL,
static_cast<uint32_t>(kMaximumIdleTimeoutSecs - 1));
peer_hello.SetValue(kMIBS,
static_cast<uint32_t>(kDefaultMaxStreamsPerConnection));
QuicConfig config;
QuicErrorCode error_code = config.ProcessPeerHello(
peer_hello, perspective == Perspective::IS_CLIENT ? SERVER : CLIENT,
&error);
QUICHE_DCHECK_EQ(error_code, QUIC_NO_ERROR)
<< "Configuration failed: " << error;
if (connection_->version().UsesTls()) {
if (connection_->perspective() == Perspective::IS_CLIENT) {
test::QuicConfigPeer::SetReceivedOriginalConnectionId(
&config, connection_->connection_id());
test::QuicConfigPeer::SetReceivedInitialSourceConnectionId(
&config, connection_->connection_id());
} else {
test::QuicConfigPeer::SetReceivedInitialSourceConnectionId(
&config, connection_->client_connection_id());
}
}
connection_->SetFromConfig(config);
connection_->DisableMtuDiscovery();
}
QuicByteCount QuicEndpoint::bytes_received() const {
QuicByteCount total = 0;
for (auto& interval : offsets_received_) {
total += interval.max() - interval.min();
}
return total;
}
QuicByteCount QuicEndpoint::bytes_to_transfer() const {
if (notifier_ != nullptr) {
return notifier_->StreamBytesToSend();
}
return bytes_to_transfer_;
}
QuicByteCount QuicEndpoint::bytes_transferred() const {
if (notifier_ != nullptr) {
return notifier_->StreamBytesSent();
}
return bytes_transferred_;
}
void QuicEndpoint::AddBytesToTransfer(QuicByteCount bytes) {
if (notifier_ != nullptr) {
if (notifier_->HasBufferedStreamData()) {
Schedule(clock_->Now());
}
notifier_->WriteOrBufferData(kDataStream, bytes, NO_FIN);
return;
}
if (bytes_to_transfer_ > 0) {
Schedule(clock_->Now());
}
bytes_to_transfer_ += bytes;
WriteStreamData();
}
void QuicEndpoint::OnStreamFrame(const QuicStreamFrame& frame) {
// Verify that the data received always matches the expected.
QUICHE_DCHECK(frame.stream_id == kDataStream);
for (size_t i = 0; i < frame.data_length; i++) {
if (frame.data_buffer[i] != kStreamDataContents) {
wrong_data_received_ = true;
}
}
offsets_received_.Add(frame.offset, frame.offset + frame.data_length);
// Sanity check against very pathological connections.
QUICHE_DCHECK_LE(offsets_received_.Size(), 1000u);
}
void QuicEndpoint::OnCryptoFrame(const QuicCryptoFrame& /*frame*/) {}
void QuicEndpoint::OnCanWrite() {
if (notifier_ != nullptr) {
notifier_->OnCanWrite();
return;
}
WriteStreamData();
}
bool QuicEndpoint::WillingAndAbleToWrite() const {
if (notifier_ != nullptr) {
return notifier_->WillingToWrite();
}
return bytes_to_transfer_ != 0;
}
bool QuicEndpoint::ShouldKeepConnectionAlive() const { return true; }
bool QuicEndpoint::AllowSelfAddressChange() const { return false; }
bool QuicEndpoint::OnFrameAcked(const QuicFrame& frame,
QuicTime::Delta ack_delay_time,
QuicTime receive_timestamp) {
if (notifier_ != nullptr) {
return notifier_->OnFrameAcked(frame, ack_delay_time, receive_timestamp);
}
return false;
}
void QuicEndpoint::OnFrameLost(const QuicFrame& frame) {
QUICHE_DCHECK(notifier_);
notifier_->OnFrameLost(frame);
}
bool QuicEndpoint::RetransmitFrames(const QuicFrames& frames,
TransmissionType type) {
QUICHE_DCHECK(notifier_);
return notifier_->RetransmitFrames(frames, type);
}
bool QuicEndpoint::IsFrameOutstanding(const QuicFrame& frame) const {
QUICHE_DCHECK(notifier_);
return notifier_->IsFrameOutstanding(frame);
}
bool QuicEndpoint::HasUnackedCryptoData() const { return false; }
bool QuicEndpoint::HasUnackedStreamData() const {
if (notifier_ != nullptr) {
return notifier_->HasUnackedStreamData();
}
return false;
}
HandshakeState QuicEndpoint::GetHandshakeState() const {
return HANDSHAKE_COMPLETE;
}
WriteStreamDataResult QuicEndpoint::DataProducer::WriteStreamData(
QuicStreamId /*id*/, QuicStreamOffset /*offset*/, QuicByteCount data_length,
QuicDataWriter* writer) {
writer->WriteRepeatedByte(kStreamDataContents, data_length);
return WRITE_SUCCESS;
}
bool QuicEndpoint::DataProducer::WriteCryptoData(EncryptionLevel /*level*/,
QuicStreamOffset /*offset*/,
QuicByteCount /*data_length*/,
QuicDataWriter* /*writer*/) {
QUIC_BUG(quic_bug_10157_1)
<< "QuicEndpoint::DataProducer::WriteCryptoData is unimplemented";
return false;
}
void QuicEndpoint::WriteStreamData() {
// Instantiate a flusher which would normally be here due to QuicSession.
QuicConnection::ScopedPacketFlusher flusher(connection_.get());
while (bytes_to_transfer_ > 0) {
// Transfer data in chunks of size at most |kWriteChunkSize|.
const size_t transmission_size =
std::min(kWriteChunkSize, bytes_to_transfer_);
QuicConsumedData consumed_data = connection_->SendStreamData(
kDataStream, transmission_size, bytes_transferred_, NO_FIN);
QUICHE_DCHECK(consumed_data.bytes_consumed <= transmission_size);
bytes_transferred_ += consumed_data.bytes_consumed;
bytes_to_transfer_ -= consumed_data.bytes_consumed;
if (consumed_data.bytes_consumed != transmission_size) {
return;
}
}
}
} // namespace simulator
} // namespace quic