diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-02 22:15:38 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-02 22:15:38 +0000 |
commit | 4e6f0edd6c424ce6ae754e07910a869e8ef358b4 (patch) | |
tree | 84c37bd0116e012244ce806f4f934ff6983256fb /net | |
parent | 896584f40469394ec27e52e7c10b9dca9f12bd33 (diff) | |
download | chromium_src-4e6f0edd6c424ce6ae754e07910a869e8ef358b4.zip chromium_src-4e6f0edd6c424ce6ae754e07910a869e8ef358b4.tar.gz chromium_src-4e6f0edd6c424ce6ae754e07910a869e8ef358b4.tar.bz2 |
Add QuicConnection and MockQuicConnectionHelper.
Review URL: https://chromiumcodereview.appspot.com/11276011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@165766 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/net.gyp | 3 | ||||
-rw-r--r-- | net/quic/quic_connection.cc | 594 | ||||
-rw-r--r-- | net/quic/quic_connection.h | 311 | ||||
-rw-r--r-- | net/quic/quic_connection_test.cc | 919 | ||||
-rw-r--r-- | net/quic/quic_framer.cc | 2 | ||||
-rw-r--r-- | net/quic/quic_protocol.h | 2 | ||||
-rw-r--r-- | net/quic/test_tools/mock_clock.h | 4 | ||||
-rw-r--r-- | net/quic/test_tools/quic_test_utils.cc | 23 | ||||
-rw-r--r-- | net/quic/test_tools/quic_test_utils.h | 39 | ||||
-rw-r--r-- | net/quic/uint128.h | 2 |
10 files changed, 1896 insertions, 3 deletions
diff --git a/net/net.gyp b/net/net.gyp index 11d3e86..e6a3279 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -657,6 +657,8 @@ 'quic/crypto/quic_encrypter.cc', 'quic/quic_clock.cc', 'quic/quic_clock.h', + 'quic/quic_connection.cc', + 'quic/quic_connection.h', 'quic/quic_data_reader.cc', 'quic/quic_data_reader.h', 'quic/quic_data_writer.cc', @@ -1405,6 +1407,7 @@ 'quic/test_tools/mock_clock.h', 'quic/test_tools/quic_test_utils.cc', 'quic/test_tools/quic_test_utils.h', + 'quic/quic_connection_test.cc', 'quic/quic_fec_group_test.cc', 'quic/quic_framer_test.cc', 'quic/quic_packet_creator_test.cc', diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc new file mode 100644 index 0000000..efe1b60 --- /dev/null +++ b/net/quic/quic_connection.cc @@ -0,0 +1,594 @@ +// 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 "net/quic/quic_connection.h" + +#include "base/logging.h" +#include "base/stl_util.h" +#include "net/base/net_errors.h" +#include "net/quic/congestion_control/quic_receipt_metrics_collector.h" +#include "net/quic/congestion_control/quic_send_scheduler.h" +#include "net/quic/quic_utils.h" + +using base::hash_map; +using base::hash_set; +using base::StringPiece; +using std::list; +using std::vector; +using std::set; + +/* +DEFINE_int32(fake_packet_loss_percentage, 0, + "The percentage of packets to drop."); +DEFINE_int32(negotiated_timeout_us, net::kDefaultTimeout, + "The default timeout for connections being closed"); +*/ + +namespace net { + +// An arbitrary number we'll probably want to tune. +const size_t kMaxUnackedPackets = 5000u; + +// The amount of time we wait before resending a packet. +const int64 kDefaultResendTimeMs = 500; + +bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) { + QuicPacketSequenceNumber delta = (a > b) ? a - b : b - a; + return delta <= kMaxUnackedPackets; +} + +QuicConnection::QuicConnection(QuicGuid guid, + IPEndPoint address, + QuicConnectionHelperInterface* helper) + : helper_(helper), + framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)), + clock_(helper->GetClock()), + guid_(guid), + peer_address_(address), + largest_seen_packet_with_ack_(0), + largest_seen_least_packet_awaiting_ack_(0), + write_blocked_(false), + packet_creator_(guid_, &framer_), + timeout_us_(kDefaultTimeout), + time_of_last_packet_us_(clock_->NowInUsec()), + collector_(new QuicReceiptMetricsCollector(clock_, kFixRate)), + scheduler_(new QuicSendScheduler(clock_, kFixRate)), + connected_(true) { + helper_->SetConnection(this); + helper_->SetTimeoutAlarm(timeout_us_); + framer_.set_visitor(this); + memset(&last_header_, 0, sizeof(last_header_)); + outgoing_ack_.sent_info.least_unacked = 0; + outgoing_ack_.received_info.largest_received = 0; + outgoing_ack_.received_info.time_received = 0; + outgoing_ack_.congestion_info.type = kNone; + /* + if (FLAGS_fake_packet_loss_percentage > 0) { + int32 seed = RandomBase::WeakSeed32(); + LOG(INFO) << "Seeding packet loss with " << seed; + random_.reset(new MTRandom(seed)); + } + */ +} + +QuicConnection::~QuicConnection() { + STLDeleteValues(&unacked_packets_); + STLDeleteValues(&group_map_); + // Queued packets that are not to be resent are owned + // by the packet queue. + for (QueuedPacketList::iterator q = queued_packets_.begin(); + q != queued_packets_.end(); ++q) { + if (!q->resend) delete q->packet; + } +} + +void QuicConnection::OnError(QuicFramer* framer) { + SendConnectionClose(framer->error()); +} + +void QuicConnection::OnPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address) { + time_of_last_packet_us_ = clock_->NowInUsec(); + DVLOG(1) << "last packet: " << time_of_last_packet_us_; + + // TODO(alyssar, rch) handle migration! + self_address_ = self_address; + peer_address_ = peer_address; +} + +void QuicConnection::OnRevivedPacket() { +} + +bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { + if (!Near(header.packet_sequence_number, + last_header_.packet_sequence_number)) { + DLOG(INFO) << "Packet out of bounds. Discarding"; + return false; + } + + last_header_ = header; + ReceivedPacketInfo info = outgoing_ack_.received_info; + // If this packet has already been seen, or that the sender + // has told us will not be resent, then stop processing the packet. + if (header.packet_sequence_number <= info.largest_received && + info.missing_packets.count(header.packet_sequence_number) != 1) { + return false; + } + return true; +} + +void QuicConnection::OnFecProtectedPayload(StringPiece payload) { + DCHECK_NE(0, last_header_.fec_group); + QuicFecGroup* group = GetFecGroup(); + group->Update(last_header_, payload); +} + +void QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { + frames_.push_back(frame); +} + +void QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { + DVLOG(1) << "Ack packet: " << incoming_ack; + + if (last_header_.packet_sequence_number <= largest_seen_packet_with_ack_) { + DLOG(INFO) << "Received an old ack frame: ignoring"; + return; + } + largest_seen_packet_with_ack_ = last_header_.packet_sequence_number; + + if (!ValidateAckFrame(incoming_ack)) { + SendConnectionClose(QUIC_INVALID_ACK_DATA); + return; + } + + UpdatePacketInformationReceivedByPeer(incoming_ack); + UpdatePacketInformationSentByPeer(incoming_ack); + scheduler_->OnIncomingAckFrame(incoming_ack); + // Now the we have received an ack, we might be able to send queued packets. + if (!queued_packets_.empty()) { + int delay = scheduler_->TimeUntilSend(false); + if (delay == 0) { + helper_->UnregisterSendAlarmIfRegistered(); + if (!write_blocked_) { + OnCanWrite(); + } + } else { + helper_->SetSendAlarm(delay); + } + } +} + +bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { + if (incoming_ack.received_info.largest_received > + packet_creator_.sequence_number()) { + DLOG(ERROR) << "Client acked unsent packet"; + // We got an error for data we have not sent. Error out. + return false; + } + + // We can't have too many missing or retransmitting packets, or our ack + // frames go over kMaxPacketSize. + DCHECK_LT(incoming_ack.received_info.missing_packets.size(), + kMaxUnackedPackets); + DCHECK_LT(incoming_ack.sent_info.non_retransmiting.size(), + kMaxUnackedPackets); + + if (incoming_ack.sent_info.least_unacked != 0 && + incoming_ack.sent_info.least_unacked < + largest_seen_least_packet_awaiting_ack_) { + DLOG(INFO) << "Client sent low least_unacked"; + // We never process old ack frames, so this number should only increase. + return false; + } + + return true; +} + +void QuicConnection::UpdatePacketInformationReceivedByPeer( + const QuicAckFrame& incoming_ack) { + QuicConnectionVisitorInterface::AckedPackets acked_packets; + + // For tracking the lowest unacked packet, pick one we have not sent yet. + QuicPacketSequenceNumber lowest_unacked = + packet_creator_.sequence_number() + 1; + + // If there's a packet between the next one we're sending and the + // highest one the peer has seen, that's our new lowest unacked. + if (incoming_ack.received_info.largest_received + 1 < lowest_unacked) { + lowest_unacked = incoming_ack.received_info.largest_received + 1; + } + + // Go through the packets we have not received an ack for and see if this + // incoming_ack shows they've been seen by the peer. + UnackedPacketMap::iterator it = unacked_packets_.begin(); + while (it != unacked_packets_.end()) { + if ((it->first < incoming_ack.received_info.largest_received && + incoming_ack.received_info.missing_packets.find(it->first) == + incoming_ack.received_info.missing_packets.end()) || + it->first == incoming_ack.received_info.largest_received) { + // This was either explicitly or implicitly acked. Remove it from our + // unacked packet list. + DVLOG(1) << "Got an ack for " << it->first; + // TODO(rch): This is inefficient and should be sped up. + // The acked packet might be queued (if a resend had been attempted). + for (QueuedPacketList::iterator q = queued_packets_.begin(); + q != queued_packets_.end(); ++q) { + if (q->sequence_number == it->first) { + queued_packets_.erase(q); + break; + } + } + delete it->second; + UnackedPacketMap::iterator tmp_it = it; + acked_packets.insert(it->first); + ++tmp_it; + unacked_packets_.erase(it); + it = tmp_it; + } else { + // This is a packet which we planned on resending and has not been + // seen at the time of this ack being sent out. See if it's our new + // lowest unacked packet. + DVLOG(1) << "still missing " << it->first; + if (it->first < lowest_unacked) { + lowest_unacked = it->first; + } + ++it; + } + } + if (acked_packets.size() > 0) { + visitor_->OnAck(acked_packets); + } + + // If we've gotten an ack for the lowest packet we were waiting on, + // update that and the list of packets we advertise we will not resend. + if (lowest_unacked > outgoing_ack_.sent_info.least_unacked) { + SequenceSet* non_retrans = &outgoing_ack_.sent_info.non_retransmiting; + // We don't need to advertise not-resending packets between the old + // and new values. + for (QuicPacketSequenceNumber i = outgoing_ack_.sent_info.least_unacked; + i < lowest_unacked; ++i) { + non_retrans->erase(i); + } + // If all packets we sent have been acked, use the special value of 0 + if (lowest_unacked > packet_creator_.sequence_number()) { + lowest_unacked = 0; + DCHECK_EQ(0u, non_retrans->size()); + } + outgoing_ack_.sent_info.least_unacked = lowest_unacked; + } +} + +void QuicConnection::UpdatePacketInformationSentByPeer( + const QuicAckFrame& incoming_ack) { + // Iteratate through the packets which will the peer will not resend and + // remove them from our missing list. + hash_set<QuicPacketSequenceNumber>::const_iterator it = + incoming_ack.sent_info.non_retransmiting.begin(); + while (it != incoming_ack.sent_info.non_retransmiting.end()) { + outgoing_ack_.received_info.missing_packets.erase(*it); + DVLOG(1) << "no longer expecting " << *it; + ++it; + } + + // Make sure we also don't expect any packets lower than the peer's + // last-packet-awaiting-ack + if (incoming_ack.sent_info.least_unacked > + largest_seen_least_packet_awaiting_ack_) { + for (QuicPacketSequenceNumber i = largest_seen_least_packet_awaiting_ack_; + i < incoming_ack.sent_info.least_unacked; + ++i) { + outgoing_ack_.received_info.missing_packets.erase(i); + } + largest_seen_least_packet_awaiting_ack_ = + incoming_ack.sent_info.least_unacked; + } + + // Possibly close any FecGroups which are now irrelevant + CloseFecGroupsBefore(incoming_ack.sent_info.least_unacked + 1); +} + +void QuicConnection::OnFecData(const QuicFecData& fec) { + DCHECK_NE(0, last_header_.fec_group); + QuicFecGroup* group = GetFecGroup(); + group->UpdateFec(last_header_.packet_sequence_number, fec); +} + +void QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { + DLOG(INFO) << "Stream reset with error " << frame.error_code; + visitor_->OnRstStream(frame); +} + +void QuicConnection::OnConnectionCloseFrame( + const QuicConnectionCloseFrame& frame) { + DLOG(INFO) << "Connection closed with error " << frame.error_code; + visitor_->ConnectionClose(frame.error_code, true); + connected_ = false; +} + +void QuicConnection::OnPacketComplete() { + DLOG(INFO) << "Got packet " << last_header_.packet_sequence_number + << " with " << frames_.size() + << " frames for " << last_header_.guid; + if (!last_packet_revived_) { + collector_->RecordIncomingPacket(last_size_, + last_header_.packet_sequence_number, + clock_->NowInUsec(), + last_packet_revived_); + } + + if (frames_.size()) { + // If there's data, pass it to the visitor and send out an ack. + bool accepted = visitor_->OnPacket(self_address_, peer_address_, + last_header_, frames_); + if (accepted) { + AckPacket(last_header_); + } else { + // Send an ack without changing our state. + SendAck(); + } + frames_.clear(); + } else { + // If there was no data, still make sure we update our internal state. + // AckPacket will not send an ack on the wire in this case. + AckPacket(last_header_); + } +} + +size_t QuicConnection::SendStreamData( + QuicStreamId id, + StringPiece data, + QuicStreamOffset offset, + bool fin, + QuicPacketSequenceNumber* last_packet) { + vector<PacketPair> packets; + packet_creator_.DataToStream(id, data, offset, fin, &packets); + DCHECK_LT(0u, packets.size()); + + for (size_t i = 0; i < packets.size(); ++i) { + SendPacket(packets[i].first, packets[i].second, true, false); + // TODO(alyssar) either only buffer this up if we send successfully, + // and make the upper levels deal with backup, or handle backup here. + unacked_packets_.insert(packets[i]); + } + + if (last_packet != NULL) { + *last_packet = packets[packets.size() - 1].first; + } + return data.size(); +} + +void QuicConnection::SendRstStream(QuicStreamId id, + QuicErrorCode error, + QuicStreamOffset offset) { + PacketPair packetpair = packet_creator_.ResetStream(id, offset, error); + + SendPacket(packetpair.first, packetpair.second, true, false); + unacked_packets_.insert(packetpair); +} + +void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicEncryptedPacket& packet) { + last_packet_revived_ = false; + last_size_ = packet.length(); + framer_.ProcessPacket(self_address, peer_address, packet); + + MaybeProcessRevivedPacket(); +} + +bool QuicConnection::OnCanWrite() { + write_blocked_ = false; + size_t num_queued_packets = queued_packets_.size() + 1; + while (!write_blocked_ && !helper_->IsSendAlarmSet() && + !queued_packets_.empty()) { + // Ensure that from one iteration of this loop to the next we + // succeeded in sending a packet so we don't infinitely loop. + // TODO(rch): clean up and close the connection if we really hit this. + DCHECK_LT(queued_packets_.size(), num_queued_packets); + num_queued_packets = queued_packets_.size(); + QueuedPacket p = queued_packets_.front(); + queued_packets_.pop_front(); + SendPacket(p.sequence_number, p.packet, p.resend, false); + } + return !write_blocked_; +} + +void QuicConnection::AckPacket(const QuicPacketHeader& header) { + QuicPacketSequenceNumber sequence_number = header.packet_sequence_number; + if (sequence_number > outgoing_ack_.received_info.largest_received) { + // We've got a new high sequence number. Note any new intermediate missing + // packets, and update the last_ack data. + for (QuicPacketSequenceNumber i = + outgoing_ack_.received_info.largest_received + 1; + i < sequence_number; ++i) { + DVLOG(1) << "missing " << i; + outgoing_ack_.received_info.missing_packets.insert(i); + } + outgoing_ack_.received_info.largest_received = sequence_number; + outgoing_ack_.received_info.time_received = clock_->NowInUsec(); + } else { + // We've gotten one of the out of order packets - remove it from our + // "missing packets" list. + DVLOG(1) << "Removing " << sequence_number << " from missing list"; + outgoing_ack_.received_info.missing_packets.erase(sequence_number); + } + // TODO(alyssar) delay sending until we have data, or enough time has elapsed. + if (frames_.size() > 0) { + SendAck(); + } +} + +void QuicConnection::MaybeResendPacket( + QuicPacketSequenceNumber sequence_number) { + UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number); + + if (it != unacked_packets_.end()) { + DVLOG(1) << "Resending unacked packet " << sequence_number; + framer_.IncrementRetransmitCount(it->second); + SendPacket(sequence_number, it->second, true, false); + } else { + DVLOG(2) << "alarm fired for " << sequence_number + << " but it has been acked"; + } +} + +bool QuicConnection::SendPacket(QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + bool resend, + bool force) { + // If this packet is being forced, don't bother checking to see if we should + // write, just write. + if (!force) { + // If we can't write, then simply queue the packet. + if (write_blocked_ || helper_->IsSendAlarmSet()) { + queued_packets_.push_back(QueuedPacket(sequence_number, packet, resend)); + return false; + } + + int delay = scheduler_->TimeUntilSend(resend); + // If the scheduler requires a delay, then we can not send this packet now. + if (delay > 0) { + helper_->SetSendAlarm(delay); + queued_packets_.push_back(QueuedPacket(sequence_number, packet, resend)); + return false; + } + } + if (resend) { + helper_->SetResendAlarm(sequence_number, kDefaultResendTimeMs * 1000); + // The second case should never happen in the real world, but does here + // because we sometimes send out of order to validate corner cases. + if (outgoing_ack_.sent_info.least_unacked == 0 || + sequence_number < outgoing_ack_.sent_info.least_unacked) { + outgoing_ack_.sent_info.least_unacked = sequence_number; + } + } else { + if (outgoing_ack_.sent_info.least_unacked!= 0 && + sequence_number > outgoing_ack_.sent_info.least_unacked) { + outgoing_ack_.sent_info.non_retransmiting.insert(sequence_number); + } + } + + // Just before we send the packet to the wire, update the transmission time. + framer_.WriteTransmissionTime(clock_->NowInUsec(), packet); + + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet)); + int error; + int rv = helper_->WritePacketToWire(sequence_number, *encrypted, resend, + &error); + if (rv == -1) { + if (error == ERR_IO_PENDING) { + write_blocked_ = true; + queued_packets_.push_front(QueuedPacket(sequence_number, packet, resend)); + return false; + } + } + + time_of_last_packet_us_ = clock_->NowInUsec(); + DVLOG(1) << "last packet: " << time_of_last_packet_us_; + + scheduler_->SentPacket(sequence_number, packet->length(), + framer_.GetRetransmitCount(packet) != 0); + if (!resend) delete packet; + return true; +} + +bool QuicConnection::ShouldSimulateLostPacket() { + // TODO(rch): enable this + return false; + /* + return FLAGS_fake_packet_loss_percentage > 0 && + random_->Rand32() % 100 < FLAGS_fake_packet_loss_percentage; + */ +} + +void QuicConnection::SendAck() { + if (!collector_->GenerateCongestionInfo(&outgoing_ack_.congestion_info)) { + outgoing_ack_.congestion_info.type = kNone; + } + DVLOG(1) << "Sending ack " << outgoing_ack_; + + PacketPair packetpair = packet_creator_.AckPacket(&outgoing_ack_); + SendPacket(packetpair.first, packetpair.second, false, false); +} + +void QuicConnection::MaybeProcessRevivedPacket() { + QuicFecGroup* group = GetFecGroup(); + if (group == NULL || !group->CanRevive()) { + return; + } + DCHECK(!revived_payload_.get()); + revived_payload_.reset(new char[kMaxPacketSize]); + size_t len = group->Revive(&revived_header_, revived_payload_.get(), + kMaxPacketSize); + group_map_.erase(last_header_.fec_group); + delete group; + + last_packet_revived_ = true; + framer_.ProcessRevivedPacket(revived_header_, + StringPiece(revived_payload_.get(), len)); + revived_payload_.reset(NULL); +} + +QuicFecGroup* QuicConnection::GetFecGroup() { + QuicFecGroupNumber fec_group_num = last_header_.fec_group; + if (fec_group_num == 0) { + return NULL; + } + if (group_map_.count(fec_group_num) == 0) { + // TODO(rch): limit the number of active FEC groups. + group_map_[fec_group_num] = new QuicFecGroup(); + } + return group_map_[fec_group_num]; +} + +void QuicConnection::SendConnectionClose(QuicErrorCode error) { + DLOG(INFO) << "Force closing with error " << QuicUtils::ErrorToString(error) + << " (" << error << ")"; + QuicConnectionCloseFrame frame; + frame.error_code = error; + frame.ack_frame = outgoing_ack_; + + PacketPair packetpair = packet_creator_.CloseConnection(&frame); + // There's no point in resending this: we're closing the connection. + SendPacket(packetpair.first, packetpair.second, false, true); + visitor_->ConnectionClose(error, false); + connected_ = false; +} + +void QuicConnection::CloseFecGroupsBefore( + QuicPacketSequenceNumber sequence_number) { + FecGroupMap::iterator it = group_map_.begin(); + while (it != group_map_.end()) { + // If this is the current group or the group doesn't protect this packet + // we can ignore it. + if (last_header_.fec_group == it->first || + !it->second->ProtectsPacketsBefore(sequence_number)) { + ++it; + continue; + } + QuicFecGroup* fec_group = it->second; + DCHECK(!fec_group->CanRevive()); + FecGroupMap::iterator next = it; + ++next; + group_map_.erase(it); + delete fec_group; + it = next; + } +} + +bool QuicConnection::CheckForTimeout() { + uint64 now_in_us = clock_->NowInUsec(); + uint64 delta_in_us = now_in_us - time_of_last_packet_us_; + DVLOG(1) << "last_packet " << time_of_last_packet_us_ + << " now:" << now_in_us + << " delta:" << delta_in_us; + if (delta_in_us >= timeout_us_) { + SendConnectionClose(QUIC_CONNECTION_TIMED_OUT); + return true; + } + helper_->SetTimeoutAlarm(timeout_us_ - delta_in_us); + return false; +} + +} // namespace net diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h new file mode 100644 index 0000000..51604db --- /dev/null +++ b/net/quic/quic_connection.h @@ -0,0 +1,311 @@ +// 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. +// +// The entity that handles framing writes for a Quic client or server. +// Each QuicSession will have a connection associated with it. +// +// On the server side, the Dispatcher handles the raw reads, and hands off +// packets via ProcessUdpPacket for framing and processing. +// +// On the client side, the Connection handles the raw reads, as well as the +// processing. +// +// Note: this class is not thread-safe. + +#ifndef NET_QUIC_QUIC_CONNECTION_H_ +#define NET_QUIC_QUIC_CONNECTION_H_ + +#include <list> +#include <set> +#include <vector> + +#include "base/hash_tables.h" +#include "net/base/ip_endpoint.h" +#include "net/quic/quic_fec_group.h" +#include "net/quic/quic_framer.h" +#include "net/quic/quic_packet_creator.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class QuicClock; +class QuicConnection; +class QuicEncrypter; +class QuicReceiptMetricsCollector; +class QuicSendScheduler; + +class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface { + public: + typedef std::set<QuicPacketSequenceNumber> AckedPackets; + + virtual ~QuicConnectionVisitorInterface() {} + // A simple visitor interface for dealing with data frames. The session + // should determine if all frames will be accepted, and return true if so. + // If any frames can't be processed or buffered, none of the data should + // be used, and the callee should return false. + virtual bool OnPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicPacketHeader& header, + const std::vector<QuicStreamFrame>& frame) = 0; + // Called when the stream is reset by the peer. + virtual void OnRstStream(const QuicRstStreamFrame& frame) = 0; + // Called when the connection is closed either locally by the framer, or + // remotely by the peer. + virtual void ConnectionClose(QuicErrorCode error, bool from_peer) = 0; + // Called when packets are acked by the peer. + virtual void OnAck(AckedPackets acked_packets) = 0; +}; + +class NET_EXPORT_PRIVATE QuicConnectionHelperInterface { + public: + virtual ~QuicConnectionHelperInterface() {} + + // Sets the QuicConnection to be used by this helper. This method + // must only be called once. + virtual void SetConnection(QuicConnection* connection) = 0; + + // Returns a QuicClock to be used for all time related functions. + virtual QuicClock* GetClock() = 0; + + // Sends the packet out to the peer, possibly simulating packet + // loss if FLAGS_fake_packet_loss_percentage is set. If the write failed + // errno will be copied to |*error|. + virtual int WritePacketToWire(QuicPacketSequenceNumber number, + const QuicEncryptedPacket& packet, + bool resend, + int* error) = 0; + + // Sets up an alarm to resend the packet with the given sequence number if we + // haven't gotten an ack in the expected time frame. Implementations must + // invoke MaybeResendPacket when the alarm fires. Implementations must also + // handle the case where |this| is deleted before the alarm fires. + virtual void SetResendAlarm(QuicPacketSequenceNumber sequence_number, + uint64 delay_in_us) = 0; + + // Sets an alarm to send packets after |delay_in_us|. Implementations must + // invoke OnCanWrite when the alarm fires. Implementations must also + // handle the case where |this| is deleted before the alarm fires. + virtual void SetSendAlarm(uint64 delay_in_us) = 0; + + // Sets An alarm which fires when the connection may have timed out. + // Implementations must call CheckForTimeout() and then reregister the alarm + // if the connection has not yet timed out. + virtual void SetTimeoutAlarm(uint64 delay_in_us) = 0; + + // Returns true if a send alarm is currently set. + virtual bool IsSendAlarmSet() = 0; + + // If a send alarm is currently set, this method unregisters it. If + // no send alarm is set, it does nothing. + virtual void UnregisterSendAlarmIfRegistered() = 0; +}; + +class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface { + public: + // Constructs a new QuicConnection for the specified |guid| and |address|. + // |helper| will be owned by this connection. + QuicConnection(QuicGuid guid, + IPEndPoint address, + QuicConnectionHelperInterface* helper); + virtual ~QuicConnection(); + + // Send the data payload to the peer. + size_t SendStreamData(QuicStreamId id, + base::StringPiece data, + QuicStreamOffset offset, + bool fin, + QuicPacketSequenceNumber* last_packet); + // Send a stream reset frame to the peer. + virtual void SendRstStream(QuicStreamId id, + QuicErrorCode error, + QuicStreamOffset offset); + // Sends a connection close frame to the peer, and notifies the visitor of + // the close. + virtual void SendConnectionClose(QuicErrorCode error); + + // Processes an incoming UDP packet (consisting of a QuicEncryptedPacket) from + // the peer. If processing this packet permits a packet to be revived from + // its FEC group that packet will be revived and processed. + virtual void ProcessUdpPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicEncryptedPacket& packet); + + // Called when the underlying connection becomes writable to allow + // queued writes to happen. Returns false if the socket has become blocked. + virtual bool OnCanWrite(); + + // From QuicFramerVisitorInterface + virtual void OnError(QuicFramer* framer) OVERRIDE; + virtual void OnPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address) OVERRIDE; + virtual void OnRevivedPacket() OVERRIDE; + virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; + virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE; + virtual void OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE; + virtual void OnAckFrame(const QuicAckFrame& frame) OVERRIDE; + virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE; + virtual void OnConnectionCloseFrame( + const QuicConnectionCloseFrame& frame) OVERRIDE; + virtual void OnFecData(const QuicFecData& fec) OVERRIDE; + virtual void OnPacketComplete() OVERRIDE; + + // Accessors + void set_visitor(QuicConnectionVisitorInterface* visitor) { + visitor_ = visitor; + } + const IPEndPoint& self_address() const { return self_address_; } + const IPEndPoint& peer_address() const { return peer_address_; } + QuicGuid guid() const { return guid_; } + + // Updates the internal state concerning which packets have been acked, and + // sends an ack if new data frames have been received. + void AckPacket(const QuicPacketHeader& header); + + // Called by a ResendAlarm when the timer goes off. If the packet has been + // acked it's a no op, otherwise the packet is resent and another alarm is + // scheduled. + void MaybeResendPacket(QuicPacketSequenceNumber sequence_number); + + QuicGuid guid() { return guid_; } + + QuicPacketCreator::Options* options() { return packet_creator_.options(); } + + bool connected() { return connected_; } + + size_t NumFecGroups() const { return group_map_.size(); } + + size_t NumQueuedPackets() const { return queued_packets_.size(); } + + // If the connection has timed out, this will close the connection and return + // true. Otherwise, it will return false and will reset the timeout alarm. + bool CheckForTimeout(); + + // Returns true of the next packet to be sent should be "lost" by + // not actually writing it to the wire. + bool ShouldSimulateLostPacket(); + + protected: + // Send a packet to the peer. If resend is true, this packet contains data, + // and will be resent if we don't get an ack. If force is true, then the + // packet will be sent immediately and the send scheduler will not be + // consulted. + virtual bool SendPacket(QuicPacketSequenceNumber number, + QuicPacket* packet, + bool resend, + bool force); + + // Make sure an ack we got from our peer is sane. + bool ValidateAckFrame(const QuicAckFrame& incoming_ack); + + // These two are called by OnAckFrame to update the appropriate internal + // state. + // + // Updates internal state based on incoming_ack.received_info + void UpdatePacketInformationReceivedByPeer( + const QuicAckFrame& incoming_ack); + // Updates internal state based in incoming_ack.sent_info + void UpdatePacketInformationSentByPeer(const QuicAckFrame& incoming_ack); + + private: + friend class QuicConnectionPeer; + typedef base::hash_set<QuicPacketSequenceNumber> SequenceSet; + + // Sets up a packet with an QuicAckFrame and sends it out. + void SendAck(); + + // If a packet can be revived from the current FEC group, then + // revive and process the packet. + void MaybeProcessRevivedPacket(); + + // Get the FEC group associate with the last processed packet. + QuicFecGroup* GetFecGroup(); + + // Fills the ack frame with the appropriate latched information. + void FillAckFrame(QuicAckFrame* ack); + + // Closes any FEC groups protecting packets before |sequence_number|. + void CloseFecGroupsBefore(QuicPacketSequenceNumber sequence_number); + + scoped_ptr<QuicConnectionHelperInterface> helper_; + QuicFramer framer_; + QuicClock* clock_; + + QuicGuid guid_; + IPEndPoint self_address_; + IPEndPoint peer_address_; + + bool last_packet_revived_; // true if the last packet was revived from FEC. + size_t last_size_; // size of the last received packet. + QuicPacketHeader last_header_; + std::vector<QuicStreamFrame> frames_; + + QuicAckFrame outgoing_ack_; + + // Track some client state so we can do less bookkeeping + // + // The largest packet we've seen which contained an ack frame. + QuicPacketSequenceNumber largest_seen_packet_with_ack_; + // The largest seen value for least_packet_awaiting_ack from the client. + QuicPacketSequenceNumber largest_seen_least_packet_awaiting_ack_; + + typedef base::hash_map<QuicPacketSequenceNumber, + QuicPacket*> UnackedPacketMap; + // When new packets are created which may be resent, they are added + // to this map, which contains owning pointers. + UnackedPacketMap unacked_packets_; + + // Packets which have not been written to the wire. + struct QueuedPacket { + QuicPacketSequenceNumber sequence_number; + QuicPacket* packet; + bool resend; + + QueuedPacket(QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + bool resend) { + this->sequence_number = sequence_number; + this->packet = packet; + this->resend = resend; + } + }; + typedef std::list<QueuedPacket> QueuedPacketList; + // When packets could not be sent because the socket was not writable, + // they are added to this list. For packets that are not resendable, this + // list contains owning pointers, since they are not added to + // unacked_packets_. + QueuedPacketList queued_packets_; + + // True when the socket becomes unwritable. + bool write_blocked_; + + typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap; + FecGroupMap group_map_; + QuicPacketHeader revived_header_; + scoped_array<char> revived_payload_; + + // Only set if we configure fake packet loss. + //scoped_ptr<MTRandom> random_; + + QuicConnectionVisitorInterface* visitor_; + QuicPacketCreator packet_creator_; + + // The number of usec of idle network before we kill of this connection. + uint64 timeout_us_; + // The time (since the epoch) that we got or tried to send a packet for this + // connection. + uint64 time_of_last_packet_us_; + + scoped_ptr<QuicReceiptMetricsCollector> collector_; + + // Scheduler which drives packet send rate. + scoped_ptr<QuicSendScheduler> scheduler_; + + // True by default. False if we've received or sent an explicit connection + // close. + bool connected_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CONNECTION_H_ diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc new file mode 100644 index 0000000..7ace43d --- /dev/null +++ b/net/quic/quic_connection_test.cc @@ -0,0 +1,919 @@ +// 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 "net/quic/quic_connection.h" + +#include "net/base/net_errors.h" +#include "net/quic/congestion_control/quic_receipt_metrics_collector.h" +#include "net/quic/congestion_control/quic_send_scheduler.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/crypto/null_encrypter.h" +#include "net/quic/test_tools/mock_clock.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +//DECLARE_int32(fake_packet_loss_percentage); + +using std::map; +using testing::_; +using testing::ContainerEq; +using testing::Return; + +namespace net { + +// Peer to make public a number of otherwise private QuicConnection methods. +class QuicConnectionPeer { + public: + static void SendAck(QuicConnection* connection) { + connection->SendAck(); + } + + static void SetCollector(QuicConnection* connection, + QuicReceiptMetricsCollector* collector) { + connection->collector_.reset(collector); + } + + static void SetScheduler(QuicConnection* connection, + QuicSendScheduler* scheduler) { + connection->scheduler_.reset(scheduler); + } +}; + +namespace test { +namespace { + +const char data1[] = "foo"; +const char data2[] = "bar"; + +class TestCollector : public QuicReceiptMetricsCollector { + public: + explicit TestCollector(CongestionInfo* info) + : QuicReceiptMetricsCollector(&clock_, kFixRate), + info_(info) { + } + + bool GenerateCongestionInfo(CongestionInfo* congestion_info) { + if (info_ == NULL) { + return false; + } + *congestion_info = *info_; + return true; + } + + MOCK_METHOD4(RecordIncomingPacket, + void(size_t, QuicPacketSequenceNumber, uint64, bool)); + + private: + MockClock clock_; + CongestionInfo* info_; +}; + +class TestConnectionHelper : public QuicConnectionHelperInterface { + public: + TestConnectionHelper(MockClock* clock) + : clock_(clock), + send_alarm_(0), + timeout_alarm_(0), + blocked_(false) { + } + + // QuicConnectionHelperInterface + virtual void SetConnection(QuicConnection* connection) {} + + virtual QuicClock* GetClock() { + return clock_; + } + + virtual int WritePacketToWire(QuicPacketSequenceNumber number, + const QuicEncryptedPacket& packet, + bool resend, + int* error) { + QuicFramer framer(QuicDecrypter::Create(kNULL), + QuicEncrypter::Create(kNULL)); + FramerVisitorCapturingAcks visitor; + framer.set_visitor(&visitor); + EXPECT_TRUE(framer.ProcessPacket(IPEndPoint(), IPEndPoint(), packet)); + header_ = *visitor.header(); + frame_ = *visitor.frame(); + if (blocked_) { + *error = ERR_IO_PENDING; + return -1; + } + return packet.length(); + } + + virtual void SetResendAlarm(QuicPacketSequenceNumber sequence_number, + uint64 delay_in_us) { + resend_alarms_[sequence_number] = clock_->NowInUsec() + delay_in_us; + } + + virtual void SetSendAlarm(uint64 delay_in_us) { + send_alarm_ = clock_->NowInUsec() + delay_in_us; + } + + virtual void SetTimeoutAlarm(uint64 delay_in_us) { + timeout_alarm_ = clock_->NowInUsec() + delay_in_us; + } + + virtual bool IsSendAlarmSet() { + return send_alarm_ > clock_->NowInUsec(); + } + + virtual void UnregisterSendAlarmIfRegistered() { + send_alarm_ = 0; + } + + const map<QuicPacketSequenceNumber, uint64>& resend_alarms() const { + return resend_alarms_; + } + + uint64 timeout_alarm() const { return timeout_alarm_; } + + QuicPacketHeader* header() { return &header_; } + + QuicAckFrame* frame() { return &frame_; } + + void set_blocked(bool blocked) { blocked_ = blocked; } + + private: + MockClock* clock_; + map<QuicPacketSequenceNumber, uint64> resend_alarms_; + uint64 send_alarm_; + uint64 timeout_alarm_; + QuicPacketHeader header_; + QuicAckFrame frame_; + bool blocked_; +}; + +class TestConnection : public QuicConnection { + public: + TestConnection(QuicGuid guid, + IPEndPoint address, + TestConnectionHelper* helper) + : QuicConnection(guid, address, helper) { + } + + void SendAck() { + QuicConnectionPeer::SendAck(this); + } + + void SetCollector(QuicReceiptMetricsCollector* collector) { + QuicConnectionPeer::SetCollector(this, collector); + } + + void SetScheduler(QuicSendScheduler* scheduler) { + QuicConnectionPeer::SetScheduler(this, scheduler); + } + + bool SendPacket(QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + bool resend, + bool force) { + return QuicConnection::SendPacket(sequence_number, packet, resend, force); + } +}; + +class QuicConnectionTest : public ::testing::Test { + protected: + QuicConnectionTest() + : guid_(42), + framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)), + creator_(guid_, &framer_), + scheduler_(new MockScheduler()), + helper_(new TestConnectionHelper(&clock_)), + connection_(guid_, IPEndPoint(), helper_), + frame1_(1, false, 0, data1), + frame2_(1, false, 3, data2), + accept_packet_(true) { + connection_.set_visitor(&visitor_); + connection_.SetScheduler(scheduler_); + EXPECT_CALL(*scheduler_, TimeUntilSend(_)).WillRepeatedly(Return(0)); + } + + QuicAckFrame* last_frame() { + return helper_->frame(); + } + + QuicPacketHeader* last_header() { + return helper_->header(); + } + + void ProcessPacket(QuicPacketSequenceNumber number) { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)) + .WillOnce(Return(accept_packet_)); + ProcessDataPacket(number, 0); + } + + void ProcessFecProtectedPacket(QuicPacketSequenceNumber number, + bool expect_revival) { + if (expect_revival) { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(2).WillRepeatedly( + Return(accept_packet_)); + } else { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce( + Return(accept_packet_)); + } + ProcessDataPacket(number, 1); + } + + void ProcessDataPacket(QuicPacketSequenceNumber number, + QuicFecGroupNumber fec_group) { + scoped_ptr<QuicPacket> packet(ConstructDataPacket(number, fec_group)); + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet)); + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); + } + + // Sends an FEC packet that convers the packets that would have been sent. + void ProcessFecPacket(QuicPacketSequenceNumber number, + QuicPacketSequenceNumber min_protected_packet, + bool expect_revival) { + if (expect_revival) { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce( + Return(accept_packet_)); + } + + // Construct the decrypted data packet so we can compute the correct + // redundancy. + scoped_ptr<QuicPacket> data_packet(ConstructDataPacket(number, 1)); + + header_.packet_sequence_number = number; + header_.flags = PACKET_FLAGS_FEC; + header_.fec_group = 1; + QuicFecData fec_data; + fec_data.min_protected_packet_sequence_number = min_protected_packet; + fec_data.fec_group = 1; + // Since all data packets in this test have the same payload, the + // redundancy is either equal to that payload or the xor of that payload + // with itself, depending on the number of packets. + if (((number - min_protected_packet) % 2) == 0) { + for (size_t i = kStartOfFecProtectedData; i < data_packet->length(); + ++i) { + data_packet->mutable_data()[i] ^= data_packet->data()[i]; + } + } + fec_data.redundancy = data_packet->FecProtectedData(); + QuicPacket* fec_packet; + framer_.ConstructFecPacket(header_, fec_data, &fec_packet); + scoped_ptr<QuicEncryptedPacket> encrypted( + framer_.EncryptPacket(*fec_packet)); + + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); + delete fec_packet; + } + + + void SendAckPacket(QuicAckFrame* frame) { + scoped_ptr<QuicPacket> packet(creator_.AckPacket(frame).second); + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet)); + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); + } + + void SendAckPacket(QuicPacketSequenceNumber least_unacked) { + QuicAckFrame frame(0, 0, least_unacked); + SendAckPacket(&frame); + } + + bool IsMissing(QuicPacketSequenceNumber number) { + return last_frame()->received_info.missing_packets.find(number) != + last_frame()->received_info.missing_packets.end(); + } + + size_t NonRetransmittingSize() { + return last_frame()->sent_info.non_retransmiting.size(); + } + + bool NonRetransmitting(QuicPacketSequenceNumber number) { + return last_frame()->sent_info.non_retransmiting.find( + number) != + last_frame()->sent_info.non_retransmiting.end(); + } + + QuicPacket* ConstructDataPacket(QuicPacketSequenceNumber number, + QuicFecGroupNumber fec_group) { + header_.guid = guid_; + header_.packet_sequence_number = number; + header_.retransmission_count = 0; + header_.transmission_time = 0; + header_.flags = PACKET_FLAGS_NONE; + header_.fec_group = fec_group; + + QuicFrames frames; + QuicFrame frame(&frame1_); + frames.push_back(frame); + QuicPacket* packet = NULL; + EXPECT_TRUE(framer_.ConstructFragementDataPacket(header_, frames, &packet)); + return packet; + } + + QuicGuid guid_; + QuicFramer framer_; + QuicPacketCreator creator_; + + MockScheduler* scheduler_; + MockClock clock_; + TestConnectionHelper* helper_; + TestConnection connection_; + testing::StrictMock<MockConnectionVisitor> visitor_; + + QuicPacketHeader header_; + QuicStreamFrame frame1_; + QuicStreamFrame frame2_; + bool accept_packet_; +}; + +TEST_F(QuicConnectionTest, PacketsInOrder) { + ProcessPacket(1); + EXPECT_EQ(1u, last_frame()->received_info.largest_received); + EXPECT_EQ(0u, last_frame()->received_info.missing_packets.size()); + + ProcessPacket(2); + EXPECT_EQ(2u, last_frame()->received_info.largest_received); + EXPECT_EQ(0u, last_frame()->received_info.missing_packets.size()); + + ProcessPacket(3); + EXPECT_EQ(3u, last_frame()->received_info.largest_received); + EXPECT_EQ(0u, last_frame()->received_info.missing_packets.size()); +} + +TEST_F(QuicConnectionTest, PacketsRejected) { + ProcessPacket(1); + EXPECT_EQ(1u, last_frame()->received_info.largest_received); + EXPECT_EQ(0u, last_frame()->received_info.missing_packets.size()); + + accept_packet_ = false; + ProcessPacket(2); + // We should not have an ack for two. + EXPECT_EQ(1u, last_frame()->received_info.largest_received); + EXPECT_EQ(0u, last_frame()->received_info.missing_packets.size()); +} + +TEST_F(QuicConnectionTest, PacketsOutOfOrder) { + ProcessPacket(3); + EXPECT_EQ(3u, last_frame()->received_info.largest_received); + EXPECT_EQ(2u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(2); + EXPECT_EQ(3u, last_frame()->received_info.largest_received); + EXPECT_EQ(1u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(1); + EXPECT_EQ(3u, last_frame()->received_info.largest_received); + EXPECT_EQ(0u, last_frame()->received_info.missing_packets.size()); +} + +TEST_F(QuicConnectionTest, DuplicatePacket) { + ProcessPacket(3); + EXPECT_EQ(3u, last_frame()->received_info.largest_received); + EXPECT_EQ(2u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + // Send packet 3 again, but do not set the expectation that + // the visitor OnPacket() will be called. + ProcessDataPacket(3, 0); + EXPECT_EQ(3u, last_frame()->received_info.largest_received); + EXPECT_EQ(2u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); +} + +TEST_F(QuicConnectionTest, LatePacketMarkedWillNotResend) { + ProcessPacket(5); + // Now send non-resending information, that we're not going to resend 3. + // The far end should stop waiting for it. + QuicAckFrame frame(0, 0, 1); + frame.sent_info.non_retransmiting.insert(3); + SendAckPacket(&frame); + // Force an ack to be sent. + connection_.SendAck(); + EXPECT_EQ(5u, last_frame()->received_info.largest_received); + EXPECT_EQ(2u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(4)); + EXPECT_TRUE(IsMissing(2)); + + // Send packet 3 again, but do not set the expectation that + // the visitor OnPacket() will be called. + ProcessDataPacket(3, 0); + connection_.SendAck(); + EXPECT_EQ(5u, last_frame()->received_info.largest_received); + EXPECT_EQ(2u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(4)); + EXPECT_TRUE(IsMissing(2)); +} + +TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndNonResend) { + ProcessPacket(3); + EXPECT_EQ(3u, last_frame()->received_info.largest_received); + EXPECT_EQ(2u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(2); + EXPECT_EQ(3u, last_frame()->received_info.largest_received); + EXPECT_EQ(1u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(6); + EXPECT_EQ(6u, last_frame()->received_info.largest_received); + EXPECT_EQ(3u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(1)); + EXPECT_TRUE(IsMissing(4)); + EXPECT_TRUE(IsMissing(5)); + + // Now send non-resending information, that we're not going to resend 4. + // The far end should stop waiting for it. + // In sending the ack, we also have sent packet 1, so we'll stop waiting for + // that as well. + QuicAckFrame frame(0, 0, 1); + frame.sent_info.non_retransmiting.insert(4); + SendAckPacket(&frame); + // Force an ack to be sent. + connection_.SendAck(); + EXPECT_EQ(1u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(5)); +} + +TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { + ProcessPacket(3); + EXPECT_EQ(3u, last_frame()->received_info.largest_received); + EXPECT_EQ(2u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(2); + EXPECT_EQ(3u, last_frame()->received_info.largest_received); + EXPECT_EQ(1u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(5); + EXPECT_EQ(5u, last_frame()->received_info.largest_received); + EXPECT_EQ(2u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(1)); + EXPECT_TRUE(IsMissing(4)); + + // Pretend at this point the client has gotten acks for 2 and 3 and 1 is a + // packet the peer will not retransmit. It indicates this by sending 'least + // awaiting' is 4. The connection should then realize 1 will not be + // retransmitted, and will remove it from the missing list. + QuicAckFrame frame(0, 0, 4); + SendAckPacket(&frame); + // Force an ack to be sent. + connection_.SendAck(); + EXPECT_EQ(1u, last_frame()->received_info.missing_packets.size()); + EXPECT_TRUE(IsMissing(4)); +} + +TEST_F(QuicConnectionTest, RejectPacketTooFarOut) { + // Call ProcessDataPacket rather than ProcessPacket, as we should not get a + // packet call to the visitor. + ProcessDataPacket(6000, 0);; + + connection_.SendAck(); // Packet 2 + EXPECT_EQ(0u, last_frame()->received_info.largest_received); +} + +TEST_F(QuicConnectionTest, LeastUnackedLower) { + connection_.SendStreamData(1, "foo", 0, false, NULL); + connection_.SendStreamData(1, "bar", 3, false, NULL); + connection_.SendStreamData(1, "eep", 6, false, NULL); + + // Start out saying the least unacked is 2 + creator_.set_sequence_number(5); + QuicAckFrame frame(0, 0, 2); + SendAckPacket(&frame); + + // Change it to 1, but lower the sequence number to fake out-of-order packets. + // This should be fine. + creator_.set_sequence_number(1); + QuicAckFrame frame2(0, 0, 1); + SendAckPacket(&frame2); + + // Now claim it's one, but set the ordering so it was sent "after" the first + // one. This should cause a connection error. + EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); + creator_.set_sequence_number(7); + SendAckPacket(&frame2); +} + +TEST_F(QuicConnectionTest, AckUnsentData) { + // Ack a packet which has not been sent + EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); + QuicAckFrame frame(1, 0, 0); + SendAckPacket(&frame); +} + +TEST_F(QuicConnectionTest, AckAll) { + ProcessPacket(1); + + creator_.set_sequence_number(1); + QuicAckFrame frame1(1, 0, 1); + SendAckPacket(&frame1); + + // Send an ack with least_unacked == 0, which indicates that all packets + // we have sent have been acked. + QuicAckFrame frame2(1, 0, 0); + SendAckPacket(&frame2); +} + +// This test is meant to validate that we can't overwhelm the far end with a ton +// of missing packets. +// We will likely fix the protocol to allow more than 190 in flight, and the +// test will need to be adjusted accordingly. +TEST_F(QuicConnectionTest, TooManyMissing) { + connection_.SendStreamData(1, "foo", 0, false, NULL); + + EXPECT_CALL(visitor_, ConnectionClose(QUIC_PACKET_TOO_LARGE, false)); + QuicAckFrame frame(1, 0, 0); + for (int i = 0; i < 5001; ++i) { + frame.received_info.missing_packets.insert(i); + } + SendAckPacket(&frame); +} + +// See comment for TooManyMissing above. +TEST_F(QuicConnectionTest, TooManyNonRetransmitting) { + connection_.SendStreamData(1, "foo", 0, false, NULL); + + EXPECT_CALL(visitor_, ConnectionClose(QUIC_PACKET_TOO_LARGE, false)); + QuicAckFrame frame(1, 0, 0); + for (int i = 0; i < 5001; ++i) { + frame.sent_info.non_retransmiting.insert(i); + } + SendAckPacket(&frame); +} + +TEST_F(QuicConnectionTest, BasicSending) { + QuicPacketSequenceNumber last_packet; + connection_.SendStreamData(1, "foo", 0, false, &last_packet); // Packet 1 + EXPECT_EQ(1u, last_packet); + connection_.SendAck(); // Packet 2 + + EXPECT_EQ(1u, last_frame()->sent_info.least_unacked); + + connection_.SendAck(); // Packet 3 + EXPECT_EQ(1u, last_frame()->sent_info.least_unacked); + EXPECT_EQ(1u, NonRetransmittingSize()); + EXPECT_TRUE(NonRetransmitting(2)); + + connection_.SendStreamData(1, "bar", 3, false, &last_packet); // Packet 4 + EXPECT_EQ(4u, last_packet); + connection_.SendAck(); // Packet 5 + EXPECT_EQ(1u, last_frame()->sent_info.least_unacked); + EXPECT_EQ(2u, NonRetransmittingSize()); + EXPECT_TRUE(NonRetransmitting(2)); + EXPECT_TRUE(NonRetransmitting(3)); + + QuicConnectionVisitorInterface::AckedPackets expected_acks; + expected_acks.insert(1); + + // Client acks up to packet 3 + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + QuicAckFrame frame(3, 0, 0); + SendAckPacket(&frame); + connection_.SendAck(); // Packet 6 + + // As soon as we've acked one, we skip ack packets 2 and 3 and note lack of + // ack for 4. + EXPECT_EQ(4u, last_frame()->sent_info.least_unacked); + EXPECT_EQ(1u, NonRetransmittingSize()); + EXPECT_TRUE(NonRetransmitting(5)); + + expected_acks.clear(); + expected_acks.insert(4); + + // Client acks up to packet 4, the last packet + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + QuicAckFrame frame2(6, 0, 0); + SendAckPacket(&frame2); + connection_.SendAck(); // Packet 7 + + // The least packet awaiting ack should now be the special value of 0 + EXPECT_EQ(0u, last_frame()->sent_info.least_unacked); + EXPECT_EQ(0u, NonRetransmittingSize()); + + // If we force an ack, we shouldn't change our retransmit state. + connection_.SendAck(); // Packet 8 + EXPECT_EQ(0u, last_frame()->sent_info.least_unacked); + EXPECT_EQ(0u, NonRetransmittingSize()); + + // But if we send more data it should. + connection_.SendStreamData(1, "eep", 6, false, &last_packet); // Packet 9 + EXPECT_EQ(9u, last_packet); + connection_.SendAck(); // Packet10 + EXPECT_EQ(9u, last_frame()->sent_info.least_unacked); +} + +// Test sending multiple acks from the connection to the session. +TEST_F(QuicConnectionTest, MultipleAcks) { + QuicPacketSequenceNumber last_packet; + connection_.SendStreamData(1, "foo", 0, false, &last_packet); // Packet 1 + EXPECT_EQ(1u, last_packet); + connection_.SendStreamData(3, "foo", 0, false, &last_packet); // Packet 2 + EXPECT_EQ(2u, last_packet); + connection_.SendAck(); // Packet 3 + connection_.SendStreamData(5, "foo", 0, false, &last_packet); // Packet 4 + EXPECT_EQ(4u, last_packet); + connection_.SendStreamData(1, "foo", 3, false, &last_packet); // Packet 5 + EXPECT_EQ(5u, last_packet); + connection_.SendStreamData(3, "foo", 3, false, &last_packet); // Packet 6 + EXPECT_EQ(6u, last_packet); + + // Client will acks packets 1, [!2], 3, 4, 5 + QuicAckFrame frame1(5, 0, 0); + frame1.received_info.missing_packets.insert(2); + + // The connection should pass up acks for 1, 4, 5. 2 is not acked, and 3 was + // an ackframe so should not be passed up. + QuicConnectionVisitorInterface::AckedPackets expected_acks; + expected_acks.insert(1); + expected_acks.insert(4); + expected_acks.insert(5); + + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + SendAckPacket(&frame1); + + // Now the client implicitly acks 2, and explicitly acks 6 + QuicAckFrame frame2(6, 0, 0); + expected_acks.clear(); + // Both acks should be passed up. + expected_acks.insert(2); + expected_acks.insert(6); + + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + SendAckPacket(&frame2); +} + +TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) { + // Don't send missing packet 1 + ProcessFecPacket(2, 1, true); +} + +TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) { + ProcessFecProtectedPacket(1, false); + // Don't send missing packet 2 + ProcessFecPacket(3, 1, true); +} + +TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) { + ProcessFecProtectedPacket(1, false); + // Don't send missing packet 2 + ProcessFecProtectedPacket(3, false); + ProcessFecPacket(4, 1, true); +} + +TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) { + // Don't send missing packet 1 + ProcessFecPacket(3, 1, false); // out of order + ProcessFecProtectedPacket(2, true); +} + +TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) { + ProcessFecProtectedPacket(1, false); + // Don't send missing packet 2 + ProcessFecPacket(6, 1, false); + ProcessFecProtectedPacket(3, false); + ProcessFecProtectedPacket(4, false); + ProcessFecProtectedPacket(5, true); +} + +TEST_F(QuicConnectionTest, TestResend) { + // TODO(rch): make this work + // FLAGS_fake_packet_loss_percentage = 100; + const uint64 kDefaultResendTimeMs = 500u; + + connection_.SendStreamData(1, "foo", 0, false, NULL); + EXPECT_EQ(0u, last_header()->retransmission_count); + EXPECT_EQ(0u, last_header()->transmission_time); + EXPECT_EQ(1u, helper_->resend_alarms().size()); + EXPECT_EQ(kDefaultResendTimeMs * 1000, + helper_->resend_alarms().find(1)->second); + // Simulate the resend alarm firing + clock_.AdvanceTimeInMicroseconds(kDefaultResendTimeMs * 1000); + connection_.MaybeResendPacket(1); + EXPECT_EQ(1u, last_header()->retransmission_count); + EXPECT_EQ(kDefaultResendTimeMs * 1000, + last_header()->transmission_time); +} + + +TEST_F(QuicConnectionTest, TestQueued) { + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + helper_->set_blocked(true); + connection_.SendStreamData(1, "foo", 0, false, NULL); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Attempt to send all packets, but since we're actually still + // blocked, they should all remain queued. + EXPECT_FALSE(connection_.OnCanWrite()); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Unblock the writes and actually send. + helper_->set_blocked(false); + EXPECT_TRUE(connection_.OnCanWrite()); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, CloseFecGroup) { + // Don't send missing packet 1 + // Don't send missing packet 2 + ProcessFecProtectedPacket(3, false); + // Don't send missing FEC packet 3 + ASSERT_EQ(1u, connection_.NumFecGroups()); + + // Now send non-fec protected ack packet and close the group + SendAckPacket(5); + ASSERT_EQ(0u, connection_.NumFecGroups()); +} + +TEST_F(QuicConnectionTest, NoCongestionInfo) { + TestCollector* collector(new TestCollector(NULL)); + connection_.SetCollector(collector); + connection_.SendAck(); + EXPECT_EQ(kNone, last_frame()->congestion_info.type); +} + +TEST_F(QuicConnectionTest, WithCongestionInfo) { + CongestionInfo info; + info.type = kFixRate; + info.fix_rate.bitrate_in_bytes_per_second = 123; + TestCollector* collector(new TestCollector(&info)); + connection_.SetCollector(collector); + connection_.SendAck(); + EXPECT_EQ(kFixRate, last_frame()->congestion_info.type); + EXPECT_EQ(info.fix_rate.bitrate_in_bytes_per_second, + last_frame()->congestion_info.fix_rate.bitrate_in_bytes_per_second); +} + +TEST_F(QuicConnectionTest, UpdateCongestionInfo) { + TestCollector* collector(new TestCollector(NULL)); + connection_.SetCollector(collector); + connection_.SendAck(); + EXPECT_CALL(*collector, RecordIncomingPacket(_, _, _, _)); + ProcessPacket(1); +} + +TEST_F(QuicConnectionTest, DontUpdateCongestionInfoForRevived) { + TestCollector* collector(new TestCollector(NULL)); + connection_.SetCollector(collector); + connection_.SendAck(); + // Process an FEC packet, and revive the missing data packet + // but only contact the collector once. + EXPECT_CALL(*collector, RecordIncomingPacket(_, _, _, _)); + ProcessFecPacket(2, 1, true); +} + +TEST_F(QuicConnectionTest, InitialTimeout) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); + + EXPECT_EQ(kDefaultTimeout, helper_->timeout_alarm()); + + // Simulate the timeout alarm firing + clock_.AdvanceTimeInMicroseconds(kDefaultTimeout); + EXPECT_TRUE(connection_.CheckForTimeout()); + EXPECT_FALSE(connection_.connected()); +} + +TEST_F(QuicConnectionTest, TimeoutAfterSend) { + EXPECT_TRUE(connection_.connected()); + + // When we send a packet, the timeout will change to 5000 + kDefaultTimeout. + clock_.AdvanceTimeInMicroseconds(5000); + // Send an ack so we don't set the resend alarm. + connection_.SendAck(); + + EXPECT_EQ(kDefaultTimeout, helper_->timeout_alarm()); + + // The original alarm will fire. We should not time out because we had a + // network event at t=5000. The alarm will reregister. + clock_.AdvanceTimeInMicroseconds(kDefaultTimeout - 5000); + EXPECT_EQ(kDefaultTimeout, clock_.NowInUsec()); + EXPECT_FALSE(connection_.CheckForTimeout()); + EXPECT_TRUE(connection_.connected()); + EXPECT_EQ(kDefaultTimeout + 5000, helper_->timeout_alarm()); + + // This time, we should time out. + EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); + clock_.AdvanceTimeInMicroseconds(5000); + EXPECT_EQ(kDefaultTimeout + 5000, clock_.NowInUsec()); + EXPECT_TRUE(connection_.CheckForTimeout()); + EXPECT_FALSE(connection_.connected()); +} + +TEST_F(QuicConnectionTest, SendScheduler) { + // Test that if we send a packet without delay, it is not queued. + scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); + EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(0)); + connection_.SendPacket(1, packet.get(), true, false); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelay) { + // Test that if we send a packet with a delay, it ends up queued. + scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); + EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(1)); + EXPECT_CALL(*scheduler_, SentPacket(1, _, _)).Times(0); + connection_.SendPacket(1, packet.get(), true, false); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerForce) { + // Test that if we force send a packet, it is not queued. + scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); + EXPECT_CALL(*scheduler_, TimeUntilSend(true)).Times(0); + connection_.SendPacket(1, packet.get(), true, true); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) { + scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); + helper_->set_blocked(true); + EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(0)); + EXPECT_CALL(*scheduler_, SentPacket(1, _, _)).Times(0); + connection_.SendPacket(1, packet.get(), true, false); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) { + // Test that if we send a packet with a delay, it ends up queued. + scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); + EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(1)); + connection_.SendPacket(1, packet.get(), true, false); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Advance the clock to fire the alarm, and configure the scheduler + // to permit the packet to be sent. + EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(0)); + clock_.AdvanceTimeInMicroseconds(1); + connection_.OnCanWrite(); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) { + scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); + EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(1)); + connection_.SendPacket(1, packet.get(), true, false); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Attempt to send another packet and make sure that it gets queued. + connection_.SendPacket(2, packet.get(), true, false); + EXPECT_EQ(2u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { + scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); + EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(10)); + connection_.SendPacket(1, packet.get(), true, false); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Now send non-retransmitting information, that we're not going to resend 3. + // The far end should stop waiting for it. + QuicAckFrame frame(0, 0, 1); + frame.sent_info.non_retransmiting.insert(3); + EXPECT_CALL(*scheduler_, OnIncomingAckFrame(testing::_)); + EXPECT_CALL(*scheduler_, TimeUntilSend(true)) + .WillRepeatedly(testing::Return(0)); + SendAckPacket(&frame); + + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + // Ensure alarm is not set + EXPECT_FALSE(helper_->IsSendAlarmSet()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { + scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); + EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(10)); + connection_.SendPacket(1, packet.get(), true, false); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Now send non-resending information, that we're not going to resend 3. + // The far end should stop waiting for it. + QuicAckFrame frame(0, 0, 1); + frame.sent_info.non_retransmiting.insert(3); + EXPECT_CALL(*scheduler_, OnIncomingAckFrame(testing::_)); + EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(1)); + SendAckPacket(&frame); + + EXPECT_EQ(1u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) { + scoped_ptr<QuicPacket> packet(ConstructDataPacket(1, 0)); + EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(10)); + connection_.SendPacket(1, packet.get(), true, false); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // OnCanWrite should not send the packet (because of the delay) + // but should still return true. + EXPECT_TRUE(connection_.OnCanWrite()); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 5198171d6..0148783 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -89,7 +89,7 @@ bool QuicFramer::ConstructFragementDataPacket( return RaiseError(QUIC_INVALID_FRAME_DATA); } } - + DCHECK_EQ(len, writer.length()); *packet = new QuicPacket(writer.take(), len, true); if (fec_builder_) { fec_builder_->OnBuiltFecProtectedPayload(header, diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index 9990fac..e49b623 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -60,7 +60,7 @@ const QuicStreamId kCryptoStreamId = 1; typedef std::pair<QuicPacketSequenceNumber, QuicPacket*> PacketPair; -const int64 kDefaultTimeout = 600000000; // 10 minutes +const uint64 kDefaultTimeout = 600000000; // 10 minutes enum QuicFrameType { STREAM_FRAME = 0, diff --git a/net/quic/test_tools/mock_clock.h b/net/quic/test_tools/mock_clock.h index 3a69869..c771fa4 100644 --- a/net/quic/test_tools/mock_clock.h +++ b/net/quic/test_tools/mock_clock.h @@ -20,6 +20,10 @@ class MockClock : public QuicClock { virtual uint64 NowInUsec() OVERRIDE; + void AdvanceTimeInMicroseconds(uint64 delta_in_us) { + now_ += delta_in_us; + } + void AdvanceTime(WallTime delta) { uint64 delta_us = delta * base::Time::kMicrosecondsPerSecond; now_ += delta_us; diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index b011349..a1c1179 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -24,6 +24,29 @@ bool NoOpFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) { return true; } +bool FramerVisitorCapturingAcks::OnPacketHeader( + const QuicPacketHeader& header) { + header_ = header; + return true; +} + +void FramerVisitorCapturingAcks::OnAckFrame(const QuicAckFrame& frame) { + frame_ = frame; +} + +MockConnectionVisitor::MockConnectionVisitor() { +} + +MockConnectionVisitor::~MockConnectionVisitor() { +} + +MockScheduler::MockScheduler() + : QuicSendScheduler(NULL, kFixRate) { +} + +MockScheduler::~MockScheduler() { +} + namespace { string HexDumpWithMarks(const char* data, int length, diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index 9333b15..e3a75c4 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -7,6 +7,8 @@ #ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ #define NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ +#include "net/quic/congestion_control/quic_send_scheduler.h" +#include "net/quic/quic_connection.h" #include "net/quic/quic_framer.h" #include "testing/gmock/include/gmock/gmock.h" @@ -64,6 +66,43 @@ class NoOpFramerVisitor : public QuicFramerVisitorInterface { virtual void OnPacketComplete() OVERRIDE {} }; + +class FramerVisitorCapturingAcks : public NoOpFramerVisitor { + public: + // NoOpFramerVisitor + virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; + virtual void OnAckFrame(const QuicAckFrame& frame) OVERRIDE; + + QuicPacketHeader* header() { return &header_; } + QuicAckFrame* frame() { return &frame_; } + private: + QuicPacketHeader header_; + QuicAckFrame frame_; +}; + +class MockConnectionVisitor : public QuicConnectionVisitorInterface { + public: + MockConnectionVisitor(); + ~MockConnectionVisitor(); + MOCK_METHOD4(OnPacket, bool(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicPacketHeader& header, + const std::vector<QuicStreamFrame>& frame)); + MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame)); + MOCK_METHOD2(ConnectionClose, void(QuicErrorCode error, bool from_peer)); + MOCK_METHOD1(OnAck, void(AckedPackets acked_packets)); +}; + +class MockScheduler : public QuicSendScheduler { + public: + MockScheduler(); + virtual ~MockScheduler(); + + MOCK_METHOD1(TimeUntilSend, int(bool)); + MOCK_METHOD1(OnIncomingAckFrame, void(const QuicAckFrame&)); + MOCK_METHOD3(SentPacket, void(QuicPacketSequenceNumber, size_t, bool)); +}; + } // namespace test } // namespace net diff --git a/net/quic/uint128.h b/net/quic/uint128.h index c368242..ce44b55 100644 --- a/net/quic/uint128.h +++ b/net/quic/uint128.h @@ -11,7 +11,7 @@ namespace net { struct uint128 { - uint128() {} + uint128() : hi(0), lo(0) {} uint128(uint64 hi, uint64 lo) : hi(hi), lo(lo) {} uint64 hi; uint64 lo; |