diff options
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) { |