| // 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 |