summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-02 22:15:38 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-02 22:15:38 +0000
commit4e6f0edd6c424ce6ae754e07910a869e8ef358b4 (patch)
tree84c37bd0116e012244ce806f4f934ff6983256fb
parent896584f40469394ec27e52e7c10b9dca9f12bd33 (diff)
downloadchromium_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
-rw-r--r--net/net.gyp3
-rw-r--r--net/quic/quic_connection.cc594
-rw-r--r--net/quic/quic_connection.h311
-rw-r--r--net/quic/quic_connection_test.cc919
-rw-r--r--net/quic/quic_framer.cc2
-rw-r--r--net/quic/quic_protocol.h2
-rw-r--r--net/quic/test_tools/mock_clock.h4
-rw-r--r--net/quic/test_tools/quic_test_utils.cc23
-rw-r--r--net/quic/test_tools/quic_test_utils.h39
-rw-r--r--net/quic/uint128.h2
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;