|  | // 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 "quic/test_tools/simulator/quic_endpoint.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "quic/core/crypto/crypto_handshake_message.h" | 
|  | #include "quic/core/crypto/crypto_protocol.h" | 
|  | #include "quic/core/quic_data_writer.h" | 
|  | #include "quic/platform/api/quic_test_output.h" | 
|  | #include "quic/test_tools/quic_config_peer.h" | 
|  | #include "quic/test_tools/quic_connection_peer.h" | 
|  | #include "quic/test_tools/quic_test_utils.h" | 
|  | #include "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_->set_visitor(this); | 
|  | connection_->SetEncrypter(ENCRYPTION_FORWARD_SECURE, | 
|  | std::make_unique<NullEncrypter>(perspective)); | 
|  | connection_->SetEncrypter(ENCRYPTION_INITIAL, nullptr); | 
|  | if (connection_->version().KnowsWhichDecrypterToUse()) { | 
|  | connection_->InstallDecrypter(ENCRYPTION_FORWARD_SECURE, | 
|  | std::make_unique<NullDecrypter>(perspective)); | 
|  | connection_->RemoveDecrypter(ENCRYPTION_INITIAL); | 
|  | } else { | 
|  | connection_->SetDecrypter(ENCRYPTION_FORWARD_SECURE, | 
|  | std::make_unique<NullDecrypter>(perspective)); | 
|  | } | 
|  | 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::SendProbingData() { | 
|  | if (connection()->sent_packet_manager().MaybeRetransmitOldestPacket( | 
|  | PROBING_RETRANSMISSION)) { | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | void QuicEndpoint::RetransmitFrames(const QuicFrames& frames, | 
|  | TransmissionType type) { | 
|  | QUICHE_DCHECK(notifier_); | 
|  | 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 |