summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/base/linked_hash_map.h12
-rw-r--r--net/net.gyp3
-rw-r--r--net/quic/congestion_control/quic_congestion_manager.h4
-rw-r--r--net/quic/congestion_control/send_algorithm_interface.cc2
-rw-r--r--net/quic/quic_connection.cc449
-rw-r--r--net/quic/quic_connection.h86
-rw-r--r--net/quic/quic_connection_helper_test.cc41
-rw-r--r--net/quic/quic_connection_test.cc158
-rw-r--r--net/quic/quic_framer.cc2
-rw-r--r--net/quic/quic_packet_creator.cc2
-rw-r--r--net/quic/quic_packet_generator.cc11
-rw-r--r--net/quic/quic_packet_generator.h24
-rw-r--r--net/quic/quic_packet_generator_test.cc26
-rw-r--r--net/quic/quic_sent_packet_manager.cc221
-rw-r--r--net/quic/quic_sent_packet_manager.h140
-rw-r--r--net/quic/quic_sent_packet_manager_test.cc80
-rw-r--r--net/quic/quic_session.cc61
-rw-r--r--net/quic/quic_session.h13
-rw-r--r--net/quic/quic_session_test.cc102
-rw-r--r--net/quic/reliable_quic_stream.cc10
-rw-r--r--net/quic/reliable_quic_stream.h2
-rw-r--r--net/quic/test_tools/quic_connection_peer.cc8
-rw-r--r--net/quic/test_tools/quic_test_utils.cc5
-rw-r--r--net/quic/test_tools/quic_test_utils.h13
-rw-r--r--net/tools/quic/end_to_end_test.cc28
-rw-r--r--net/tools/quic/quic_epoll_connection_helper_test.cc17
-rw-r--r--net/tools/quic/quic_reliable_server_stream_test.cc9
-rw-r--r--net/tools/quic/quic_server_session_test.cc18
28 files changed, 1009 insertions, 538 deletions
diff --git a/net/base/linked_hash_map.h b/net/base/linked_hash_map.h
index d08b68d..7948647 100644
--- a/net/base/linked_hash_map.h
+++ b/net/base/linked_hash_map.h
@@ -146,7 +146,7 @@ class linked_hash_map {
std::pair<typename MapType::iterator, typename MapType::iterator> eq_range =
map_.equal_range(key);
- return make_pair(eq_range.first->second, eq_range.second->second);
+ return std::make_pair(eq_range.first->second, eq_range.second->second);
}
std::pair<const_iterator, const_iterator> equal_range(
@@ -159,13 +159,13 @@ class linked_hash_map {
const const_iterator& end_iter = eq_range.second != map_.end() ?
eq_range.second->second : end();
- return make_pair(start_iter, end_iter);
+ return std::make_pair(start_iter, end_iter);
}
// Returns the value mapped to key, or an inserted iterator to that position
// in the map.
Value& operator[](const key_type& key) {
- return (*((this->insert(make_pair(key, Value()))).first)).second;
+ return (*((this->insert(std::make_pair(key, Value()))).first)).second;
}
// Inserts an element into the map
@@ -174,7 +174,7 @@ class linked_hash_map {
// return a pair with an iterator to it, and false indicating that we
// didn't insert anything.
typename MapType::iterator found = map_.find(pair.first);
- if (found != map_.end()) return make_pair(found->second, false);
+ if (found != map_.end()) return std::make_pair(found->second, false);
// Otherwise, insert into the list first.
list_.push_back(pair);
@@ -184,10 +184,10 @@ class linked_hash_map {
typename ListType::iterator last = list_.end();
--last;
- CHECK(map_.insert(make_pair(pair.first, last)).second)
+ CHECK(map_.insert(std::make_pair(pair.first, last)).second)
<< "Map and list are inconsistent";
- return make_pair(last, true);
+ return std::make_pair(last, true);
}
size_type size() const {
diff --git a/net/net.gyp b/net/net.gyp
index 3bea4ca..905f70b 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -839,6 +839,8 @@
'quic/quic_reliable_client_stream.h',
'quic/quic_sent_entropy_manager.cc',
'quic/quic_sent_entropy_manager.h',
+ 'quic/quic_sent_packet_manager.cc',
+ 'quic/quic_sent_packet_manager.h',
'quic/quic_session.cc',
'quic/quic_session.h',
'quic/quic_spdy_compressor.cc',
@@ -1775,6 +1777,7 @@
'quic/quic_received_packet_manager_test.cc',
'quic/quic_reliable_client_stream_test.cc',
'quic/quic_sent_entropy_manager_test.cc',
+ 'quic/quic_sent_packet_manager_test.cc',
'quic/quic_session_test.cc',
'quic/quic_spdy_compressor_test.cc',
'quic/quic_spdy_decompressor_test.cc',
diff --git a/net/quic/congestion_control/quic_congestion_manager.h b/net/quic/congestion_control/quic_congestion_manager.h
index 465819b..6630343 100644
--- a/net/quic/congestion_control/quic_congestion_manager.h
+++ b/net/quic/congestion_control/quic_congestion_manager.h
@@ -1,7 +1,3 @@
-// Copyright (c) 2013 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.
-
// 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.
diff --git a/net/quic/congestion_control/send_algorithm_interface.cc b/net/quic/congestion_control/send_algorithm_interface.cc
index ce24a00..90399e7 100644
--- a/net/quic/congestion_control/send_algorithm_interface.cc
+++ b/net/quic/congestion_control/send_algorithm_interface.cc
@@ -14,7 +14,7 @@ const bool kUseReno = false;
// TODO(ianswett): Increase the max congestion window once the RTO logic is
// improved, particularly in cases when RTT is larger than the RTO. b/10075719
// Maximum number of outstanding packets for tcp.
-const QuicTcpCongestionWindow kMaxTcpCongestionWindow = 50;
+const QuicTcpCongestionWindow kMaxTcpCongestionWindow = 100;
// Factory for send side congestion control algorithm.
SendAlgorithmInterface* SendAlgorithmInterface::Create(
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index 0633f90..7417bd4 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -33,7 +33,7 @@ const QuicPacketSequenceNumber kMaxPacketGap = 5000;
// We want to make sure if we get a large nack packet, we don't queue up too
// many packets at once. 10 is arbitrary.
-const int kMaxRetransmissionsPerAck = 10;
+const size_t kMaxRetransmissionsPerAck = 10;
// TCP retransmits after 2 nacks. We allow for a third in case of out-of-order
// delivery.
@@ -126,6 +126,21 @@ class TimeoutAlarm : public QuicAlarm::Delegate {
QuicConnection* connection_;
};
+// Indicates if any of the frames are intended to be sent with FORCE.
+// Returns true when one of the frames is a CONNECTION_CLOSE_FRAME.
+net::QuicConnection::Force HasForcedFrames(
+ const RetransmittableFrames* retransmittable_frames) {
+ if (!retransmittable_frames) {
+ return net::QuicConnection::NO_FORCE;
+ }
+ for (size_t i = 0; i < retransmittable_frames->frames().size(); ++i) {
+ if (retransmittable_frames->frames()[i].type == CONNECTION_CLOSE_FRAME) {
+ return net::QuicConnection::FORCE;
+ }
+ }
+ return net::QuicConnection::NO_FORCE;
+}
+
} // namespace
// TODO(rch): Remove this.
@@ -169,6 +184,7 @@ QuicConnection::QuicConnection(QuicGuid guid,
time_of_last_received_packet_(clock_->ApproximateNow()),
time_of_last_sent_packet_(clock_->ApproximateNow()),
congestion_manager_(clock_, kTCP),
+ sent_packet_manager_(is_server, this),
version_negotiation_state_(START_NEGOTIATION),
max_packets_per_retransmission_alarm_(kMaxPacketsPerRetransmissionAlarm),
is_server_(is_server),
@@ -193,7 +209,6 @@ QuicConnection::QuicConnection(QuicGuid guid,
QuicConnection::~QuicConnection() {
STLDeleteElements(&ack_notifiers_);
STLDeleteElements(&undecryptable_packets_);
- STLDeleteValues(&unacked_packets_);
STLDeleteValues(&group_map_);
for (QueuedPacketList::iterator it = queued_packets_.begin();
it != queued_packets_.end(); ++it) {
@@ -437,6 +452,17 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) {
SendConnectionClose(QUIC_INVALID_ACK_DATA);
return false;
}
+
+ // Reset the RTO timeout for each packet when an ack is received.
+ if (retransmission_alarm_->IsSet()) {
+ retransmission_alarm_->Cancel();
+ QuicTime::Delta retransmission_delay =
+ congestion_manager_.GetRetransmissionDelay(
+ sent_packet_manager_.GetNumUnackedPackets(), 0);
+ retransmission_alarm_->Set(clock_->ApproximateNow().Add(
+ retransmission_delay));
+ }
+
last_ack_frames_.push_back(incoming_ack);
return connected_;
}
@@ -456,12 +482,11 @@ void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) {
sent_entropy_manager_.ClearEntropyBefore(
received_packet_manager_.least_packet_awaited_by_peer() - 1);
+ retransmitted_nacked_packet_count_ = 0;
SequenceNumberSet acked_packets;
- HandleAckForSentPackets(incoming_ack, &acked_packets);
- HandleAckForSentFecPackets(incoming_ack, &acked_packets);
+ sent_packet_manager_.HandleAckForSentPackets(incoming_ack, &acked_packets);
+ sent_packet_manager_.HandleAckForSentFecPackets(incoming_ack, &acked_packets);
if (acked_packets.size() > 0) {
- visitor_->OnAck(acked_packets);
-
// Inform all the registered AckNotifiers of the new ACKs.
// TODO(rjshade): Make this more efficient by maintaining a mapping of
// <sequence number, set<AckNotifierList>> so that OnAck
@@ -479,6 +504,13 @@ void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) {
}
}
}
+ // Clear the earliest retransmission timeouts that are no longer unacked to
+ // ensure the priority queue doesn't become too large.
+ while (!retransmission_timeouts_.empty() &&
+ !sent_packet_manager_.IsUnacked(
+ retransmission_timeouts_.top().sequence_number)) {
+ retransmission_timeouts_.pop();
+ }
congestion_manager_.OnIncomingAckFrame(incoming_ack,
time_of_last_received_packet_);
}
@@ -567,73 +599,6 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
return true;
}
-void QuicConnection::HandleAckForSentPackets(const QuicAckFrame& incoming_ack,
- SequenceNumberSet* acked_packets) {
- int retransmitted_packets = 0;
- // 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()) {
- QuicPacketSequenceNumber sequence_number = it->first;
- if (sequence_number >
- received_packet_manager_.peer_largest_observed_packet()) {
- // These are very new sequence_numbers.
- break;
- }
- RetransmittableFrames* unacked = it->second;
- if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) {
- // Packet was acked, so remove it from our unacked packet list.
- DVLOG(1) << ENDPOINT <<"Got an ack for packet " << sequence_number;
- acked_packets->insert(sequence_number);
- delete unacked;
- unacked_packets_.erase(it++);
- retransmission_map_.erase(sequence_number);
- } else {
- // This is a packet which we planned on retransmitting 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) << ENDPOINT << "still missing packet " << sequence_number;
- ++it;
- // The peer got packets after this sequence number. This is an explicit
- // nack.
- RetransmissionMap::iterator retransmission_it =
- retransmission_map_.find(sequence_number);
- ++(retransmission_it->second.number_nacks);
- if (retransmission_it->second.number_nacks >=
- kNumberOfNacksBeforeRetransmission &&
- retransmitted_packets < kMaxRetransmissionsPerAck) {
- ++retransmitted_packets;
- DVLOG(1) << ENDPOINT << "Trying to retransmit packet "
- << sequence_number
- << " as it has been nacked 3 or more times.";
- // RetransmitPacket will retransmit with a new sequence_number.
- RetransmitPacket(sequence_number);
- }
- }
- }
-}
-
-void QuicConnection::HandleAckForSentFecPackets(
- const QuicAckFrame& incoming_ack, SequenceNumberSet* acked_packets) {
- UnackedPacketMap::iterator it = unacked_fec_packets_.begin();
- while (it != unacked_fec_packets_.end()) {
- QuicPacketSequenceNumber sequence_number = it->first;
- if (sequence_number >
- received_packet_manager_.peer_largest_observed_packet()) {
- break;
- }
- if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) {
- DVLOG(1) << ENDPOINT << "Got an ack for fec packet: " << sequence_number;
- acked_packets->insert(sequence_number);
- unacked_fec_packets_.erase(it++);
- } else {
- DVLOG(1) << ENDPOINT << "Still missing ack for fec packet: "
- << sequence_number;
- ++it;
- }
- }
-}
-
void QuicConnection::OnFecData(const QuicFecData& fec) {
DCHECK_EQ(IN_FEC_GROUP, last_header_.is_in_fec_group);
DCHECK_NE(0u, last_header_.fec_group);
@@ -703,9 +668,8 @@ void QuicConnection::OnPacketComplete() {
// from unacket_packets_, increasing the least_unacked.
const bool last_packet_should_instigate_ack = ShouldLastPacketInstigateAck();
- if ((last_stream_frames_.empty() ||
- visitor_->OnPacket(self_address_, peer_address_,
- last_header_, last_stream_frames_))) {
+ if (last_stream_frames_.empty() ||
+ visitor_->OnStreamFrames(last_stream_frames_)) {
received_packet_manager_.RecordPacketReceived(
last_header_, time_of_last_received_packet_);
}
@@ -763,13 +727,10 @@ bool QuicConnection::ShouldLastPacketInstigateAck() {
// the high water mark.
if (!last_ack_frames_.empty() &&
!last_ack_frames_.back().received_info.missing_packets.empty() &&
- !unacked_packets_.empty()) {
- if (unacked_packets_.begin()->first >
- *last_ack_frames_.back().received_info.missing_packets.begin()) {
- return true;
- }
+ sent_packet_manager_.HasUnackedPackets()) {
+ return sent_packet_manager_.GetLeastUnackedSentPacket() >
+ *last_ack_frames_.back().received_info.missing_packets.begin();
}
-
return false;
}
@@ -821,11 +782,13 @@ void QuicConnection::SendVersionNegotiationPacket() {
delete encrypted;
}
-QuicConsumedData QuicConnection::SendvStreamData(QuicStreamId id,
- const struct iovec* iov,
- int count,
- QuicStreamOffset offset,
- bool fin) {
+QuicConsumedData QuicConnection::SendvStreamDataInner(
+ QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier* notifier) {
// TODO(ianswett): Further improve sending by passing the iovec down
// instead of batching into multiple stream frames in a single packet.
const bool already_in_batch_mode = packet_generator_.InBatchMode();
@@ -833,31 +796,37 @@ QuicConsumedData QuicConnection::SendvStreamData(QuicStreamId id,
size_t bytes_written = 0;
bool fin_consumed = false;
- for (int i = 0; i < count; ++i) {
- bool send_fin = fin && (i == count - 1);
+
+ for (int i = 0; i < iov_count; ++i) {
+ bool send_fin = fin && (i == iov_count - 1);
if (!send_fin && iov[i].iov_len == 0) {
LOG(DFATAL) << "Attempt to send empty stream frame";
}
- QuicConsumedData data_consumed = packet_generator_.ConsumeData(
- id,
- StringPiece(static_cast<char*>(iov[i].iov_base), iov[i].iov_len),
- offset + bytes_written,
- send_fin);
- DCHECK_LE(data_consumed.bytes_consumed, numeric_limits<uint32>::max());
- bytes_written += data_consumed.bytes_consumed;
- fin_consumed = data_consumed.fin_consumed;
+
+ StringPiece data(static_cast<char*>(iov[i].iov_base), iov[i].iov_len);
+ int currentOffset = offset + bytes_written;
+ QuicConsumedData consumed_data =
+ packet_generator_.ConsumeData(id,
+ data,
+ currentOffset,
+ send_fin,
+ notifier);
+
+ DCHECK_LE(consumed_data.bytes_consumed, numeric_limits<uint32>::max());
+ bytes_written += consumed_data.bytes_consumed;
+ fin_consumed = consumed_data.fin_consumed;
// If no bytes were consumed, bail now, because the stream can not write
// more data.
- if (data_consumed.bytes_consumed < iov[i].iov_len) {
+ if (consumed_data.bytes_consumed < iov[i].iov_len) {
break;
}
}
// Handle the 0 byte write properly.
- if (count == 0) {
+ if (iov_count == 0) {
DCHECK(fin);
- QuicConsumedData data_consumed = packet_generator_.ConsumeData(
- id, StringPiece(), offset, fin);
- fin_consumed = data_consumed.fin_consumed;
+ QuicConsumedData consumed_data = packet_generator_.ConsumeData(
+ id, StringPiece(), offset, fin, NULL);
+ fin_consumed = consumed_data.fin_consumed;
}
// Leave the generator in the original batch state.
@@ -865,23 +834,33 @@ QuicConsumedData QuicConnection::SendvStreamData(QuicStreamId id,
packet_generator_.FinishBatchOperations();
}
DCHECK_EQ(already_in_batch_mode, packet_generator_.InBatchMode());
+
return QuicConsumedData(bytes_written, fin_consumed);
}
-QuicConsumedData QuicConnection::SendStreamDataAndNotifyWhenAcked(
+QuicConsumedData QuicConnection::SendvStreamData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ QuicStreamOffset offset,
+ bool fin) {
+ return SendvStreamDataInner(id, iov, iov_count, offset, fin, NULL);
+}
+
+QuicConsumedData QuicConnection::SendvStreamDataAndNotifyWhenAcked(
QuicStreamId id,
- StringPiece data,
+ const struct iovec* iov,
+ int iov_count,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier::DelegateInterface* delegate) {
- if (!fin && data.empty()) {
+ if (!fin && iov_count == 0) {
LOG(DFATAL) << "Attempt to send empty stream frame";
}
// This notifier will be deleted in ProcessAckFrame once it has seen ACKs for
// all the consumed data (or below if no data was consumed).
QuicAckNotifier* notifier = new QuicAckNotifier(delegate);
QuicConsumedData consumed_data =
- packet_generator_.ConsumeData(id, data, offset, fin, notifier);
+ SendvStreamDataInner(id, iov, iov_count, offset, fin, notifier);
if (consumed_data.bytes_consumed > 0) {
// If some data was consumed, then the delegate should be registered for
@@ -970,21 +949,14 @@ bool QuicConnection::DoWrite() {
DCHECK(!write_blocked_);
WriteQueuedPackets();
- // We are postulating if we are not yet forward secure, the visitor may have
- // handshake messages to send.
- // TODO(jar): add a new visitor_ method that returns whether it has handshake
- // messages to send, and call it and pass the return value to each CanWrite
- // call.
- const IsHandshake maybe_handshake =
- encryption_level_ == ENCRYPTION_FORWARD_SECURE ? NOT_HANDSHAKE
- : IS_HANDSHAKE;
-
+ IsHandshake pending_handshake = visitor_->HasPendingHandshake() ?
+ IS_HANDSHAKE : NOT_HANDSHAKE;
// Sending queued packets may have caused the socket to become write blocked,
// or the congestion manager to prohibit sending. If we've sent everything
// we had queued and we're still not blocked, let the visitor know it can
// write more.
if (CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- maybe_handshake)) {
+ pending_handshake)) {
const bool already_in_batch_mode = packet_generator_.InBatchMode();
if (!already_in_batch_mode) {
packet_generator_.StartBatchOperations();
@@ -996,12 +968,11 @@ bool QuicConnection::DoWrite() {
// After the visitor writes, it may have caused the socket to become write
// blocked or the congestion manager to prohibit sending, so check again.
- // TODO(jar): we need to pass NOT_HANDSHAKE instead of maybe_handshake to
- // this CanWrite call to avoid getting into an infinite loop calling
- // DoWrite.
+ pending_handshake = visitor_->HasPendingHandshake() ? IS_HANDSHAKE
+ : NOT_HANDSHAKE;
if (!write_blocked_ && !all_bytes_written &&
CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE)) {
+ pending_handshake)) {
// We're not write blocked, but some stream didn't write out all of its
// bytes. Register for 'immediate' resumption so we'll keep writing after
// other quic connections have had a chance to use the socket.
@@ -1035,7 +1006,7 @@ bool QuicConnection::WriteQueuedPackets() {
packet_iterator->sequence_number,
packet_iterator->packet,
packet_iterator->retransmittable,
- NO_FORCE)) {
+ packet_iterator->forced)) {
packet_iterator = queued_packets_.erase(packet_iterator);
} else {
// Continue, because some queued packets may still be writable.
@@ -1049,101 +1020,72 @@ bool QuicConnection::WriteQueuedPackets() {
bool QuicConnection::MaybeRetransmitPacketForRTO(
QuicPacketSequenceNumber sequence_number) {
- DCHECK_EQ(ContainsKey(unacked_packets_, sequence_number),
- ContainsKey(retransmission_map_, sequence_number));
-
- if (!ContainsKey(unacked_packets_, sequence_number)) {
+ if (!sent_packet_manager_.IsUnacked(sequence_number)) {
DVLOG(2) << ENDPOINT << "alarm fired for " << sequence_number
<< " but it has been acked or already retransmitted with"
- << " different sequence number.";
+ << " a different sequence number.";
// So no extra delay is added for this packet.
return true;
}
- RetransmissionMap::iterator retransmission_it =
- retransmission_map_.find(sequence_number);
// If the packet hasn't been acked and we're getting truncated acks, ignore
// any RTO for packets larger than the peer's largest observed packet; it may
// have been received by the peer and just wasn't acked due to the ack frame
// running out of space.
- if (received_truncated_ack_ && sequence_number >
- received_packet_manager_.peer_largest_observed_packet() &&
+ if (received_truncated_ack_ &&
+ sequence_number > GetPeerLargestObservedPacket() &&
// We allow retransmission of already retransmitted packets so that we
// retransmit packets that were retransmissions of the packet with
// sequence number < the largest observed field of the truncated ack.
- retransmission_it->second.number_retransmissions == 0) {
+ !sent_packet_manager_.IsRetransmission(sequence_number)) {
return false;
- } else {
- ++stats_.rto_count;
- RetransmitPacket(sequence_number);
- return true;
}
+
+ ++stats_.rto_count;
+ RetransmitPacket(sequence_number);
+ return true;
}
void QuicConnection::RetransmitUnackedPackets(
RetransmissionType retransmission_type) {
- if (unacked_packets_.empty()) {
+ SequenceNumberSet unacked_packets = sent_packet_manager_.GetUnackedPackets();
+ if (unacked_packets.empty()) {
return;
}
- UnackedPacketMap::iterator next_it = unacked_packets_.begin();
- QuicPacketSequenceNumber end_sequence_number =
- unacked_packets_.rbegin()->first;
- do {
- UnackedPacketMap::iterator current_it = next_it;
- ++next_it;
+ for (SequenceNumberSet::const_iterator unacked_it = unacked_packets.begin();
+ unacked_it != unacked_packets.end(); ++unacked_it) {
+ const RetransmittableFrames& frames =
+ sent_packet_manager_.GetRetransmittableFrames(*unacked_it);
if (retransmission_type == ALL_PACKETS ||
- current_it->second->encryption_level() == ENCRYPTION_INITIAL) {
+ frames.encryption_level() == ENCRYPTION_INITIAL) {
// TODO(satyamshekhar): Think about congestion control here.
// Specifically, about the retransmission count of packets being sent
// proactively to achieve 0 (minimal) RTT.
- RetransmitPacket(current_it->first);
+ RetransmitPacket(*unacked_it);
}
- } while (next_it != unacked_packets_.end() &&
- next_it->first <= end_sequence_number);
+ }
}
void QuicConnection::RetransmitPacket(
QuicPacketSequenceNumber sequence_number) {
- UnackedPacketMap::iterator unacked_it =
- unacked_packets_.find(sequence_number);
- RetransmissionMap::iterator retransmission_it =
- retransmission_map_.find(sequence_number);
- // There should always be an entry corresponding to |sequence_number| in
- // both |retransmission_map_| and |unacked_packets_|. Retransmissions due to
- // RTO for sequence numbers that are already acked or retransmitted are
- // ignored by MaybeRetransmitPacketForRTO.
- DCHECK(unacked_it != unacked_packets_.end());
- DCHECK(retransmission_it != retransmission_map_.end());
- RetransmittableFrames* unacked = unacked_it->second;
+ DCHECK(sent_packet_manager_.IsUnacked(sequence_number));
+
// TODO(pwestin): Need to fix potential issue with FEC and a 1 packet
// congestion window see b/8331807 for details.
congestion_manager_.AbandoningPacket(sequence_number);
+ const RetransmittableFrames& retransmittable_frames =
+ sent_packet_manager_.GetRetransmittableFrames(sequence_number);
+
// Re-packetize the frames with a new sequence number for retransmission.
// Retransmitted data packets do not use FEC, even when it's enabled.
// Retransmitted packets use the same sequence number length as the original.
QuicSequenceNumberLength original_sequence_number_length =
- retransmission_it->second.sequence_number_length;
+ sent_packet_manager_.GetSequenceNumberLength(sequence_number);
SerializedPacket serialized_packet =
- packet_creator_.ReserializeAllFrames(unacked->frames(),
+ packet_creator_.ReserializeAllFrames(retransmittable_frames.frames(),
original_sequence_number_length);
- RetransmissionInfo retransmission_info(
- serialized_packet.sequence_number,
- serialized_packet.sequence_number_length);
- retransmission_info.number_retransmissions =
- retransmission_it->second.number_retransmissions + 1;
- // Remove info with old sequence number.
- unacked_packets_.erase(unacked_it);
- retransmission_map_.erase(retransmission_it);
- DLOG(INFO) << ENDPOINT << "Retransmitting unacked packet " << sequence_number
- << " as " << serialized_packet.sequence_number;
- DCHECK(unacked_packets_.empty() ||
- unacked_packets_.rbegin()->first < serialized_packet.sequence_number);
- unacked_packets_.insert(make_pair(serialized_packet.sequence_number,
- unacked));
- retransmission_map_.insert(make_pair(serialized_packet.sequence_number,
- retransmission_info));
// A notifier may be waiting to hear about ACKs for the original sequence
// number. Inform them that the sequence number has changed.
@@ -1153,15 +1095,21 @@ void QuicConnection::RetransmitPacket(
serialized_packet.sequence_number);
}
+ DLOG(INFO) << ENDPOINT << "Retransmitting " << sequence_number << " as "
+ << serialized_packet.sequence_number;
if (debug_visitor_) {
debug_visitor_->OnPacketRetransmitted(sequence_number,
serialized_packet.sequence_number);
}
- SendOrQueuePacket(unacked->encryption_level(),
+ sent_packet_manager_.OnRetransmittedPacket(sequence_number,
+ serialized_packet.sequence_number);
+
+ SendOrQueuePacket(retransmittable_frames.encryption_level(),
serialized_packet.sequence_number,
serialized_packet.packet,
serialized_packet.entropy_hash,
- HAS_RETRANSMITTABLE_DATA);
+ HAS_RETRANSMITTABLE_DATA,
+ HasForcedFrames(serialized_packet.retransmittable_frames));
}
bool QuicConnection::CanWrite(Retransmission retransmission,
@@ -1189,30 +1137,22 @@ bool QuicConnection::CanWrite(Retransmission retransmission,
return true;
}
-bool QuicConnection::IsRetransmission(
- QuicPacketSequenceNumber sequence_number) {
- RetransmissionMap::iterator it = retransmission_map_.find(sequence_number);
- return it != retransmission_map_.end() &&
- it->second.number_retransmissions > 0;
-}
-
void QuicConnection::SetupRetransmission(
QuicPacketSequenceNumber sequence_number,
EncryptionLevel level) {
- RetransmissionMap::iterator it = retransmission_map_.find(sequence_number);
- if (it == retransmission_map_.end()) {
+ if (!sent_packet_manager_.IsUnacked(sequence_number)) {
DVLOG(1) << ENDPOINT << "Will not retransmit packet " << sequence_number;
return;
}
-
- RetransmissionInfo retransmission_info = it->second;
+ size_t retransmission_count =
+ sent_packet_manager_.GetRetransmissionCount(sequence_number);
// TODO(rch): consider using a much smaller retransmisison_delay
// for the ENCRYPTION_NONE packets.
size_t effective_retransmission_count =
- level == ENCRYPTION_NONE ? 0 : retransmission_info.number_retransmissions;
+ level == ENCRYPTION_NONE ? 0 : retransmission_count;
QuicTime::Delta retransmission_delay =
congestion_manager_.GetRetransmissionDelay(
- unacked_packets_.size(),
+ sent_packet_manager_.GetNumUnackedPackets(),
effective_retransmission_count);
retransmission_timeouts_.push(RetransmissionTime(
@@ -1233,7 +1173,6 @@ void QuicConnection::SetupRetransmission(
void QuicConnection::SetupAbandonFecTimer(
QuicPacketSequenceNumber sequence_number) {
- DCHECK(ContainsKey(unacked_fec_packets_, sequence_number));
QuicTime::Delta retransmission_delay =
QuicTime::Delta::FromMilliseconds(
congestion_manager_.DefaultRetransmissionTime().ToMilliseconds() * 3);
@@ -1243,21 +1182,6 @@ void QuicConnection::SetupAbandonFecTimer(
true));
}
-void QuicConnection::DropPacket(QuicPacketSequenceNumber sequence_number) {
- UnackedPacketMap::iterator unacked_it =
- unacked_packets_.find(sequence_number);
- // Packet was not meant to be retransmitted.
- if (unacked_it == unacked_packets_.end()) {
- DCHECK(!ContainsKey(retransmission_map_, sequence_number));
- return;
- }
- // Delete the unacked packet.
- delete unacked_it->second;
- unacked_packets_.erase(unacked_it);
- retransmission_map_.erase(sequence_number);
- return;
-}
-
bool QuicConnection::WritePacket(EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
@@ -1276,14 +1200,15 @@ bool QuicConnection::WritePacket(EncryptionLevel level,
level == ENCRYPTION_NONE) {
// Drop packets that are NULL encrypted since the peer won't accept them
// anymore.
- DLOG(INFO) << ENDPOINT << "Dropped packet: " << sequence_number
+ DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number
<< " since the packet is NULL encrypted.";
- DropPacket(sequence_number);
+ sent_packet_manager_.DiscardPacket(sequence_number);
delete packet;
return true;
}
- Retransmission retransmission = IsRetransmission(sequence_number) ?
+ Retransmission retransmission =
+ sent_packet_manager_.IsRetransmission(sequence_number) ?
IS_RETRANSMISSION : NOT_RETRANSMISSION;
// TODO(wtc): use the same logic that is used in the packet generator.
// Namely, a packet is a handshake if it contains a stream frame for the
@@ -1395,46 +1320,49 @@ int QuicConnection::WritePacketToWire(QuicPacketSequenceNumber sequence_number,
bool QuicConnection::OnSerializedPacket(
const SerializedPacket& serialized_packet) {
- if (serialized_packet.retransmittable_frames != NULL) {
- DCHECK(unacked_packets_.empty() ||
- unacked_packets_.rbegin()->first <
- serialized_packet.sequence_number);
- // Retransmitted frames will be sent with the same encryption level as the
- // original.
+ if (serialized_packet.retransmittable_frames) {
serialized_packet.retransmittable_frames->set_encryption_level(
encryption_level_);
- unacked_packets_.insert(
- make_pair(serialized_packet.sequence_number,
- serialized_packet.retransmittable_frames));
- // All unacked packets might be retransmitted.
- retransmission_map_.insert(
- make_pair(serialized_packet.sequence_number,
- RetransmissionInfo(
- serialized_packet.sequence_number,
- serialized_packet.sequence_number_length)));
- } else if (serialized_packet.packet->is_fec_packet()) {
- unacked_fec_packets_.insert(make_pair(
- serialized_packet.sequence_number,
- serialized_packet.retransmittable_frames));
}
+ sent_packet_manager_.OnSerializedPacket(serialized_packet);
return SendOrQueuePacket(encryption_level_,
serialized_packet.sequence_number,
serialized_packet.packet,
serialized_packet.entropy_hash,
serialized_packet.retransmittable_frames != NULL ?
HAS_RETRANSMITTABLE_DATA :
- NO_RETRANSMITTABLE_DATA);
+ NO_RETRANSMITTABLE_DATA,
+ HasForcedFrames(
+ serialized_packet.retransmittable_frames));
+}
+
+QuicPacketSequenceNumber QuicConnection::GetPeerLargestObservedPacket() {
+ return received_packet_manager_.peer_largest_observed_packet();
+}
+
+QuicPacketSequenceNumber QuicConnection::GetNextPacketSequenceNumber() {
+ return packet_creator_.sequence_number() + 1;
+}
+
+void QuicConnection::OnPacketNacked(QuicPacketSequenceNumber sequence_number,
+ size_t nack_count) {
+ if (nack_count >= kNumberOfNacksBeforeRetransmission &&
+ retransmitted_nacked_packet_count_ < kMaxRetransmissionsPerAck) {
+ ++retransmitted_nacked_packet_count_;
+ RetransmitPacket(sequence_number);
+ }
}
bool QuicConnection::SendOrQueuePacket(EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
QuicPacketEntropyHash entropy_hash,
- HasRetransmittableData retransmittable) {
+ HasRetransmittableData retransmittable,
+ Force forced) {
sent_entropy_manager_.RecordPacketEntropyHash(sequence_number, entropy_hash);
- if (!WritePacket(level, sequence_number, packet, retransmittable, NO_FORCE)) {
+ if (!WritePacket(level, sequence_number, packet, retransmittable, forced)) {
queued_packets_.push_back(QueuedPacket(sequence_number, packet, level,
- retransmittable));
+ retransmittable, forced));
return false;
}
return true;
@@ -1450,14 +1378,7 @@ bool QuicConnection::ShouldSimulateLostPacket() {
}
void QuicConnection::UpdateSentPacketInfo(SentPacketInfo* sent_info) {
- if (!unacked_packets_.empty()) {
- sent_info->least_unacked = unacked_packets_.begin()->first;
- } else {
- // If there are no unacked packets, set the least unacked packet to
- // sequence_number() + 1 since that will be the sequence number of this
- // ack packet whenever it is sent.
- sent_info->least_unacked = packet_creator_.sequence_number() + 1;
- }
+ sent_info->least_unacked = sent_packet_manager_.GetLeastUnackedSentPacket();
sent_info->entropy_hash = sent_entropy_manager_.EntropyHash(
sent_info->least_unacked - 1);
}
@@ -1481,13 +1402,12 @@ void QuicConnection::SendAck() {
void QuicConnection::MaybeAbandonFecPacket(
QuicPacketSequenceNumber sequence_number) {
- if (!ContainsKey(unacked_fec_packets_, sequence_number)) {
+ if (!sent_packet_manager_.IsFecUnacked(sequence_number)) {
DVLOG(2) << ENDPOINT << "no need to abandon fec packet: "
<< sequence_number << "; it's already acked'";
return;
}
congestion_manager_.AbandoningPacket(sequence_number);
- // TODO(satyashekhar): Should this decrease the congestion window?
}
QuicTime QuicConnection::OnRetransmissionTimeout() {
@@ -1654,36 +1574,6 @@ void QuicConnection::SendConnectionClose(QuicErrorCode error) {
SendConnectionCloseWithDetails(error, string());
}
-void QuicConnection::SendConnectionClosePacket(QuicErrorCode error,
- const string& details) {
- DLOG(INFO) << ENDPOINT << "Force closing with error "
- << QuicUtils::ErrorToString(error) << " (" << error << ") "
- << details;
- QuicConnectionCloseFrame frame;
- frame.error_code = error;
- frame.error_details = details;
- UpdateSentPacketInfo(&frame.ack_frame.sent_info);
- received_packet_manager_.UpdateReceivedPacketInfo(
- &frame.ack_frame.received_info, clock_->ApproximateNow());
-
- SerializedPacket serialized_packet =
- packet_creator_.SerializeConnectionClose(&frame);
-
- // We need to update the sent entropy hash for all sent packets.
- sent_entropy_manager_.RecordPacketEntropyHash(
- serialized_packet.sequence_number,
- serialized_packet.entropy_hash);
-
- if (!WritePacket(encryption_level_,
- serialized_packet.sequence_number,
- serialized_packet.packet,
- serialized_packet.retransmittable_frames != NULL ?
- HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA,
- FORCE)) {
- delete serialized_packet.packet;
- }
-}
-
void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error,
const string& details) {
if (!write_blocked_) {
@@ -1692,6 +1582,21 @@ void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error,
CloseConnection(error, false);
}
+void QuicConnection::SendConnectionClosePacket(QuicErrorCode error,
+ const string& details) {
+ DLOG(INFO) << ENDPOINT << "Force closing with error "
+ << QuicUtils::ErrorToString(error) << " (" << error << ") "
+ << details;
+ QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame();
+ frame->error_code = error;
+ frame->error_details = details;
+ UpdateSentPacketInfo(&frame->ack_frame.sent_info);
+ received_packet_manager_.UpdateReceivedPacketInfo(
+ &frame->ack_frame.received_info, clock_->ApproximateNow());
+ packet_generator_.AddControlFrame(QuicFrame(frame));
+ Flush();
+}
+
void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) {
DCHECK(connected_);
connected_ = false;
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
index 8420752..1524909 100644
--- a/net/quic/quic_connection.h
+++ b/net/quic/quic_connection.h
@@ -38,6 +38,7 @@
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_received_packet_manager.h"
#include "net/quic/quic_sent_entropy_manager.h"
+#include "net/quic/quic_sent_packet_manager.h"
namespace net {
@@ -50,6 +51,8 @@ namespace test {
class QuicConnectionPeer;
} // namespace test
+// Class that receives callbacks from the connection when frames are received
+// and when other interesting events happen.
class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
public:
virtual ~QuicConnectionVisitorInterface() {}
@@ -58,10 +61,7 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
// 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;
+ virtual bool OnStreamFrames(const std::vector<QuicStreamFrame>& frames) = 0;
// Called when the stream is reset by the peer.
virtual void OnRstStream(const QuicRstStreamFrame& frame) = 0;
@@ -74,9 +74,6 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
virtual void ConnectionClose(QuicErrorCode error,
bool from_peer) = 0;
- // Called when packets are acked by the peer.
- virtual void OnAck(const SequenceNumberSet& acked_packets) = 0;
-
// Called once a specific QUIC version is agreed by both endpoints.
virtual void OnSuccessfulVersionNegotiation(const QuicVersion& version) = 0;
@@ -84,6 +81,9 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
// this visitor are consumed by the connection successfully this should
// return true, otherwise it should return false.
virtual bool OnCanWrite() = 0;
+
+ // Called to ask if any handshake messages are pending in this visitor.
+ virtual bool HasPendingHandshake() const = 0;
};
// Interface which gets callbacks from the QuicConnection at interesting
@@ -188,7 +188,8 @@ class NET_EXPORT_PRIVATE QuicConnectionHelperInterface {
class NET_EXPORT_PRIVATE QuicConnection
: public QuicFramerVisitorInterface,
public QuicBlockedWriterInterface,
- public QuicPacketGenerator::DelegateInterface {
+ public QuicPacketGenerator::DelegateInterface,
+ public QuicSentPacketManager::HelperInterface {
public:
enum Force {
NO_FORCE,
@@ -216,16 +217,17 @@ class NET_EXPORT_PRIVATE QuicConnection
// if the socket was unexpectedly blocked.
QuicConsumedData SendvStreamData(QuicStreamId id,
const struct iovec* iov,
- int count,
+ int iov_count,
QuicStreamOffset offset,
bool fin);
- // Same as above, except that the provided delegate will be informed once ACKs
- // have been received for all the packets written.
+ // Same as SendvStreamData, except the provided delegate will be informed
+ // once ACKs have been received for all the packets written.
// The |delegate| is not owned by the QuicConnection and must outlive it.
- QuicConsumedData SendStreamDataAndNotifyWhenAcked(
+ QuicConsumedData SendvStreamDataAndNotifyWhenAcked(
QuicStreamId id,
- base::StringPiece data,
+ const struct iovec* iov,
+ int iov_count,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier::DelegateInterface* delegate);
@@ -309,6 +311,12 @@ class NET_EXPORT_PRIVATE QuicConnection
virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() OVERRIDE;
virtual bool OnSerializedPacket(const SerializedPacket& packet) OVERRIDE;
+ // QuicSentPacketManager::HelperInterface
+ virtual QuicPacketSequenceNumber GetPeerLargestObservedPacket() OVERRIDE;
+ virtual QuicPacketSequenceNumber GetNextPacketSequenceNumber() OVERRIDE;
+ virtual void OnPacketNacked(QuicPacketSequenceNumber sequence_number,
+ size_t nack_count) OVERRIDE;
+
// Accessors
void set_visitor(QuicConnectionVisitorInterface* visitor) {
visitor_ = visitor;
@@ -415,14 +423,17 @@ class NET_EXPORT_PRIVATE QuicConnection
// is present in the |retransmission_map_|, then contents of this packet will
// be retransmitted with a new sequence number if it's not acked by the peer.
// Deletes |packet| via WritePacket call or transfers ownership to
- // QueuedPacket, ultimately deleted via WritePacket. Also, it updates the
+ // QueuedPacket, ultimately deleted via WritePacket. Updates the
// entropy map corresponding to |sequence_number| using |entropy_hash|.
+ // |retransmittable| is supplied to the congestion manager, and when |forced|
+ // is true, it bypasses the congestion manager.
// TODO(wtc): none of the callers check the return value.
virtual bool SendOrQueuePacket(EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
QuicPacketEntropyHash entropy_hash,
- HasRetransmittableData retransmittable);
+ HasRetransmittableData retransmittable,
+ Force forced);
// Writes the given packet to socket, encrypted with |level|, with the help
// of helper. Returns true on successful write, false otherwise. However,
@@ -458,23 +469,35 @@ class NET_EXPORT_PRIVATE QuicConnection
private:
friend class test::QuicConnectionPeer;
+ // Inner helper function to SendvStreamData and
+ // SendvStreamDataAndNotifyWhenAcked.
+ QuicConsumedData SendvStreamDataInner(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier *notifier);
+
// Packets which have not been written to the wire.
// Owns the QuicPacket* packet.
struct QueuedPacket {
QueuedPacket(QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
EncryptionLevel level,
- HasRetransmittableData retransmittable)
+ HasRetransmittableData retransmittable,
+ Force forced)
: sequence_number(sequence_number),
packet(packet),
encryption_level(level),
- retransmittable(retransmittable) {
+ retransmittable(retransmittable),
+ forced(forced) {
}
QuicPacketSequenceNumber sequence_number;
QuicPacket* packet;
const EncryptionLevel encryption_level;
HasRetransmittableData retransmittable;
+ Force forced;
};
struct RetransmissionInfo {
@@ -516,11 +539,7 @@ class NET_EXPORT_PRIVATE QuicConnection
};
typedef std::list<QueuedPacket> QueuedPacketList;
- typedef linked_hash_map<QuicPacketSequenceNumber,
- RetransmittableFrames*> UnackedPacketMap;
typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap;
- typedef base::hash_map<QuicPacketSequenceNumber,
- RetransmissionInfo> RetransmissionMap;
typedef std::priority_queue<RetransmissionTime,
std::vector<RetransmissionTime>,
RetransmissionTimeComparator>
@@ -572,11 +591,6 @@ class NET_EXPORT_PRIVATE QuicConnection
void ProcessAckFrame(const QuicAckFrame& incoming_ack);
- void HandleAckForSentPackets(const QuicAckFrame& incoming_ack,
- SequenceNumberSet* acked_packets);
- void HandleAckForSentFecPackets(const QuicAckFrame& incoming_ack,
- SequenceNumberSet* acked_packets);
-
// Update the |sent_info| for an outgoing ack.
void UpdateSentPacketInfo(SentPacketInfo* sent_info);
@@ -615,6 +629,9 @@ class NET_EXPORT_PRIVATE QuicConnection
std::vector<QuicCongestionFeedbackFrame> last_congestion_frames_;
std::vector<QuicRstStreamFrame> last_rst_frames_;
std::vector<QuicGoAwayFrame> last_goaway_frames_;
+ // Then number of packets retransmitted because of nacks
+ // while processed the current ack frame.
+ size_t retransmitted_nacked_packet_count_;
QuicCongestionFeedbackFrame outgoing_congestion_feedback_;
@@ -622,16 +639,6 @@ class NET_EXPORT_PRIVATE QuicConnection
// Largest sequence sent by the peer which had an ack frame (latest ack info).
QuicPacketSequenceNumber largest_seen_packet_with_ack_;
- // When new packets are created which may be retransmitted, they are added
- // to this map, which contains owning pointers to the contained frames.
- UnackedPacketMap unacked_packets_;
-
- // Pending fec packets that have not been acked yet. These packets need to be
- // cleared out of the cgst_window after a timeout since FEC packets are never
- // retransmitted.
- // Ask: What should be the timeout for these packets?
- UnackedPacketMap unacked_fec_packets_;
-
// Collection of packets which were received before encryption was
// established, but which could not be decrypted. We buffer these on
// the assumption that they could not be processed because they were
@@ -646,9 +653,6 @@ class NET_EXPORT_PRIVATE QuicConnection
// contains all packets that have been retransmitted x times.
RetransmissionTimeouts retransmission_timeouts_;
- // Map from sequence number to the retransmission info.
- RetransmissionMap retransmission_map_;
-
// True while OnRetransmissionTimeout is running to prevent
// SetRetransmissionAlarm from being called erroneously.
bool handling_retransmission_timeout_;
@@ -702,6 +706,10 @@ class NET_EXPORT_PRIVATE QuicConnection
// as well as collecting and generating congestion feedback.
QuicCongestionManager congestion_manager_;
+ // Sent packet manager which tracks the status of packets sent by this
+ // connection.
+ QuicSentPacketManager sent_packet_manager_;
+
// The state of connection in version negotiation finite state machine.
QuicVersionNegotiationState version_negotiation_state_;
diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc
index 366e97b..aaa692b 100644
--- a/net/quic/quic_connection_helper_test.cc
+++ b/net/quic/quic_connection_helper_test.cc
@@ -9,6 +9,7 @@
#include "net/base/net_errors.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_connection.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
@@ -18,6 +19,8 @@
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
+using testing::AnyNumber;
+using testing::Return;
namespace net {
namespace test {
@@ -120,13 +123,14 @@ class QuicConnectionHelperTest : public ::testing::Test {
&random_generator_, socket_.get());
send_algorithm_ = new testing::StrictMock<MockSendAlgorithm>();
EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)).
- WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
+ WillRepeatedly(Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillRepeatedly(
- testing::Return(QuicBandwidth::FromKBitsPerSecond(100)));
+ Return(QuicBandwidth::FromKBitsPerSecond(100)));
EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillRepeatedly(
- testing::Return(QuicTime::Delta::FromMilliseconds(100)));
+ Return(QuicTime::Delta::FromMilliseconds(100)));
ON_CALL(*send_algorithm_, SentPacket(_, _, _, _, _))
- .WillByDefault(testing::Return(true));
+ .WillByDefault(Return(true));
+ EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
connection_.reset(new TestConnection(guid_, IPEndPoint(), helper_));
connection_->set_visitor(&visitor_);
connection_->SetSendAlgorithm(send_algorithm_);
@@ -311,7 +315,7 @@ TEST_F(QuicConnectionHelperTest, TestRetransmission) {
Initialize();
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
- testing::Return(QuicTime::Delta::Zero()));
+ Return(QuicTime::Delta::Zero()));
QuicTime::Delta kDefaultRetransmissionTime =
QuicTime::Delta::FromMilliseconds(500);
@@ -341,7 +345,7 @@ TEST_F(QuicConnectionHelperTest, TestMultipleRetransmission) {
Initialize();
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
- testing::Return(QuicTime::Delta::Zero()));
+ Return(QuicTime::Delta::Zero()));
QuicTime::Delta kDefaultRetransmissionTime =
QuicTime::Delta::FromMilliseconds(500);
@@ -383,7 +387,10 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) {
EXPECT_EQ(base::TimeDelta::FromSeconds(kDefaultInitialTimeoutSecs),
runner_->GetPostedTasks().front().delay);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA));
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillOnce(
+ Return(QuicTime::Delta::FromMicroseconds(1)));
// After we run the next task, we should close the connection.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
@@ -410,7 +417,7 @@ TEST_F(QuicConnectionHelperTest, WritePacketToWireAsync) {
AddWrite(ASYNC, ConstructClosePacket(1, 0));
Initialize();
- EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true));
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(Return(true));
int error = 0;
EXPECT_EQ(-1, helper_->WritePacketToWire(*GetWrite(0), &error));
EXPECT_EQ(ERR_IO_PENDING, error);
@@ -447,8 +454,10 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) {
// This time, we should time out.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer));
- EXPECT_CALL(*send_algorithm_,
- SentPacket(_, 2, _, NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA));
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillOnce(
+ Return(QuicTime::Delta::FromMicroseconds(1)));
runner_->RunNextTask();
EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000 + 5000,
clock_.ApproximateNow().Subtract(
@@ -463,14 +472,15 @@ TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) {
// Test that if we send a packet with a delay, it ends up queued.
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
- testing::Return(QuicTime::Delta::Zero()));
+ Return(QuicTime::Delta::Zero()));
EXPECT_CALL(
*send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
- testing::Return(QuicTime::Delta::FromMicroseconds(1)));
+ Return(QuicTime::Delta::FromMicroseconds(1)));
QuicPacket* packet = ConstructRawDataPacket(1);
connection_->SendOrQueuePacket(ENCRYPTION_NONE, 1, packet, 0,
- HAS_RETRANSMITTABLE_DATA);
+ HAS_RETRANSMITTABLE_DATA,
+ QuicConnection::NO_FORCE);
EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION,
_));
EXPECT_EQ(1u, connection_->NumQueuedPackets());
@@ -479,8 +489,9 @@ TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) {
// to permit the packet to be sent.
EXPECT_CALL(*send_algorithm_,
TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
- testing::Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true));
+ Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(Return(true));
+ EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
runner_->RunNextTask();
EXPECT_EQ(0u, connection_->NumQueuedPackets());
EXPECT_TRUE(AtEof());
diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc
index 147d8bc..afe8a76 100644
--- a/net/quic/quic_connection_test.cc
+++ b/net/quic/quic_connection_test.cc
@@ -389,6 +389,19 @@ class TestConnection : public QuicConnection {
QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm);
}
+ bool SendOrQueuePacket(EncryptionLevel level,
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacket* packet,
+ QuicPacketEntropyHash entropy_hash,
+ HasRetransmittableData retransmittable) {
+ return SendOrQueuePacket(level,
+ sequence_number,
+ packet,
+ entropy_hash,
+ retransmittable,
+ NO_FORCE);
+ }
+
QuicConsumedData SendStreamData(QuicStreamId id,
StringPiece data,
QuicStreamOffset offset,
@@ -398,6 +411,18 @@ class TestConnection : public QuicConnection {
return SendvStreamData(id, &iov, 1, offset, fin);
}
+ QuicConsumedData SendStreamDataAndNotifyWhenAcked(
+ QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ struct iovec iov = {const_cast<char*>(data.data()),
+ static_cast<size_t>(data.size())};
+ return SendvStreamDataAndNotifyWhenAcked(id, &iov, 1, offset, fin,
+ delegate);
+ }
+
QuicConsumedData SendStreamData3() {
return SendStreamData(kStreamId3, "food", 0, !kFin);
}
@@ -491,6 +516,7 @@ class QuicConnectionTest : public ::testing::Test {
.WillByDefault(Return(true));
// TODO(rch): remove this.
QuicConnection::g_acks_do_not_instigate_acks = true;
+ EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
}
~QuicConnectionTest() {
@@ -528,8 +554,7 @@ class QuicConnectionTest : public ::testing::Test {
}
void ProcessPacket(QuicPacketSequenceNumber number) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _))
- .WillOnce(Return(accept_packet_));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
ProcessDataPacket(number, 0, !kEntropyFlag);
}
@@ -577,10 +602,9 @@ class QuicConnectionTest : public ::testing::Test {
size_t ProcessFecProtectedPacket(QuicPacketSequenceNumber number,
bool expect_revival, bool entropy_flag) {
if (expect_revival) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(DoAll(
- SaveArg<2>(&revived_header_), Return(accept_packet_)));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
}
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(Return(accept_packet_))
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_))
.RetiresOnSaturation();
return ProcessDataPacket(number, 1, entropy_flag);
}
@@ -593,8 +617,7 @@ class QuicConnectionTest : public ::testing::Test {
bool entropy_flag,
QuicPacket* packet) {
if (expect_revival) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(DoAll(
- SaveArg<2>(&revived_header_), Return(accept_packet_)));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
}
// Construct the decrypted data packet so we can compute the correct
@@ -745,7 +768,6 @@ class QuicConnectionTest : public ::testing::Test {
StrictMock<MockConnectionVisitor> visitor_;
QuicPacketHeader header_;
- QuicPacketHeader revived_header_;
QuicStreamFrame frame1_;
QuicStreamFrame frame2_;
scoped_ptr<QuicAckFrame> outgoing_ack_;
@@ -813,7 +835,7 @@ TEST_F(QuicConnectionTest, DuplicatePacket) {
EXPECT_TRUE(IsMissing(1));
// Send packet 3 again, but do not set the expectation that
- // the visitor OnPacket() will be called.
+ // the visitor OnStreamFrames() will be called.
ProcessDataPacket(3, 0, !kEntropyFlag);
EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed);
EXPECT_TRUE(IsMissing(2));
@@ -859,7 +881,6 @@ TEST_F(QuicConnectionTest, RejectPacketTooFarOut) {
TEST_F(QuicConnectionTest, TruncatedAck) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(visitor_, OnAck(_)).Times(testing::AnyNumber());
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
for (int i = 0; i < 200; ++i) {
@@ -979,7 +1000,6 @@ TEST_F(QuicConnectionTest, LargestObservedLower) {
QuicAckFrame frame(2, QuicTime::Zero(), 0);
frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
&connection_, 2);
- EXPECT_CALL(visitor_, OnAck(_));
ProcessAckPacket(&frame, true);
// Now change it to 1, and it should cause a connection error.
@@ -1149,11 +1169,7 @@ TEST_F(QuicConnectionTest, BasicSending) {
SendAckPacketToPeer(); // Packet 5
EXPECT_EQ(1u, last_ack()->sent_info.least_unacked);
- SequenceNumberSet expected_acks;
- expected_acks.insert(1);
-
// Peer acks up to packet 3.
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
QuicAckFrame frame(3, QuicTime::Zero(), 0);
frame.received_info.entropy_hash =
QuicConnectionPeer::GetSentEntropyHash(&connection_, 3);
@@ -1164,11 +1180,7 @@ TEST_F(QuicConnectionTest, BasicSending) {
// ack for 4.
EXPECT_EQ(4u, last_ack()->sent_info.least_unacked);
- expected_acks.clear();
- expected_acks.insert(4);
-
// Peer acks up to packet 4, the last packet.
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
QuicAckFrame frame2(6, QuicTime::Zero(), 0);
frame2.received_info.entropy_hash =
QuicConnectionPeer::GetSentEntropyHash(&connection_, 6);
@@ -1265,7 +1277,6 @@ TEST_F(QuicConnectionTest, DontAbandonAckedFEC) {
QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- EXPECT_CALL(visitor_, OnAck(_)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
@@ -1507,9 +1518,6 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) {
SendStreamDataToPeer(1, "foos", 3, !kFin, &last_packet); // Packet 2
SendStreamDataToPeer(1, "fooos", 7, !kFin, &last_packet); // Packet 3
- SequenceNumberSet expected_acks;
- expected_acks.insert(1);
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Peer acks one but not two or three. Right now we only retransmit on
@@ -1521,10 +1529,6 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) {
ProcessAckPacket(&ack_one, true);
ProcessAckPacket(&ack_one, true);
- expected_acks.clear();
- expected_acks.insert(3);
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
-
// Peer acks up to 3 with two explicitly missing. Two nacks should cause no
// change.
QuicAckFrame nack_two(3, QuicTime::Zero(), 0);
@@ -1581,7 +1585,6 @@ TEST_F(QuicConnectionTest, RetransmitNackedPacketsOnTruncatedAck) {
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
- EXPECT_CALL(visitor_, OnAck(_)).Times(1);
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessAckPacket(&frame, true);
EXPECT_TRUE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_));
@@ -1623,9 +1626,6 @@ TEST_F(QuicConnectionTest, LimitPacketsPerNack) {
nack.received_info.entropy_hash =
QuicConnectionPeer::GetSentEntropyHash(&connection_, 12) ^
QuicConnectionPeer::GetSentEntropyHash(&connection_, 11);
- SequenceNumberSet expected_acks;
- expected_acks.insert(12);
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
// Nack three times.
ProcessAckPacket(&nack, true);
@@ -1664,14 +1664,6 @@ TEST_F(QuicConnectionTest, MultipleAcks) {
QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- // 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.
- SequenceNumberSet expected_acks;
- expected_acks.insert(1);
- expected_acks.insert(4);
- expected_acks.insert(5);
-
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessAckPacket(&frame1, true);
@@ -1680,12 +1672,7 @@ TEST_F(QuicConnectionTest, MultipleAcks) {
QuicAckFrame frame2(6, QuicTime::Zero(), 0);
frame2.received_info.entropy_hash =
QuicConnectionPeer::GetSentEntropyHash(&connection_, 6);
- expected_acks.clear();
- // Both acks should be passed up.
- expected_acks.insert(2);
- expected_acks.insert(6);
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
ProcessAckPacket(&frame2, true);
}
@@ -1694,12 +1681,6 @@ TEST_F(QuicConnectionTest, DontLatchUnackedPacket) {
SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); // Packet 1;
SendAckPacketToPeer(); // Packet 2
- // This sets least unacked to 3 (unsent packet), since we don't need
- // an ack for Packet 2 (ack packet).
- SequenceNumberSet expected_acks;
- expected_acks.insert(1);
- // Peer acks packet 1.
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicAckFrame frame(1, QuicTime::Zero(), 0);
frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
@@ -1729,7 +1710,8 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) {
// Don't send missing packet 1.
ProcessFecPacket(2, 1, true, !kEntropyFlag, NULL);
- EXPECT_FALSE(revived_header_.entropy_flag);
+ // Entropy flag should be false, so entropy should be 0.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) {
@@ -1738,7 +1720,8 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) {
ProcessFecProtectedPacket(1, false, kEntropyFlag);
// Don't send missing packet 2.
ProcessFecPacket(3, 1, true, !kEntropyFlag, NULL);
- EXPECT_TRUE(revived_header_.entropy_flag);
+ // Entropy flag should be true, so entropy should not be 0.
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) {
@@ -1748,7 +1731,8 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) {
// Don't send missing packet 2.
ProcessFecProtectedPacket(3, false, !kEntropyFlag);
ProcessFecPacket(4, 1, true, kEntropyFlag, NULL);
- EXPECT_TRUE(revived_header_.entropy_flag);
+ // Entropy flag should be true, so entropy should not be 0.
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
@@ -1758,7 +1742,8 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
ProcessFecPacket(3, 1, false, !kEntropyFlag, NULL);
// out of order
ProcessFecProtectedPacket(2, true, !kEntropyFlag);
- EXPECT_FALSE(revived_header_.entropy_flag);
+ // Entropy flag should be false, so entropy should be 0.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) {
@@ -1770,7 +1755,8 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) {
ProcessFecProtectedPacket(3, false, kEntropyFlag);
ProcessFecProtectedPacket(4, false, kEntropyFlag);
ProcessFecProtectedPacket(5, true, !kEntropyFlag);
- EXPECT_TRUE(revived_header_.entropy_flag);
+ // Entropy flag should be true, so entropy should be 0.
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
TEST_F(QuicConnectionTest, TestRetransmit) {
@@ -1883,13 +1869,13 @@ TEST_F(QuicConnectionTest, BufferNonDecryptablePackets) {
connection_.SetDecrypter(new StrictTaggingDecrypter(tag));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag));
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(2).WillRepeatedly(
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(2).WillRepeatedly(
Return(true));
ProcessDataPacketAtLevel(2, false, kEntropyFlag, ENCRYPTION_INITIAL);
// Finally, process a third packet and note that we do not
// reprocess the buffered packet.
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(true));
ProcessDataPacketAtLevel(3, false, kEntropyFlag, ENCRYPTION_INITIAL);
}
@@ -1978,6 +1964,45 @@ TEST_F(QuicConnectionTest, SetRTOAfterWritingToSocket) {
EXPECT_EQ(1u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
}
+TEST_F(QuicConnectionTest, DelayRTOWithAckReceipt) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION, _))
+ .Times(2);
+ connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamData(2, "bar", 0, !kFin);
+ EXPECT_EQ(2u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
+
+ // Advance the time right before the RTO, then receive an ack for the first
+ // packet to delay the RTO.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_EQ(2u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
+ EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
+ QuicAckFrame ack(1, QuicTime::Zero(), 0);
+ ProcessAckPacket(&ack, true);
+ EXPECT_EQ(1u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
+
+ // Move forward past the original RTO and ensure the RTO is still pending.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_EQ(1u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
+
+ // Ensure the second packet gets retransmitted when it finally fires.
+ EXPECT_TRUE(
+ QuicConnectionPeer::GetRetransmissionAlarm(&connection_)->IsSet());
+ EXPECT_GE(
+ QuicConnectionPeer::GetRetransmissionAlarm(&connection_)->deadline(),
+ clock_.ApproximateNow());
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_LT(
+ QuicConnectionPeer::GetRetransmissionAlarm(&connection_)->deadline(),
+ clock_.ApproximateNow());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, IS_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _));
+ connection_.OnRetransmissionTimeout();
+
+ // The new retransmitted sequence number should now be in the timeout queue.
+ EXPECT_EQ(1u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
+}
+
TEST_F(QuicConnectionTest, TestQueued) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
helper_->set_blocked(true);
@@ -2362,7 +2387,7 @@ TEST_F(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) {
}
TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(4, 1, kEntropyFlag);
@@ -2372,7 +2397,7 @@ TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) {
}
TEST_F(QuicConnectionTest, UpdateEntropyForReceivedPackets) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(5, 1, kEntropyFlag);
@@ -2393,7 +2418,7 @@ TEST_F(QuicConnectionTest, UpdateEntropyForReceivedPackets) {
}
TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(5, 1, !kEntropyFlag);
@@ -2413,7 +2438,7 @@ TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
}
TEST_F(QuicConnectionTest, EntropyCalculationForTruncatedAck) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacketEntropyHash entropy[51];
entropy[0] = 0;
@@ -2538,7 +2563,7 @@ TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) {
scoped_ptr<QuicPacket> packet(
framer_.BuildUnsizedDataPacket(header, frames).packet);
encrypted.reset(framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
@@ -2599,7 +2624,6 @@ TEST_F(QuicConnectionTest, CheckSendStats) {
QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
QuicConnectionPeer::GetSentEntropyHash(&connection_, 2);
QuicFrame frame(&nack_three);
- EXPECT_CALL(visitor_, OnAck(_));
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
EXPECT_CALL(visitor_, OnCanWrite()).Times(3).WillRepeatedly(Return(true));
@@ -2695,7 +2719,7 @@ TEST_F(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) {
ENCRYPTION_NONE, 1, *packet));
EXPECT_CALL(visitor_, ConnectionClose(QUIC_PEER_GOING_AWAY, true));
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(0);
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(0);
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
@@ -2779,7 +2803,6 @@ TEST_F(QuicConnectionTest, AckNotifierTriggerCallback) {
connection_.SendStreamDataAndNotifyWhenAcked(1, "foo", 0, !kFin, &delegate);
// Process an ACK from the server which should trigger the callback.
- EXPECT_CALL(visitor_, OnAck(_)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
QuicAckFrame frame(1, QuicTime::Zero(), 0);
ProcessAckPacket(&frame, true);
@@ -2792,7 +2815,6 @@ TEST_F(QuicConnectionTest, AckNotifierFailToTriggerCallback) {
MockAckNotifierDelegate delegate;
EXPECT_CALL(delegate, OnAckNotification()).Times(0);;
- EXPECT_CALL(visitor_, OnAck(_)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
@@ -2818,9 +2840,6 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) {
MockAckNotifierDelegate delegate;
EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
- // OnAck called twice: once with missing packet, once after retransmit.
- EXPECT_CALL(visitor_, OnAck(_)).Times(2);
-
// In total expect ACKs for all 4 packets.
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(4);
@@ -2863,7 +2882,6 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
// Expect ACKs for 1 packet.
- EXPECT_CALL(visitor_, OnAck(_)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
// Send one packet, and register to be notified on ACK.
diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc
index 4160ddc..cfc8c95 100644
--- a/net/quic/quic_framer.cc
+++ b/net/quic/quic_framer.cc
@@ -1040,7 +1040,7 @@ bool QuicFramer::ProcessFrameData() {
// TODO(jri): Retain this else block when support for
// QUIC version < 10 removed. Remove above if block.
- // Special frame type processing for QUIC version >= 10
+ // Special frame type processing for QUIC version >= 10.
if (frame_type & kQuicFrameTypeSpecialMask) {
// Stream Frame
if (frame_type & kQuicFrameTypeStreamMask) {
diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc
index 819c872..609ebcb 100644
--- a/net/quic/quic_packet_creator.cc
+++ b/net/quic/quic_packet_creator.cc
@@ -32,7 +32,7 @@ QuicPacketCreator::QuicPacketCreator(QuicGuid guid,
send_version_in_packet_(!is_server),
sequence_number_length_(options_.send_sequence_number_length),
packet_size_(0) {
- framer_->set_fec_builder(reinterpret_cast<QuicFecBuilderInterface*>(this));
+ framer_->set_fec_builder(this);
}
QuicPacketCreator::~QuicPacketCreator() {
diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc
index 4650068..9151cb8 100644
--- a/net/quic/quic_packet_generator.cc
+++ b/net/quic/quic_packet_generator.cc
@@ -70,13 +70,6 @@ void QuicPacketGenerator::AddControlFrame(const QuicFrame& frame) {
QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
StringPiece data,
QuicStreamOffset offset,
- bool fin) {
- return ConsumeData(id, data, offset, fin, NULL);
-}
-
-QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
- StringPiece data,
- QuicStreamOffset offset,
bool fin,
QuicAckNotifier* notifier) {
IsHandshake handshake = id == kCryptoStreamId ? IS_HANDSHAKE : NOT_HANDSHAKE;
@@ -203,7 +196,7 @@ bool QuicPacketGenerator::HasPendingFrames() const {
bool QuicPacketGenerator::AddNextPendingFrame() {
if (should_send_ack_) {
- pending_ack_frame_.reset((delegate_->CreateAckFrame()));
+ pending_ack_frame_.reset(delegate_->CreateAckFrame());
// If we can't this add the frame now, then we still need to do so later.
should_send_ack_ = !AddFrame(QuicFrame(pending_ack_frame_.get()));
// Return success if we have cleared out this flag (i.e., added the frame).
@@ -212,7 +205,7 @@ bool QuicPacketGenerator::AddNextPendingFrame() {
}
if (should_send_feedback_) {
- pending_feedback_frame_.reset((delegate_->CreateFeedbackFrame()));
+ pending_feedback_frame_.reset(delegate_->CreateFeedbackFrame());
// If we can't this add the frame now, then we still need to do so later.
should_send_feedback_ = !AddFrame(QuicFrame(pending_feedback_frame_.get()));
// Return success if we have cleared out this flag (i.e., added the frame).
diff --git a/net/quic/quic_packet_generator.h b/net/quic/quic_packet_generator.h
index 8d415bf..940b259 100644
--- a/net/quic/quic_packet_generator.h
+++ b/net/quic/quic_packet_generator.h
@@ -15,7 +15,7 @@
// If the Delegate is not writable, then no operations will cause
// a packet to be serialized. In particular:
// * SetShouldSendAck will simply record that an ack is to be sent.
-// * AddControlFram will enqueue the control frame.
+// * AddControlFrame will enqueue the control frame.
// * ConsumeData will do nothing.
//
// If the Delegate is writable, then the behavior depends on the second
@@ -91,20 +91,20 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
virtual ~QuicPacketGenerator();
+ // Indicates that an ACK frame should be sent. If |also_send_feedback| is
+ // true, then it also indicates a CONGESTION_FEEDBACK frame should be sent.
+ // The contents of the frame(s) will be generated via a call to the delegates
+ // CreateAckFrame() and CreateFeedbackFrame() when the packet is serialized.
void SetShouldSendAck(bool also_send_feedback);
void AddControlFrame(const QuicFrame& frame);
- // Given some data, may consume part or all of it and pass it to the packet
- // creator to be serialized into packets. If not in batch mode, these packets
- // will also be sent during this call.
- QuicConsumedData ConsumeData(QuicStreamId id,
- base::StringPiece data,
- QuicStreamOffset offset,
- bool fin);
-
- // As above, but attaches a QuicAckNotifier to any created stream frames,
- // which will be called once the frame is ACKed by the peer.
- // The QuicAckNotifier is owned by the QuicConnection.
+ // Given some data, may consume part or all of it and pass it to the
+ // packet creator to be serialized into packets. If not in batch
+ // mode, these packets will also be sent during this call. Also
+ // attaches a QuicAckNotifier to any created stream frames, which
+ // will be called once the frame is ACKed by the peer. The
+ // QuicAckNotifier is owned by the QuicConnection. |notifier| may
+ // be NULL.
QuicConsumedData ConsumeData(QuicStreamId id,
base::StringPiece data,
QuicStreamOffset offset,
diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc
index eabbec1..ec8dd56 100644
--- a/net/quic/quic_packet_generator_test.cc
+++ b/net/quic/quic_packet_generator_test.cc
@@ -327,7 +327,7 @@ TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) {
TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) {
delegate_.SetCanNotWrite();
- QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true);
+ QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true, NULL);
EXPECT_EQ(0u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -337,7 +337,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) {
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true);
+ QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -348,7 +348,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) {
EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
DoAll(SaveArg<0>(&packet_), Return(true)));
- QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true);
+ QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -363,8 +363,8 @@ TEST_F(QuicPacketGeneratorTest,
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- generator_.ConsumeData(1, "foo", 2, true);
- QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false);
+ generator_.ConsumeData(1, "foo", 2, true, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false, NULL);
EXPECT_EQ(4u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -374,8 +374,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) {
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- generator_.ConsumeData(1, "foo", 2, true);
- QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false);
+ generator_.ConsumeData(1, "foo", 2, true, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false, NULL);
EXPECT_EQ(4u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -414,7 +414,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) {
// Send enough data to create 3 packets: two full and one partial.
size_t data_len = 2 * kMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -446,7 +446,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) {
// Send enough data to create 2 packets: one full and one partial.
size_t data_len = 1 * kMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -480,7 +480,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
generator_.StartBatchOperations();
// Queue enough data to prevent a stream frame with a non-zero offset from
// fitting.
- QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 0, false);
+ QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 0, false, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -488,7 +488,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
// This frame will not fit with the existing frame, causing the queued frame
// to be serialized, and it will not fit with another frame like it, so it is
// serialized by itself.
- consumed = generator_.ConsumeData(1, "bar", 3, true);
+ consumed = generator_.ConsumeData(1, "bar", 3, true, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -517,7 +517,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
Return(CreateFeedbackFrame()));
// Send some data and a control frame
- generator_.ConsumeData(3, "quux", 7, false);
+ generator_.ConsumeData(3, "quux", 7, false, NULL);
generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
// All five frames will be flushed out in a single packet.
@@ -564,7 +564,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) {
// Send enough data to exceed one packet
size_t data_len = kMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
diff --git a/net/quic/quic_sent_packet_manager.cc b/net/quic/quic_sent_packet_manager.cc
new file mode 100644
index 0000000..a8960f0
--- /dev/null
+++ b/net/quic/quic_sent_packet_manager.cc
@@ -0,0 +1,221 @@
+// Copyright 2013 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_sent_packet_manager.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+using std::make_pair;
+
+namespace net {
+
+#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
+
+QuicSentPacketManager::HelperInterface::~HelperInterface() {
+}
+
+QuicSentPacketManager::QuicSentPacketManager(bool is_server,
+ HelperInterface* helper)
+ : is_server_(is_server),
+ helper_(helper) {
+}
+
+QuicSentPacketManager::~QuicSentPacketManager() {
+ STLDeleteValues(&unacked_packets_);
+}
+
+void QuicSentPacketManager::OnSerializedPacket(
+ const SerializedPacket& serialized_packet) {
+ if (serialized_packet.packet->is_fec_packet()) {
+ unacked_fec_packets_.insert(make_pair(
+ serialized_packet.sequence_number,
+ serialized_packet.retransmittable_frames));
+ return;
+ }
+
+ if (serialized_packet.retransmittable_frames == NULL) {
+ // Don't track ack/congestion feedback packets.
+ return;
+ }
+
+ DCHECK(unacked_packets_.empty() ||
+ unacked_packets_.rbegin()->first <
+ serialized_packet.sequence_number);
+ unacked_packets_[serialized_packet.sequence_number] =
+ serialized_packet.retransmittable_frames;
+ retransmission_map_[serialized_packet.sequence_number] =
+ RetransmissionInfo(serialized_packet.sequence_number,
+ serialized_packet.sequence_number_length);
+}
+
+void QuicSentPacketManager::OnRetransmittedPacket(
+ QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number) {
+ DCHECK(ContainsKey(unacked_packets_, old_sequence_number));
+ DCHECK(ContainsKey(retransmission_map_, old_sequence_number));
+ DCHECK(unacked_packets_.empty() ||
+ unacked_packets_.rbegin()->first < new_sequence_number);
+
+ RetransmissionInfo retransmission_info(
+ new_sequence_number, GetSequenceNumberLength(old_sequence_number));
+ retransmission_info.number_retransmissions =
+ retransmission_map_[old_sequence_number].number_retransmissions + 1;
+ retransmission_map_.erase(old_sequence_number);
+ retransmission_map_[new_sequence_number] = retransmission_info;
+
+ RetransmittableFrames* frames = unacked_packets_[old_sequence_number];
+ DCHECK(frames);
+ unacked_packets_.erase(old_sequence_number);
+ unacked_packets_[new_sequence_number] = frames;
+}
+
+void QuicSentPacketManager::HandleAckForSentPackets(
+ const QuicAckFrame& incoming_ack,
+ SequenceNumberSet* acked_packets) {
+ // 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()) {
+ QuicPacketSequenceNumber sequence_number = it->first;
+ if (sequence_number > helper_->GetPeerLargestObservedPacket()) {
+ // These are very new sequence_numbers.
+ break;
+ }
+ RetransmittableFrames* unacked = it->second;
+ if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) {
+ // Packet was acked, so remove it from our unacked packet list.
+ DVLOG(1) << ENDPOINT <<"Got an ack for packet " << sequence_number;
+ acked_packets->insert(sequence_number);
+ delete unacked;
+ unacked_packets_.erase(it++);
+ retransmission_map_.erase(sequence_number);
+ } else {
+ // This is a packet which we planned on retransmitting 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) << ENDPOINT << "still missing packet " << sequence_number;
+ ++it;
+ // The peer got packets after this sequence number. This is an explicit
+ // nack.
+ RetransmissionMap::iterator retransmission_it =
+ retransmission_map_.find(sequence_number);
+ if (retransmission_it == retransmission_map_.end()) {
+ continue;
+ }
+ size_t nack_count = ++(retransmission_it->second.number_nacks);
+ helper_->OnPacketNacked(sequence_number, nack_count);
+ }
+ }
+}
+
+void QuicSentPacketManager::HandleAckForSentFecPackets(
+ const QuicAckFrame& incoming_ack,
+ SequenceNumberSet* acked_packets) {
+ UnackedPacketMap::iterator it = unacked_fec_packets_.begin();
+ while (it != unacked_fec_packets_.end()) {
+ QuicPacketSequenceNumber sequence_number = it->first;
+ if (sequence_number > helper_->GetPeerLargestObservedPacket()) {
+ break;
+ }
+ if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) {
+ DVLOG(1) << ENDPOINT << "Got an ack for fec packet: " << sequence_number;
+ acked_packets->insert(sequence_number);
+ unacked_fec_packets_.erase(it++);
+ } else {
+ DVLOG(1) << ENDPOINT << "Still missing ack for fec packet: "
+ << sequence_number;
+ ++it;
+ }
+ }
+}
+
+void QuicSentPacketManager::DiscardPacket(
+ QuicPacketSequenceNumber sequence_number) {
+ UnackedPacketMap::iterator unacked_it =
+ unacked_packets_.find(sequence_number);
+ if (unacked_it == unacked_packets_.end()) {
+ // Packet was not meant to be retransmitted.
+ DCHECK(!ContainsKey(retransmission_map_, sequence_number));
+ return;
+ }
+
+ // Delete the unacked packet.
+ delete unacked_it->second;
+ unacked_packets_.erase(unacked_it);
+ retransmission_map_.erase(sequence_number);
+}
+
+bool QuicSentPacketManager::IsRetransmission(
+ QuicPacketSequenceNumber sequence_number) const {
+ RetransmissionMap::const_iterator it =
+ retransmission_map_.find(sequence_number);
+ return it != retransmission_map_.end() &&
+ it->second.number_retransmissions > 0;
+}
+
+size_t QuicSentPacketManager::GetRetransmissionCount(
+ QuicPacketSequenceNumber sequence_number) const {
+ DCHECK(ContainsKey(retransmission_map_, sequence_number));
+ RetransmissionMap::const_iterator it =
+ retransmission_map_.find(sequence_number);
+ return it->second.number_retransmissions;
+}
+
+bool QuicSentPacketManager::IsUnacked(
+ QuicPacketSequenceNumber sequence_number) const {
+ return ContainsKey(unacked_packets_, sequence_number);
+}
+
+bool QuicSentPacketManager::IsFecUnacked(
+ QuicPacketSequenceNumber sequence_number) const {
+ return ContainsKey(unacked_fec_packets_, sequence_number);
+}
+
+const RetransmittableFrames& QuicSentPacketManager::GetRetransmittableFrames(
+ QuicPacketSequenceNumber sequence_number) const {
+ DCHECK(ContainsKey(unacked_packets_, sequence_number));
+ DCHECK(ContainsKey(retransmission_map_, sequence_number));
+
+ return *unacked_packets_.find(sequence_number)->second;
+}
+
+QuicSequenceNumberLength QuicSentPacketManager::GetSequenceNumberLength(
+ QuicPacketSequenceNumber sequence_number) const {
+ DCHECK(ContainsKey(unacked_packets_, sequence_number));
+ DCHECK(ContainsKey(retransmission_map_, sequence_number));
+
+ return retransmission_map_.find(
+ sequence_number)->second.sequence_number_length;
+}
+
+bool QuicSentPacketManager::HasUnackedPackets() const {
+ return !unacked_packets_.empty();
+}
+
+size_t QuicSentPacketManager::GetNumUnackedPackets() const {
+ return unacked_packets_.size();
+}
+
+QuicPacketSequenceNumber
+QuicSentPacketManager::GetLeastUnackedSentPacket() const {
+ if (unacked_packets_.empty()) {
+ // If there are no unacked packets, set the least unacked packet to
+ // the sequence number of the next packet sent.
+ return helper_->GetNextPacketSequenceNumber();
+ }
+
+ return unacked_packets_.begin()->first;
+}
+
+SequenceNumberSet QuicSentPacketManager::GetUnackedPackets() const {
+ SequenceNumberSet unacked_packets;
+ for (UnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ unacked_packets.insert(it->first);
+ }
+ return unacked_packets;
+}
+
+} // namespace net
diff --git a/net/quic/quic_sent_packet_manager.h b/net/quic/quic_sent_packet_manager.h
new file mode 100644
index 0000000..355ea49
--- /dev/null
+++ b/net/quic/quic_sent_packet_manager.h
@@ -0,0 +1,140 @@
+// Copyright 2013 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.
+
+#ifndef NET_QUIC_QUIC_SENT_PACKET_MANAGER_H_
+#define NET_QUIC_QUIC_SENT_PACKET_MANAGER_H_
+
+#include <deque>
+#include <list>
+#include <map>
+#include <queue>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE QuicSentPacketManager {
+ public:
+ class NET_EXPORT_PRIVATE HelperInterface {
+ public:
+ virtual QuicPacketSequenceNumber GetPeerLargestObservedPacket() = 0;
+ virtual QuicPacketSequenceNumber GetNextPacketSequenceNumber() = 0;
+
+ // Called when a packet has been explicitly NACKd
+ virtual void OnPacketNacked(QuicPacketSequenceNumber sequence_number,
+ size_t nack_count) = 0;
+ virtual ~HelperInterface();
+ };
+
+ QuicSentPacketManager(bool is_server, HelperInterface* helper);
+ virtual ~QuicSentPacketManager();
+
+ // Called when a new packet is serialized. If the packet contains
+ // retransmittable data, it will be added to the unacked packet map.
+ void OnSerializedPacket(const SerializedPacket& serialized_packet);
+
+ // Called when a packet is retransmitted with a new sequence number.
+ // Replaces the old entry in the unacked packet map with the new
+ // sequence number.
+ void OnRetransmittedPacket(QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number);
+
+ // Process the incoming ack looking for newly ack'd data packets.
+ void HandleAckForSentPackets(const QuicAckFrame& incoming_ack,
+ SequenceNumberSet* acked_packets);
+
+ // Process the incoming ack looking for newly ack'd FEC packets.
+ void HandleAckForSentFecPackets(const QuicAckFrame& incoming_ack,
+ SequenceNumberSet* acked_packets);
+
+ // Discards all information about packet |sequence_number|.
+ void DiscardPacket(QuicPacketSequenceNumber sequence_number);
+
+ // Returns true if |sequence_number| is a retransmission of a packet.
+ bool IsRetransmission(QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns the number of times the data in the packet |sequence_number|
+ // has been transmitted.
+ size_t GetRetransmissionCount(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns true if the non-FEC packet |sequence_number| is unacked.
+ bool IsUnacked(QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns true if the FEC packet |sequence_number| is unacked.
+ bool IsFecUnacked(QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns the RetransmittableFrames for |sequence_number|.
+ const RetransmittableFrames& GetRetransmittableFrames(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns the length of the serialized sequence number for
+ // the packet |sequence_number|.
+ QuicSequenceNumberLength GetSequenceNumberLength(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns true if there are any unacked packets.
+ bool HasUnackedPackets() const;
+
+ // Returns the number of unacked packets.
+ size_t GetNumUnackedPackets() const;
+
+ // Returns the smallest sequence number of a sent packet which has not
+ // been acked by the peer. If all packets have been acked, returns the
+ // sequence number of the next packet that will be sent.
+ QuicPacketSequenceNumber GetLeastUnackedSentPacket() const;
+
+ // Returns the set of unacked packet sequence numbers.
+ SequenceNumberSet GetUnackedPackets() const;
+
+ private:
+ struct RetransmissionInfo {
+ RetransmissionInfo() {}
+ explicit RetransmissionInfo(QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length)
+ : sequence_number(sequence_number),
+ sequence_number_length(sequence_number_length),
+ number_nacks(0),
+ number_retransmissions(0) {
+ }
+
+ QuicPacketSequenceNumber sequence_number;
+ QuicSequenceNumberLength sequence_number_length;
+ size_t number_nacks;
+ size_t number_retransmissions;
+ };
+
+ typedef linked_hash_map<QuicPacketSequenceNumber,
+ RetransmittableFrames*> UnackedPacketMap;
+ typedef base::hash_map<QuicPacketSequenceNumber,
+ RetransmissionInfo> RetransmissionMap;
+
+ // When new packets are created which may be retransmitted, they are added
+ // to this map, which contains owning pointers to the contained frames.
+ UnackedPacketMap unacked_packets_;
+
+ // Pending fec packets that have not been acked yet. These packets need to be
+ // cleared out of the cgst_window after a timeout since FEC packets are never
+ // retransmitted.
+ // TODO(satyamshekhar): What should be the timeout for these packets?
+ UnackedPacketMap unacked_fec_packets_;
+
+ // Map from sequence number to the retransmission info.
+ RetransmissionMap retransmission_map_;
+
+ // Tracks if the connection was created by the server.
+ bool is_server_;
+ HelperInterface* helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManager);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SENT_PACKET_MANAGER_H_
diff --git a/net/quic/quic_sent_packet_manager_test.cc b/net/quic/quic_sent_packet_manager_test.cc
new file mode 100644
index 0000000..8f5cb3d
--- /dev/null
+++ b/net/quic/quic_sent_packet_manager_test.cc
@@ -0,0 +1,80 @@
+// Copyright 2013 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_sent_packet_manager.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Return;
+using testing::StrictMock;
+
+namespace net {
+namespace test {
+namespace {
+
+class MockHelper : public QuicSentPacketManager::HelperInterface {
+ public:
+ MOCK_METHOD0(GetPeerLargestObservedPacket, QuicPacketSequenceNumber());
+ MOCK_METHOD0(GetNextPacketSequenceNumber, QuicPacketSequenceNumber());
+ MOCK_METHOD2(OnPacketNacked, void(QuicPacketSequenceNumber sequence_number,
+ size_t nack_count));
+};
+
+class QuicSentPacketManagerTest : public ::testing::Test {
+ protected:
+ QuicSentPacketManagerTest()
+ : manager_(true, &helper_) {
+ }
+
+ testing::StrictMock<MockHelper> helper_;
+ QuicSentPacketManager manager_;
+};
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacket) {
+ EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(1u));
+ EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketUnacked) {
+ scoped_ptr<QuicPacket> packet(QuicPacket::NewDataPacket(
+ NULL, 0, false, PACKET_8BYTE_GUID, false, PACKET_6BYTE_SEQUENCE_NUMBER));
+ SerializedPacket serialized_packet(1u, PACKET_6BYTE_SEQUENCE_NUMBER,
+ packet.get(), 7u,
+ new RetransmittableFrames());
+
+ manager_.OnSerializedPacket(serialized_packet);
+ EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketUnackedFec) {
+ scoped_ptr<QuicPacket> packet(QuicPacket::NewFecPacket(
+ NULL, 0, false, PACKET_8BYTE_GUID, false, PACKET_6BYTE_SEQUENCE_NUMBER));
+ SerializedPacket serialized_packet(1u, PACKET_6BYTE_SEQUENCE_NUMBER,
+ packet.get(), 7u, NULL);
+
+ manager_.OnSerializedPacket(serialized_packet);
+ // FEC packets do not count as "unacked".
+ EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(2u));
+ EXPECT_EQ(2u, manager_.GetLeastUnackedSentPacket());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketDiscardUnacked) {
+ scoped_ptr<QuicPacket> packet(QuicPacket::NewDataPacket(
+ NULL, 0, false, PACKET_8BYTE_GUID, false, PACKET_6BYTE_SEQUENCE_NUMBER));
+ SerializedPacket serialized_packet(1u, PACKET_6BYTE_SEQUENCE_NUMBER,
+ packet.get(), 7u,
+ new RetransmittableFrames());
+
+ manager_.OnSerializedPacket(serialized_packet);
+ manager_.DiscardPacket(1u);
+ EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(2u));
+ EXPECT_EQ(2u, manager_.GetLeastUnackedSentPacket());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
index da980ca..9389af4 100644
--- a/net/quic/quic_session.cc
+++ b/net/quic/quic_session.cc
@@ -33,12 +33,8 @@ class VisitorShim : public QuicConnectionVisitorInterface {
public:
explicit VisitorShim(QuicSession* session) : session_(session) {}
- virtual bool OnPacket(const IPEndPoint& self_address,
- const IPEndPoint& peer_address,
- const QuicPacketHeader& header,
- const vector<QuicStreamFrame>& frame) OVERRIDE {
- bool accepted = session_->OnPacket(self_address, peer_address, header,
- frame);
+ virtual bool OnStreamFrames(const vector<QuicStreamFrame>& frames) OVERRIDE {
+ bool accepted = session_->OnStreamFrames(frames);
session_->PostProcessAfterData();
return accepted;
}
@@ -52,11 +48,6 @@ class VisitorShim : public QuicConnectionVisitorInterface {
session_->PostProcessAfterData();
}
- virtual void OnAck(const SequenceNumberSet& acked_packets) OVERRIDE {
- session_->OnAck(acked_packets);
- session_->PostProcessAfterData();
- }
-
virtual bool OnCanWrite() OVERRIDE {
bool rc = session_->OnCanWrite();
session_->PostProcessAfterData();
@@ -73,6 +64,10 @@ class VisitorShim : public QuicConnectionVisitorInterface {
// The session will go away, so don't bother with cleanup.
}
+ virtual bool HasPendingHandshake() const OVERRIDE {
+ return session_->HasPendingHandshake();
+ }
+
private:
QuicSession* session_;
};
@@ -89,7 +84,8 @@ QuicSession::QuicSession(QuicConnection* connection,
largest_peer_created_stream_id_(0),
error_(QUIC_NO_ERROR),
goaway_received_(false),
- goaway_sent_(false) {
+ goaway_sent_(false),
+ has_pending_handshake_(false) {
connection_->set_visitor(visitor_shim_.get());
connection_->SetIdleNetworkTimeout(config_.idle_connection_state_lifetime());
@@ -105,16 +101,7 @@ QuicSession::~QuicSession() {
STLDeleteValues(&stream_map_);
}
-bool QuicSession::OnPacket(const IPEndPoint& self_address,
- const IPEndPoint& peer_address,
- const QuicPacketHeader& header,
- const vector<QuicStreamFrame>& frames) {
- if (header.public_header.guid != connection()->guid()) {
- DLOG(INFO) << ENDPOINT << "Got packet header for invalid GUID: "
- << header.public_header.guid;
- return false;
- }
-
+bool QuicSession::OnStreamFrames(const vector<QuicStreamFrame>& frames) {
for (size_t i = 0; i < frames.size(); ++i) {
// TODO(rch) deal with the error case of stream id 0
if (IsClosedStream(frames[i].stream_id)) {
@@ -222,11 +209,17 @@ bool QuicSession::OnCanWrite() {
while (!connection_->HasQueuedData() &&
remaining_writes > 0) {
DCHECK(write_blocked_streams_.HasWriteBlockedStreams());
- ReliableQuicStream* stream = NULL;
int index = write_blocked_streams_.GetHighestPriorityWriteBlockedList();
- if (index != -1) {
- stream = GetStream(write_blocked_streams_.PopFront(index));
+ if (index == -1) {
+ LOG(DFATAL) << "WriteBlockedStream is missing";
+ connection_->CloseConnection(QUIC_INTERNAL_ERROR, false);
+ return true; // We have no write blocked streams.
+ }
+ QuicStreamId stream_id = write_blocked_streams_.PopFront(index);
+ if (stream_id == kCryptoStreamId) {
+ has_pending_handshake_ = false; // We just popped it.
}
+ ReliableQuicStream* stream = GetStream(stream_id);
if (stream != NULL) {
// If the stream can't write all bytes, it'll re-add itself to the blocked
// list.
@@ -238,12 +231,16 @@ bool QuicSession::OnCanWrite() {
return !write_blocked_streams_.HasWriteBlockedStreams();
}
+bool QuicSession::HasPendingHandshake() const {
+ return has_pending_handshake_;
+}
+
QuicConsumedData QuicSession::WritevData(QuicStreamId id,
const struct iovec* iov,
- int count,
+ int iov_count,
QuicStreamOffset offset,
bool fin) {
- return connection_->SendvStreamData(id, iov, count, offset, fin);
+ return connection_->SendvStreamData(id, iov, iov_count, offset, fin);
}
void QuicSession::SendRstStream(QuicStreamId id,
@@ -381,7 +378,7 @@ QuicConfig* QuicSession::config() {
void QuicSession::ActivateStream(ReliableQuicStream* stream) {
DLOG(INFO) << ENDPOINT << "num_streams: " << stream_map_.size()
<< ". activating " << stream->id();
- DCHECK(stream_map_.count(stream->id()) == 0);
+ DCHECK_EQ(stream_map_.count(stream->id()), 0u);
stream_map_[stream->id()] = stream;
}
@@ -481,6 +478,14 @@ size_t QuicSession::GetNumOpenStreams() const {
}
void QuicSession::MarkWriteBlocked(QuicStreamId id, QuicPriority priority) {
+ if (id == kCryptoStreamId) {
+ DCHECK(!has_pending_handshake_);
+ has_pending_handshake_ = true;
+ // TODO(jar): Be sure to use the highest priority for the crypto stream,
+ // perhaps by adding a "special" priority for it that is higher than
+ // kHighestPriority.
+ priority = kHighestPriority;
+ }
write_blocked_streams_.PushBack(id, priority);
}
diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h
index 9916e58..b58feb2 100644
--- a/net/quic/quic_session.h
+++ b/net/quic/quic_session.h
@@ -59,18 +59,16 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
virtual ~QuicSession();
// QuicConnectionVisitorInterface methods:
- virtual bool OnPacket(const IPEndPoint& self_address,
- const IPEndPoint& peer_address,
- const QuicPacketHeader& header,
- const std::vector<QuicStreamFrame>& frame) OVERRIDE;
+ virtual bool OnStreamFrames(
+ const std::vector<QuicStreamFrame>& frames) OVERRIDE;
virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE;
virtual void OnGoAway(const QuicGoAwayFrame& frame) OVERRIDE;
virtual void ConnectionClose(QuicErrorCode error, bool from_peer) OVERRIDE;
virtual void OnSuccessfulVersionNegotiation(
const QuicVersion& version) OVERRIDE{}
// Not needed for HTTP.
- virtual void OnAck(const SequenceNumberSet& acked_packets) OVERRIDE {}
virtual bool OnCanWrite() OVERRIDE;
+ virtual bool HasPendingHandshake() const OVERRIDE;
// Called by streams when they want to write data to the peer.
// Returns a pair with the number of bytes consumed from data, and a boolean
@@ -79,7 +77,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
// if the socket was unexpectedly blocked.
virtual QuicConsumedData WritevData(QuicStreamId id,
const struct iovec* iov,
- int count,
+ int iov_count,
QuicStreamOffset offset,
bool fin);
@@ -286,6 +284,9 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
// Whether a GoAway has been sent.
bool goaway_sent_;
+ // Indicate if there is pending data for the crypto stream.
+ bool has_pending_handshake_;
+
DISALLOW_COPY_AND_ASSIGN(QuicSession);
};
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
index 9d229a1..e90b20f 100644
--- a/net/quic/quic_session_test.cc
+++ b/net/quic/quic_session_test.cc
@@ -23,12 +23,15 @@ using std::set;
using std::vector;
using testing::_;
using testing::InSequence;
+using testing::InvokeWithoutArgs;
using testing::StrictMock;
namespace net {
namespace test {
namespace {
+const QuicPriority kSomeMiddlePriority = 2;
+
class TestCryptoStream : public QuicCryptoStream {
public:
explicit TestCryptoStream(QuicSession* session)
@@ -47,6 +50,8 @@ class TestCryptoStream : public QuicCryptoStream {
EXPECT_EQ(QUIC_NO_ERROR, error);
session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
}
+
+ MOCK_METHOD0(OnCanWrite, void());
};
class TestStream : public ReliableQuicStream {
@@ -64,6 +69,23 @@ class TestStream : public ReliableQuicStream {
MOCK_METHOD0(OnCanWrite, void());
};
+// Poor man's functor for use as callback in a mock.
+class StreamBlocker {
+ public:
+ StreamBlocker(QuicSession* session, QuicStreamId stream_id)
+ : session_(session),
+ stream_id_(stream_id) {
+ }
+
+ void MarkWriteBlocked() {
+ session_->MarkWriteBlocked(stream_id_, kSomeMiddlePriority);
+ }
+
+ private:
+ QuicSession* const session_;
+ const QuicStreamId stream_id_;
+};
+
class TestSession : public QuicSession {
public:
TestSession(QuicConnection* connection, bool is_server)
@@ -71,7 +93,7 @@ class TestSession : public QuicSession {
crypto_stream_(this) {
}
- virtual QuicCryptoStream* GetCryptoStream() OVERRIDE {
+ virtual TestCryptoStream* GetCryptoStream() OVERRIDE {
return &crypto_stream_;
}
@@ -93,11 +115,6 @@ class TestSession : public QuicSession {
return QuicSession::GetIncomingReliableStream(stream_id);
}
- // Helper method for gmock
- void MarkTwoWriteBlocked() {
- this->MarkWriteBlocked(2, 0);
- }
-
TestCryptoStream crypto_stream_;
};
@@ -240,29 +257,77 @@ TEST_F(QuicSessionTest, OnCanWrite) {
TestStream* stream4 = session_.CreateOutgoingReliableStream();
TestStream* stream6 = session_.CreateOutgoingReliableStream();
- session_.MarkWriteBlocked(2, 0);
- session_.MarkWriteBlocked(6, 0);
- session_.MarkWriteBlocked(4, 0);
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
InSequence s;
+ StreamBlocker stream2_blocker(&session_, stream2->id());
EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(
// Reregister, to test the loop limit.
- testing::InvokeWithoutArgs(&session_, &TestSession::MarkTwoWriteBlocked));
+ InvokeWithoutArgs(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
EXPECT_CALL(*stream6, OnCanWrite());
EXPECT_CALL(*stream4, OnCanWrite());
EXPECT_FALSE(session_.OnCanWrite());
}
+TEST_F(QuicSessionTest, BufferedHandshake) {
+ EXPECT_FALSE(session_.HasPendingHandshake()); // Default value.
+
+ // Test that blocking other streams does not change our status.
+ TestStream* stream2 = session_.CreateOutgoingReliableStream();
+ StreamBlocker stream2_blocker(&session_, stream2->id());
+ stream2_blocker.MarkWriteBlocked();
+ EXPECT_FALSE(session_.HasPendingHandshake());
+
+ TestStream* stream3 = session_.CreateOutgoingReliableStream();
+ StreamBlocker stream3_blocker(&session_, stream3->id());
+ stream3_blocker.MarkWriteBlocked();
+ EXPECT_FALSE(session_.HasPendingHandshake());
+
+ // Blocking (due to buffering of) the Crypto stream is detected.
+ session_.MarkWriteBlocked(kCryptoStreamId, kSomeMiddlePriority);
+ EXPECT_TRUE(session_.HasPendingHandshake());
+
+ TestStream* stream4 = session_.CreateOutgoingReliableStream();
+ StreamBlocker stream4_blocker(&session_, stream4->id());
+ stream4_blocker.MarkWriteBlocked();
+ EXPECT_TRUE(session_.HasPendingHandshake());
+
+ InSequence s;
+ // Force most streams to re-register, which is common scenario when we block
+ // the Crypto stream, and only the crypto stream can "really" write.
+
+ // Due to prioritization, we *should* be asked to write the crypto stream
+ // first.
+ // Don't re-register the crypto stream (which signals complete writing).
+ TestCryptoStream* crypto_stream = session_.GetCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite());
+
+ // Re-register all other streams, to show they weren't able to proceed.
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(
+ InvokeWithoutArgs(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
+
+ EXPECT_CALL(*stream3, OnCanWrite()).WillOnce(
+ InvokeWithoutArgs(&stream3_blocker, &StreamBlocker::MarkWriteBlocked));
+
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(
+ InvokeWithoutArgs(&stream4_blocker, &StreamBlocker::MarkWriteBlocked));
+
+ EXPECT_FALSE(session_.OnCanWrite());
+ EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote.
+}
+
TEST_F(QuicSessionTest, OnCanWriteWithClosedStream) {
TestStream* stream2 = session_.CreateOutgoingReliableStream();
TestStream* stream4 = session_.CreateOutgoingReliableStream();
- session_.CreateOutgoingReliableStream(); // stream 6
+ TestStream* stream6 = session_.CreateOutgoingReliableStream();
- session_.MarkWriteBlocked(2, 0);
- session_.MarkWriteBlocked(6, 0);
- session_.MarkWriteBlocked(4, 0);
- CloseStream(6);
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
+ CloseStream(stream6->id());
InSequence s;
EXPECT_CALL(*stream2, OnCanWrite());
@@ -293,11 +358,11 @@ TEST_F(QuicSessionTest, OutOfOrderHeaders) {
// Process the second frame first. This will cause the headers to
// be queued up and processed after the first frame is processed.
frames.push_back(frame2);
- session_.OnPacket(IPEndPoint(), IPEndPoint(), header, frames);
+ session_.OnStreamFrames(frames);
// Process the first frame, and un-cork the buffered headers.
frames[0] = frame1;
- session_.OnPacket(IPEndPoint(), IPEndPoint(), header, frames);
+ session_.OnStreamFrames(frames);
// Ensure that the streams actually close and we don't DCHECK.
connection_->CloseConnection(QUIC_CONNECTION_TIMED_OUT, true);
@@ -356,7 +421,8 @@ TEST_F(QuicSessionTest, ZombieStream) {
// be queued up and processed after the first frame is processed.
frames.push_back(frame1);
EXPECT_FALSE(stream3->headers_decompressed());
- session.OnPacket(IPEndPoint(), IPEndPoint(), header, frames);
+
+ session.OnStreamFrames(frames);
EXPECT_EQ(1u, session.GetNumOpenStreams());
EXPECT_TRUE(connection->connected());
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
index 7a44713..41cb457 100644
--- a/net/quic/reliable_quic_stream.cc
+++ b/net/quic/reliable_quic_stream.cc
@@ -248,7 +248,7 @@ QuicConsumedData ReliableQuicStream::WriteDataInternal(
}
QuicConsumedData ReliableQuicStream::WritevDataInternal(const struct iovec* iov,
- int count,
+ int iov_count,
bool fin) {
if (write_side_closed_) {
DLOG(ERROR) << "Attempt to write when the write side is closed";
@@ -256,11 +256,11 @@ QuicConsumedData ReliableQuicStream::WritevDataInternal(const struct iovec* iov,
}
size_t write_length = 0u;
- for (int i = 0; i < count; ++i) {
+ for (int i = 0; i < iov_count; ++i) {
write_length += iov[i].iov_len;
}
QuicConsumedData consumed_data =
- session()->WritevData(id(), iov, count, stream_bytes_written_, fin);
+ session()->WritevData(id(), iov, iov_count, stream_bytes_written_, fin);
stream_bytes_written_ += consumed_data.bytes_consumed;
if (consumed_data.bytes_consumed == write_length) {
if (fin && consumed_data.fin_consumed) {
@@ -293,10 +293,8 @@ void ReliableQuicStream::CloseReadSide() {
}
uint32 ReliableQuicStream::ProcessRawData(const char* data, uint32 data_len) {
+ DCHECK_NE(0u, data_len);
if (id() == kCryptoStreamId) {
- if (data_len == 0) {
- return 0;
- }
// The crypto stream does not use compression.
return ProcessData(data, data_len);
}
diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h
index 4fe4134..807882ca 100644
--- a/net/quic/reliable_quic_stream.h
+++ b/net/quic/reliable_quic_stream.h
@@ -167,7 +167,7 @@ class NET_EXPORT_PRIVATE ReliableQuicStream : public
// as the connection will consume.
// Returns the number of bytes consumed by the connection.
QuicConsumedData WritevDataInternal(const struct iovec* iov,
- int count,
+ int iov_count,
bool fin);
private:
diff --git a/net/quic/test_tools/quic_connection_peer.cc b/net/quic/test_tools/quic_connection_peer.cc
index c168313..5d715db 100644
--- a/net/quic/test_tools/quic_connection_peer.cc
+++ b/net/quic/test_tools/quic_connection_peer.cc
@@ -70,17 +70,15 @@ QuicTime::Delta QuicConnectionPeer::GetNetworkTimeout(
bool QuicConnectionPeer::IsSavedForRetransmission(
QuicConnection* connection,
QuicPacketSequenceNumber sequence_number) {
- return ContainsKey(connection->retransmission_map_, sequence_number);
+ return connection->sent_packet_manager_.IsUnacked(sequence_number);
}
// static
size_t QuicConnectionPeer::GetRetransmissionCount(
QuicConnection* connection,
QuicPacketSequenceNumber sequence_number) {
- QuicConnection::RetransmissionMap::iterator it =
- connection->retransmission_map_.find(sequence_number);
- DCHECK(connection->retransmission_map_.end() != it);
- return it->second.number_retransmissions;
+ return connection->sent_packet_manager_.GetRetransmissionCount(
+ sequence_number);
}
// static
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index 750f9c7..8ba46b8 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -243,8 +243,9 @@ bool PacketSavingConnection::SendOrQueuePacket(
EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
- QuicPacketEntropyHash entropy_hash,
- HasRetransmittableData retransmittable) {
+ QuicPacketEntropyHash /* entropy_hash */,
+ HasRetransmittableData /* retransmittable */,
+ Force /* forced */) {
packets_.push_back(packet);
QuicEncryptedPacket* encrypted =
framer_.EncryptPacket(level, sequence_number, *packet);
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index 38aa852..64a9d30 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -175,15 +175,12 @@ class MockConnectionVisitor : public QuicConnectionVisitorInterface {
MockConnectionVisitor();
virtual ~MockConnectionVisitor();
- MOCK_METHOD4(OnPacket, bool(const IPEndPoint& self_address,
- const IPEndPoint& peer_address,
- const QuicPacketHeader& header,
- const std::vector<QuicStreamFrame>& frame));
+ MOCK_METHOD1(OnStreamFrames, bool(const std::vector<QuicStreamFrame>& frame));
MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame));
MOCK_METHOD1(OnGoAway, void(const QuicGoAwayFrame& frame));
MOCK_METHOD2(ConnectionClose, void(QuicErrorCode error, bool from_peer));
- MOCK_METHOD1(OnAck, void(const SequenceNumberSet& acked_packets));
MOCK_METHOD0(OnCanWrite, bool());
+ MOCK_CONST_METHOD0(HasPendingHandshake, bool());
MOCK_METHOD1(OnSuccessfulVersionNegotiation,
void(const QuicVersion& version));
@@ -203,7 +200,7 @@ class MockHelper : public QuicConnectionHelperInterface {
MOCK_METHOD2(WritePacketToWire, int(const QuicEncryptedPacket& packet,
int* error));
MOCK_METHOD0(IsWriteBlockedDataBuffered, bool());
- MOCK_METHOD1(IsWriteBlocked, bool(int));
+ MOCK_METHOD1(IsWriteBlocked, bool(int stream_id));
virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate);
private:
@@ -237,6 +234,7 @@ class MockConnection : public QuicConnection {
QuicStreamId last_good_stream_id,
const string& reason));
MOCK_METHOD0(OnCanWrite, bool());
+ MOCK_CONST_METHOD0(HasPendingHandshake, bool());
void ProcessUdpPacketInternal(const IPEndPoint& self_address,
const IPEndPoint& peer_address,
@@ -264,7 +262,8 @@ class PacketSavingConnection : public MockConnection {
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
QuicPacketEntropyHash entropy_hash,
- HasRetransmittableData has_retransmittable_data) OVERRIDE;
+ HasRetransmittableData has_retransmittable_data,
+ Force forced) OVERRIDE;
std::vector<QuicPacket*> packets_;
std::vector<QuicEncryptedPacket*> encrypted_packets_;
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index 9ad6649..7b61be6 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -423,7 +423,29 @@ TEST_P(EndToEndTest, PostMissingBytes) {
EXPECT_EQ(500u, client_->response_headers()->parsed_response_code());
}
-TEST_P(EndToEndTest, LargePost) {
+TEST_P(EndToEndTest, LargePostNoPacketLoss) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+
+ // 1 Mb body.
+ string body;
+ GenerateBody(&body, 1024 * 1024);
+
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.AddBody(body, true);
+
+ EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
+}
+
+TEST_P(EndToEndTest, LargePostWithPacketLoss) {
// TODO(rtenneti): Delete this when NSS is supported.
if (!Aes128Gcm12Encrypter::IsSupported()) {
LOG(INFO) << "AES GCM not supported. Test skipped.";
@@ -439,8 +461,9 @@ TEST_P(EndToEndTest, LargePost) {
client_->client()->WaitForCryptoHandshakeConfirmed();
// FLAGS_fake_packet_loss_percentage = 30;
+ // 10 Kb body.
string body;
- GenerateBody(&body, 10240);
+ GenerateBody(&body, 1024 * 10);
HTTPMessage request(HttpConstants::HTTP_1_1,
HttpConstants::POST, "/foo");
@@ -563,7 +586,6 @@ TEST_P(EndToEndTest, DISABLED_MultipleTermination) {
}
ASSERT_TRUE(Initialize());
- scoped_ptr<QuicTestClient> client2(CreateQuicClient());
HTTPMessage request(HttpConstants::HTTP_1_1,
HttpConstants::POST, "/foo");
diff --git a/net/tools/quic/quic_epoll_connection_helper_test.cc b/net/tools/quic/quic_epoll_connection_helper_test.cc
index 9f0321a..636d98f 100644
--- a/net/tools/quic/quic_epoll_connection_helper_test.cc
+++ b/net/tools/quic/quic_epoll_connection_helper_test.cc
@@ -8,6 +8,7 @@
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_connection.h"
#include "net/quic/quic_framer.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
@@ -21,6 +22,7 @@ using net::test::QuicConnectionPeer;
using net::test::MockConnectionVisitor;
using net::tools::test::MockEpollServer;
using testing::_;
+using testing::AnyNumber;
using testing::Return;
namespace net {
@@ -154,7 +156,10 @@ TEST_F(QuicEpollConnectionHelperTest, DISABLED_TestRetransmission) {
TEST_F(QuicEpollConnectionHelperTest, InitialTimeout) {
EXPECT_TRUE(connection_.connected());
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA));
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillOnce(
+ Return(QuicTime::Delta::FromMicroseconds(1)));
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer));
epoll_server_.WaitForEventsAndExecuteCallbacks();
EXPECT_FALSE(connection_.connected());
@@ -182,8 +187,10 @@ TEST_F(QuicEpollConnectionHelperTest, TimeoutAfterSend) {
// This time, we should time out.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer));
- EXPECT_CALL(*send_algorithm_,
- SentPacket(_, 2, _, NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA));
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillOnce(
+ Return(QuicTime::Delta::FromMicroseconds(1)));
epoll_server_.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000 + 5000,
epoll_server_.NowInUsec());
@@ -199,7 +206,8 @@ TEST_F(QuicEpollConnectionHelperTest, SendSchedulerDelayThenSend) {
*send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
Return(QuicTime::Delta::FromMicroseconds(1)));
connection_.SendOrQueuePacket(ENCRYPTION_NONE, 1, packet, 0,
- HAS_RETRANSMITTABLE_DATA);
+ HAS_RETRANSMITTABLE_DATA,
+ QuicConnection::NO_FORCE);
EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION,
_));
EXPECT_EQ(1u, connection_.NumQueuedPackets());
@@ -210,6 +218,7 @@ TEST_F(QuicEpollConnectionHelperTest, SendSchedulerDelayThenSend) {
TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
Return(QuicTime::Delta::Zero()));
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(Return(true));
+ EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
epoll_server_.AdvanceByAndCallCallbacks(1);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
diff --git a/net/tools/quic/quic_reliable_server_stream_test.cc b/net/tools/quic/quic_reliable_server_stream_test.cc
index 8e4ae4c..53533a3 100644
--- a/net/tools/quic/quic_reliable_server_stream_test.cc
+++ b/net/tools/quic/quic_reliable_server_stream_test.cc
@@ -121,10 +121,13 @@ class QuicReliableServerStreamTest : public ::testing::Test {
string body_;
};
-QuicConsumedData ConsumeAllData(QuicStreamId id, const struct iovec* iov,
- int count, QuicStreamOffset offset, bool fin) {
+QuicConsumedData ConsumeAllData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ QuicStreamOffset offset,
+ bool fin) {
ssize_t consumed_length = 0;
- for (int i = 0; i < count; ++i) {
+ for (int i = 0; i < iov_count; ++i) {
consumed_length += iov[i].iov_len;
}
return QuicConsumedData(consumed_length, fin);
diff --git a/net/tools/quic/quic_server_session_test.cc b/net/tools/quic/quic_server_session_test.cc
index ef3674b..a6f94ab 100644
--- a/net/tools/quic/quic_server_session_test.cc
+++ b/net/tools/quic/quic_server_session_test.cc
@@ -130,8 +130,7 @@ TEST_F(QuicServerSessionTest, CloseStreamDueToReset) {
QuicStreamFrame data1(3, false, 0, "HT");
vector<QuicStreamFrame> frames;
frames.push_back(data1);
- EXPECT_TRUE(visitor_->OnPacket(IPEndPoint(), IPEndPoint(),
- header, frames));
+ EXPECT_TRUE(visitor_->OnStreamFrames(frames));
EXPECT_EQ(1u, session_->GetNumOpenStreams());
// Pretend we got full headers, so we won't trigger the 'unrecoverable
@@ -144,8 +143,7 @@ TEST_F(QuicServerSessionTest, CloseStreamDueToReset) {
EXPECT_EQ(0u, session_->GetNumOpenStreams());
// Send the same two bytes of payload in a new packet.
- EXPECT_TRUE(visitor_->OnPacket(IPEndPoint(), IPEndPoint(),
- header, frames));
+ EXPECT_TRUE(visitor_->OnStreamFrames(frames));
// The stream should not be re-opened.
EXPECT_EQ(0u, session_->GetNumOpenStreams());
@@ -171,8 +169,7 @@ TEST_F(QuicServerSessionTest, NeverOpenStreamDueToReset) {
// unrecoverable compression context state.
EXPECT_CALL(*connection_, SendConnectionClose(
QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED));
- EXPECT_FALSE(visitor_->OnPacket(IPEndPoint(), IPEndPoint(),
- header, frames));
+ EXPECT_FALSE(visitor_->OnStreamFrames(frames));
// The stream should never be opened, now that the reset is received.
EXPECT_EQ(0u, session_->GetNumOpenStreams());
@@ -193,8 +190,7 @@ TEST_F(QuicServerSessionTest, GoOverPrematureClosedStreamLimit) {
EXPECT_CALL(*connection_, SendConnectionClose(
QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED));
- EXPECT_FALSE(visitor_->OnPacket(IPEndPoint(), IPEndPoint(),
- header, frames));
+ EXPECT_FALSE(visitor_->OnStreamFrames(frames));
}
TEST_F(QuicServerSessionTest, AcceptClosedStream) {
@@ -206,8 +202,7 @@ TEST_F(QuicServerSessionTest, AcceptClosedStream) {
// Send (empty) compressed headers followed by two bytes of data.
frames.push_back(QuicStreamFrame(3, false, 0, "\1\0\0\0\0\0\0\0HT"));
frames.push_back(QuicStreamFrame(5, false, 0, "\2\0\0\0\0\0\0\0HT"));
- EXPECT_TRUE(visitor_->OnPacket(IPEndPoint(), IPEndPoint(),
- header, frames));
+ EXPECT_TRUE(visitor_->OnStreamFrames(frames));
// Pretend we got full headers, so we won't trigger the 'unercoverable
// compression context' state.
@@ -223,8 +218,7 @@ TEST_F(QuicServerSessionTest, AcceptClosedStream) {
frames.clear();
frames.push_back(QuicStreamFrame(3, false, 2, "TP"));
frames.push_back(QuicStreamFrame(5, false, 2, "TP"));
- EXPECT_TRUE(visitor_->OnPacket(IPEndPoint(), IPEndPoint(),
- header, frames));
+ EXPECT_TRUE(visitor_->OnStreamFrames(frames));
}
TEST_F(QuicServerSessionTest, MaxNumConnections) {