summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-25 05:27:03 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-25 05:27:03 +0000
commit9db4439170f43fb458827d4731ac24211ddb6ecf (patch)
tree94d350faaa0024fd030fdd9c0d4f41bff86d4139
parent5729f4ac33ddeb87b632c858b4a502f43392c938 (diff)
downloadchromium_src-9db4439170f43fb458827d4731ac24211ddb6ecf.zip
chromium_src-9db4439170f43fb458827d4731ac24211ddb6ecf.tar.gz
chromium_src-9db4439170f43fb458827d4731ac24211ddb6ecf.tar.bz2
Land recent QUIC changes.
Reviving entropy flag of missing packet. Merge internal change: 42912121 Fix bug in QuicConnection::OnCanWrite in which the packet generator never flushed the packets when the visitor did not write all bytes. Merge internal change: 42919513 Clarify the SendAlarm behavior of the TestHelper in QuicConnectionTests. In particular, it is important that if we set a send alarm for a delay of Zero, that IsSendAlarmSet() returns true. Merge internal change: 42908913 Fix bug in SimpleQuicFramerVisitor which did not make a copy of StreamFrame data. Merge internal change: 42880797 Use the QuicPacketGenerator in QuicConnection. Merge internal change: 42861764 Add new files Create a new QuicPacketGenerator class to encapsulate the write side operation of a QuicConnection. It provides methods for enqueing control frames, consuming stream data, and sending acks and feedback. It does just-in-time serialization. Also adds a new test-only simple framer which provides an easy mechanism for parsing packets to verify they contain correct contents. Merge internal change: 42813986 Add missing files Implementing logic for checking entropy of received packets and setting entropy on outgoing packets. Merge internal change: 42715914 Fixing a bug where when QuicPacketCreator queues a stream frame, the underlying string is not immediately owned, which can cause issues if SendStreamData doesn't always flush packets before exiting. Merge internal change: 42649091 Add the SetKey, SetNoncePrefix, GetKeySize, and GetNoncePrefixSize methods to the QuicEncrypter and QuicDecrypter interfaces. Specify the format of the nonce for AEAD algorithms. Add a |packet_sequence_number| argument to QuicEncrypter::Encrypt and QuicDecrypter::Decrypt. This requires passing the packet sequence number to QuicFramer::EncryptPacket and QuicFramer::DecryptPayload. Merge internal change: 42641062 Move all logic out of ReceivedInfo. Now it is just a plain struct that is used to represent the state of the received packets info. Also, fixes the following bug: i) Packet 7 8 get lost, largest observed packet is 6. ii) Packet 9 arrives with an AckFrame which has least unacked as 9. iii) We clear 7 8 from missing packets. iv) While adding packet 9 we reinsert 7 and 8 as missing packets since largest observed is 6. Merge internal change: 42556974 Make framer return SerializedPacket instead of QuicPacket. Merge internal change: 42551314 Add SendConnectionClosePacket method to QuicConnection Merge internal change: 42474257 Removing offset from the reset stream frame. Merge internal change: 42436913 Fix for when the end of a frame leave too few bytes for the next header Merge internal change: 42435044 Bug fix for packet loss in inter arrival. Added min drift threshold for inter arrival. Merge internal change: 42391696 Add missing file. Use linked_hash_map to store UnackedPackets: simplifies and optimizes the code. Merge internal change: 42381785 Bugfix "Flaky tests due to QuicTime not initialized" Removed DCHECK that due to timing was invalid Merge internal change: 42374053 Fix TSAN test failure. Merge internal change: 42371302 A simple tcp tail drop like implementation. Merge internal change: 42314912 Adding the GoAway Frame. Merge internal change: 42291652 Chromium review fixes. Merge internal change: 42290161 Implement framing for entropy in AckFrame. Merge internal change: 42286308 Make QuicPacketCreator return SerializedPacket struct. Merge internal change: 42240179 Fix for incoming duplicate packet. Avoids NACKing a duplicate. Merge internal change: 42150291 Remove QuicConnectionVisitorInterface::AckedPackets and use the existing SequenceSet typedef instead. Renamed SequenceSet to SequenceNumberSet to be more readable. Merge internal change: 42147690 Made ramp up bitrate in inter arrival aware of the current send rate. Merge internal change: 42118607 Style fix of full_packet. Merge internal change: 42057032 Packing multiple streams together in a single packet from OnCanWrite. Merge internal change: 42054845 Change the representation of public and private flags to explicts bools. Using enums turns out to be problematic because ENUM1 | ENUM2 can not be assigned to EnumType. I discovered this problem in attempting to implement version negotiation. Merge internal change: 42042022 Various small QUIC cleanups after merging to Chrome. Merge internal change: 42041347 Start using QuicClock Now where needed and sending it around instead of querying it multiple times. This CL also fixes the reports of incoming lost packets which had been lost. Merge internal change: 41944659 Fix mock clock. Adding Now function back to QuicClock but now as a more accurate now Merge internal change: 41909929 Rename of function Now to ApproximateNow to allow us to differentiate between the two in the future. Merge internal change: 41909178 TBR=jar@chromium.org R=jar@chromium.org BUG= Review URL: https://chromiumcodereview.appspot.com/12334063 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@184374 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--net/base/linked_hash_map.h210
-rw-r--r--net/net.gyp9
-rw-r--r--net/quic/congestion_control/cubic.cc3
-rw-r--r--net/quic/congestion_control/fix_rate_sender.cc40
-rw-r--r--net/quic/congestion_control/fix_rate_sender.h18
-rw-r--r--net/quic/congestion_control/fix_rate_test.cc61
-rw-r--r--net/quic/congestion_control/hybrid_slow_start.cc4
-rw-r--r--net/quic/congestion_control/leaky_bucket.cc24
-rw-r--r--net/quic/congestion_control/leaky_bucket.h16
-rw-r--r--net/quic/congestion_control/leaky_bucket_test.cc60
-rw-r--r--net/quic/congestion_control/paced_sender.cc45
-rw-r--r--net/quic/congestion_control/paced_sender.h14
-rw-r--r--net/quic/congestion_control/paced_sender_test.cc63
-rw-r--r--net/quic/congestion_control/quic_congestion_manager.cc63
-rw-r--r--net/quic/congestion_control/quic_congestion_manager.h15
-rw-r--r--net/quic/congestion_control/send_algorithm_interface.h12
-rw-r--r--net/quic/congestion_control/tcp_cubic_sender.cc22
-rw-r--r--net/quic/congestion_control/tcp_cubic_sender.h11
-rw-r--r--net/quic/congestion_control/tcp_cubic_sender_test.cc41
-rw-r--r--net/quic/crypto/null_decrypter.cc11
-rw-r--r--net/quic/crypto/null_decrypter.h5
-rw-r--r--net/quic/crypto/null_decrypter_test.cc6
-rw-r--r--net/quic/crypto/null_encrypter.cc23
-rw-r--r--net/quic/crypto/null_encrypter.h11
-rw-r--r--net/quic/crypto/null_encrypter_test.cc2
-rw-r--r--net/quic/crypto/quic_decrypter.h32
-rw-r--r--net/quic/crypto/quic_encrypter.h45
-rw-r--r--net/quic/crypto/quic_random.cc7
-rw-r--r--net/quic/crypto/quic_random.h3
-rw-r--r--net/quic/quic_client_session.cc5
-rw-r--r--net/quic/quic_client_session_test.cc30
-rw-r--r--net/quic/quic_clock.cc5
-rw-r--r--net/quic/quic_clock.h4
-rw-r--r--net/quic/quic_clock_test.cc2
-rw-r--r--net/quic/quic_connection.cc655
-rw-r--r--net/quic/quic_connection.h111
-rw-r--r--net/quic/quic_connection_helper_test.cc89
-rw-r--r--net/quic/quic_connection_logger.cc13
-rw-r--r--net/quic/quic_connection_test.cc773
-rw-r--r--net/quic/quic_fec_group.cc38
-rw-r--r--net/quic/quic_fec_group.h18
-rw-r--r--net/quic/quic_fec_group_test.cc50
-rw-r--r--net/quic/quic_framer.cc260
-rw-r--r--net/quic/quic_framer.h78
-rw-r--r--net/quic/quic_framer_test.cc544
-rw-r--r--net/quic/quic_http_stream_test.cc27
-rw-r--r--net/quic/quic_network_transaction_unittest.cc37
-rw-r--r--net/quic/quic_packet_creator.cc120
-rw-r--r--net/quic/quic_packet_creator.h55
-rw-r--r--net/quic/quic_packet_creator_test.cc79
-rw-r--r--net/quic/quic_packet_entropy_manager.cc145
-rw-r--r--net/quic/quic_packet_entropy_manager.h102
-rw-r--r--net/quic/quic_packet_entropy_manager_test.cc139
-rw-r--r--net/quic/quic_packet_generator.cc203
-rw-r--r--net/quic/quic_packet_generator.h113
-rw-r--r--net/quic/quic_packet_generator_test.cc508
-rw-r--r--net/quic/quic_protocol.cc134
-rw-r--r--net/quic/quic_protocol.h134
-rw-r--r--net/quic/quic_protocol_test.cc45
-rw-r--r--net/quic/quic_session.cc36
-rw-r--r--net/quic/quic_session.h33
-rw-r--r--net/quic/quic_session_test.cc11
-rw-r--r--net/quic/quic_stream_factory_test.cc38
-rw-r--r--net/quic/quic_stream_sequencer.cc21
-rw-r--r--net/quic/quic_stream_sequencer.h5
-rw-r--r--net/quic/quic_stream_sequencer_test.cc6
-rw-r--r--net/quic/quic_time_test.cc4
-rw-r--r--net/quic/quic_utils.cc4
-rw-r--r--net/quic/reliable_quic_stream.cc16
-rw-r--r--net/quic/reliable_quic_stream.h5
-rw-r--r--net/quic/test_tools/mock_clock.cc4
-rw-r--r--net/quic/test_tools/mock_clock.h2
-rw-r--r--net/quic/test_tools/mock_random.cc4
-rw-r--r--net/quic/test_tools/mock_random.h2
-rw-r--r--net/quic/test_tools/quic_connection_peer.cc29
-rw-r--r--net/quic/test_tools/quic_connection_peer.h13
-rw-r--r--net/quic/test_tools/quic_test_utils.cc54
-rw-r--r--net/quic/test_tools/quic_test_utils.h102
-rw-r--r--net/quic/test_tools/reliable_quic_stream_peer.cc7
-rw-r--r--net/quic/test_tools/reliable_quic_stream_peer.h3
-rw-r--r--net/quic/test_tools/simple_quic_framer.cc180
-rw-r--r--net/quic/test_tools/simple_quic_framer.h55
82 files changed, 4526 insertions, 1500 deletions
diff --git a/net/base/linked_hash_map.h b/net/base/linked_hash_map.h
new file mode 100644
index 0000000..5c1f9ef
--- /dev/null
+++ b/net/base/linked_hash_map.h
@@ -0,0 +1,210 @@
+// 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.
+//
+// This is a simplistic insertion-ordered map. It behaves similarly to an STL
+// map, but only implements a small subset of the map's methods. Internally, we
+// just keep a map and a list going in parallel.
+//
+// This class provides no thread safety guarantees, beyond what you would
+// normally see with std::list.
+//
+// Iterators should be stable in the face of mutations, except for an
+// iterator pointing to an element that was just deleted.
+
+#ifndef UTIL_GTL_LINKED_HASH_MAP_H_
+#define UTIL_GTL_LINKED_HASH_MAP_H_
+
+#include <list>
+#include <utility>
+
+#include "base/hash_tables.h"
+#include "base/logging.h"
+
+// This holds a list of pair<Key, Value> items. This list is what gets
+// traversed, and it's iterators from this list that we return from
+// begin/end/find.
+//
+// We also keep a map<Key, list::iterator> for find. Since std::list is a
+// doubly-linked list, the iterators should remain stable.
+template<class Key, class Value>
+class linked_hash_map {
+ private:
+ typedef std::list<std::pair<Key, Value> > ListType;
+ typedef base::hash_map<Key, typename ListType::iterator> MapType;
+
+ public:
+ typedef typename ListType::iterator iterator;
+ typedef typename ListType::reverse_iterator reverse_iterator;
+ typedef typename ListType::const_iterator const_iterator;
+ typedef typename ListType::const_reverse_iterator const_reverse_iterator;
+ typedef typename MapType::key_type key_type;
+ typedef typename ListType::value_type value_type;
+ typedef typename ListType::size_type size_type;
+
+ linked_hash_map() : map_(), list_() {
+ }
+
+ // Returns an iterator to the first (insertion-ordered) element. Like a map,
+ // this can be dereferenced to a pair<Key, Value>.
+ iterator begin() {
+ return list_.begin();
+ }
+ const_iterator begin() const {
+ return list_.begin();
+ }
+
+ // Returns an iterator beyond the last element.
+ iterator end() {
+ return list_.end();
+ }
+ const_iterator end() const {
+ return list_.end();
+ }
+
+ // Returns an iterator to the last (insertion-ordered) element. Like a map,
+ // this can be dereferenced to a pair<Key, Value>.
+ reverse_iterator rbegin() {
+ return list_.rbegin();
+ }
+ const_reverse_iterator rbegin() const {
+ return list_.rbegin();
+ }
+
+ // Returns an iterator beyond the first element.
+ reverse_iterator rend() {
+ return list_.rend();
+ }
+ const_reverse_iterator rend() const {
+ return list_.rend();
+ }
+
+ // Clears the map of all values.
+ void clear() {
+ map_.clear();
+ list_.clear();
+ }
+
+ // Returns true iff the map is empty.
+ bool empty() const {
+ return list_.empty();
+ }
+
+ // Erases values with the provided key. Returns the number of elements
+ // erased. In this implementation, this will be 0 or 1.
+ size_type erase(const Key& key) {
+ typename MapType::iterator found = map_.find(key);
+ if (found == map_.end()) return 0;
+
+ list_.erase(found->second);
+ map_.erase(found);
+
+ return 1;
+ }
+
+ // Erases values with the provided iterator. If the provided iterator is
+ // invalid or there is inconsistency between the map and list, a CHECK() error
+ // will occur.
+ void erase(iterator position) {
+ typename MapType::iterator found = map_.find(position->first);
+ CHECK(found->second == position)
+ << "Inconsisent iterator for map and list, or the iterator is invalid.";
+
+ list_.erase(position);
+ map_.erase(found);
+ }
+
+ // Erases values between first and last, not including last.
+ void erase(iterator first, iterator last) {
+ while (first != last && first != end()) {
+ erase(first++);
+ }
+ }
+
+ // Finds the element with the given key. Returns an iterator to the
+ // value found, or to end() if the value was not found. Like a map, this
+ // iterator points to a pair<Key, Value>.
+ iterator find(const Key& key) {
+ typename MapType::iterator found = map_.find(key);
+ if (found == map_.end()) {
+ return end();
+ }
+ return found->second;
+ }
+
+ const_iterator find(const Key& key) const {
+ typename MapType::const_iterator found = map_.find(key);
+ if (found == map_.end()) {
+ return end();
+ }
+ return found->second;
+ }
+
+ // Returns the bounds of a range that includes all the elements in the
+ // container with a key that compares equal to x.
+ std::pair<iterator, iterator> equal_range(const key_type& key) {
+ 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);
+ }
+
+ std::pair<const_iterator, const_iterator> equal_range(
+ const key_type& key) const {
+ std::pair<typename MapType::const_iterator,
+ typename MapType::const_iterator> eq_range =
+ map_.equal_range(key);
+ const const_iterator& start_iter = eq_range.first != map_.end() ?
+ eq_range.first->second : end();
+ const const_iterator& end_iter = eq_range.second != map_.end() ?
+ eq_range.second->second : end();
+
+ return 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;
+ }
+
+ // Inserts an element into the map
+ std::pair<iterator, bool> insert(const std::pair<Key, Value>& pair) {
+ // First make sure the map doesn't have a key with this value. If it does,
+ // 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);
+
+ // Otherwise, insert into the list first.
+ list_.push_back(pair);
+
+ // Obtain an iterator to the newly added element. We do -- instead of -
+ // since list::iterator doesn't implement operator-().
+ typename ListType::iterator last = list_.end();
+ --last;
+
+ CHECK(map_.insert(make_pair(pair.first, last)).second)
+ << "Map and list are inconsistent";
+
+ return make_pair(last, true);
+ }
+
+ size_type size() const {
+ return list_.size();
+ }
+
+ void swap(linked_hash_map& other) {
+ map_.swap(other.map_);
+ list_.swap(other.list_);
+ }
+
+ private:
+ // The map component, used for speedy lookups
+ MapType map_;
+
+ // The list component, used for maintaining insertion order
+ ListType list_;
+};
+
+#endif // UTIL_GTL_LINKED_HASH_MAP_H_
diff --git a/net/net.gyp b/net/net.gyp
index 2403f96..fe2b734 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -192,6 +192,7 @@
'base/keygen_handler_nss.cc',
'base/keygen_handler_openssl.cc',
'base/keygen_handler_win.cc',
+ 'base/linked_hash_map.h',
'base/load_flags.h',
'base/load_flags_list.h',
'base/load_states.h',
@@ -729,6 +730,10 @@
'quic/quic_http_stream.h',
'quic/quic_packet_creator.cc',
'quic/quic_packet_creator.h',
+ 'quic/quic_packet_entropy_manager.cc',
+ 'quic/quic_packet_entropy_manager.h',
+ 'quic/quic_packet_generator.cc',
+ 'quic/quic_packet_generator.h',
'quic/quic_protocol.cc',
'quic/quic_protocol.h',
'quic/quic_reliable_client_stream.cc',
@@ -1516,6 +1521,8 @@
'quic/test_tools/quic_test_utils.h',
'quic/test_tools/reliable_quic_stream_peer.cc',
'quic/test_tools/reliable_quic_stream_peer.h',
+ 'quic/test_tools/simple_quic_framer.cc',
+ 'quic/test_tools/simple_quic_framer.h',
'quic/test_tools/test_task_runner.cc',
'quic/test_tools/test_task_runner.h',
'quic/quic_bandwidth_test.cc',
@@ -1531,6 +1538,8 @@
'quic/quic_http_stream_test.cc',
'quic/quic_network_transaction_unittest.cc',
'quic/quic_packet_creator_test.cc',
+ 'quic/quic_packet_entropy_manager_test.cc',
+ 'quic/quic_packet_generator_test.cc',
'quic/quic_protocol_test.cc',
'quic/quic_reliable_client_stream_test.cc',
'quic/quic_session_test.cc',
diff --git a/net/quic/congestion_control/cubic.cc b/net/quic/congestion_control/cubic.cc
index c1faeb3..1c41500 100644
--- a/net/quic/congestion_control/cubic.cc
+++ b/net/quic/congestion_control/cubic.cc
@@ -134,12 +134,11 @@ QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck(
QuicTcpCongestionWindow current_congestion_window,
QuicTime::Delta delay_min) {
acked_packets_count_ += 1; // Packets acked.
- QuicTime current_time = clock_->Now();
+ QuicTime current_time = clock_->ApproximateNow();
// Cubic is "independent" of RTT, the update is limited by the time elapsed.
if (last_congestion_window_ == current_congestion_window &&
(current_time.Subtract(last_update_time_) <= MaxCubicTimeInterval())) {
- DCHECK(epoch_.IsInitialized());
return std::max(last_target_congestion_window_,
estimated_tcp_congestion_window_);
}
diff --git a/net/quic/congestion_control/fix_rate_sender.cc b/net/quic/congestion_control/fix_rate_sender.cc
index 2738526..34addfc 100644
--- a/net/quic/congestion_control/fix_rate_sender.cc
+++ b/net/quic/congestion_control/fix_rate_sender.cc
@@ -18,21 +18,23 @@ namespace net {
FixRateSender::FixRateSender(const QuicClock* clock)
: bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)),
- fix_rate_leaky_bucket_(clock, bitrate_),
- paced_sender_(clock, bitrate_),
+ fix_rate_leaky_bucket_(bitrate_),
+ paced_sender_(bitrate_),
data_in_flight_(0) {
DLOG(INFO) << "FixRateSender";
}
void FixRateSender::OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time,
+ QuicBandwidth /*sent_bandwidth*/,
const SentPacketsMap& /*sent_packets*/) {
DCHECK(feedback.type == kFixRate) <<
"Invalid incoming CongestionFeedbackType:" << feedback.type;
if (feedback.type == kFixRate) {
bitrate_ = feedback.fix_rate.bitrate;
- fix_rate_leaky_bucket_.SetDrainingRate(bitrate_);
- paced_sender_.UpdateBandwidthEstimate(bitrate_);
+ fix_rate_leaky_bucket_.SetDrainingRate(feedback_receive_time, bitrate_);
+ paced_sender_.UpdateBandwidthEstimate(feedback_receive_time, bitrate_);
}
// Silently ignore invalid messages in release mode.
}
@@ -44,34 +46,36 @@ void FixRateSender::OnIncomingAck(
data_in_flight_ -= bytes_acked;
}
-void FixRateSender::OnIncomingLoss(int /*number_of_lost_packets*/) {
+void FixRateSender::OnIncomingLoss(QuicTime /*ack_receive_time*/) {
// Ignore losses for fix rate sender.
}
-void FixRateSender::SentPacket(QuicPacketSequenceNumber /*sequence_number*/,
+void FixRateSender::SentPacket(QuicTime sent_time,
+ QuicPacketSequenceNumber /*sequence_number*/,
QuicByteCount bytes,
bool is_retransmission) {
- fix_rate_leaky_bucket_.Add(bytes);
- paced_sender_.SentPacket(bytes);
+ fix_rate_leaky_bucket_.Add(sent_time, bytes);
+ paced_sender_.SentPacket(sent_time, bytes);
if (!is_retransmission) {
data_in_flight_ += bytes;
}
}
-QuicTime::Delta FixRateSender::TimeUntilSend(bool /*is_retransmission*/) {
- if (CongestionWindow() > fix_rate_leaky_bucket_.BytesPending()) {
+QuicTime::Delta FixRateSender::TimeUntilSend(QuicTime now,
+ bool /*is_retransmission*/) {
+ if (CongestionWindow() > fix_rate_leaky_bucket_.BytesPending(now)) {
if (CongestionWindow() <= data_in_flight_) {
// We need an ack before we send more.
return QuicTime::Delta::Infinite();
}
- return paced_sender_.TimeUntilSend(QuicTime::Delta::Zero());
+ return paced_sender_.TimeUntilSend(now, QuicTime::Delta::Zero());
}
- QuicTime::Delta time_remaining = fix_rate_leaky_bucket_.TimeRemaining();
+ QuicTime::Delta time_remaining = fix_rate_leaky_bucket_.TimeRemaining(now);
if (time_remaining.IsZero()) {
// We need an ack before we send more.
return QuicTime::Delta::Infinite();
}
- return paced_sender_.TimeUntilSend(time_remaining);
+ return paced_sender_.TimeUntilSend(now, time_remaining);
}
QuicByteCount FixRateSender::CongestionWindow() {
@@ -81,16 +85,6 @@ QuicByteCount FixRateSender::CongestionWindow() {
return std::max(kMaxPacketSize, window_size_bytes);
}
-QuicByteCount FixRateSender::AvailableCongestionWindow() {
- QuicByteCount congestion_window = CongestionWindow();
- if (data_in_flight_ >= congestion_window) {
- return 0;
- }
- QuicByteCount available_congestion_window = congestion_window -
- data_in_flight_;
- return paced_sender_.AvailableWindow(available_congestion_window);
-}
-
QuicBandwidth FixRateSender::BandwidthEstimate() {
return bitrate_;
}
diff --git a/net/quic/congestion_control/fix_rate_sender.h b/net/quic/congestion_control/fix_rate_sender.h
index 4e864b7..336352f 100644
--- a/net/quic/congestion_control/fix_rate_sender.h
+++ b/net/quic/congestion_control/fix_rate_sender.h
@@ -18,33 +18,31 @@
namespace net {
-namespace test {
-class FixRateSenderPeer;
-} // namespace test
-
class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface {
+
public:
explicit FixRateSender(const QuicClock* clock);
// Start implementation of SendAlgorithmInterface.
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth,
const SentPacketsMap& sent_packets) OVERRIDE;
virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number,
QuicByteCount acked_bytes,
QuicTime::Delta rtt) OVERRIDE;
- virtual void OnIncomingLoss(int number_of_lost_packets) OVERRIDE;
- virtual void SentPacket(QuicPacketSequenceNumber equence_number,
+ virtual void OnIncomingLoss(QuicTime ack_receive_time) OVERRIDE;
+ virtual void SentPacket(QuicTime sent_time,
+ QuicPacketSequenceNumber equence_number,
QuicByteCount bytes,
bool is_retransmission) OVERRIDE;
- virtual QuicTime::Delta TimeUntilSend(bool is_retransmission) OVERRIDE;
+ virtual QuicTime::Delta TimeUntilSend(QuicTime now,
+ bool is_retransmission) OVERRIDE;
virtual QuicBandwidth BandwidthEstimate() OVERRIDE;
// End implementation of SendAlgorithmInterface.
private:
- friend class test::FixRateSenderPeer;
-
- QuicByteCount AvailableCongestionWindow();
QuicByteCount CongestionWindow();
QuicBandwidth bitrate_;
diff --git a/net/quic/congestion_control/fix_rate_test.cc b/net/quic/congestion_control/fix_rate_test.cc
index 2c0770a..a4ff5ba 100644
--- a/net/quic/congestion_control/fix_rate_test.cc
+++ b/net/quic/congestion_control/fix_rate_test.cc
@@ -1,8 +1,6 @@
// 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.
-//
-// Test for FixRate sender and receiver.
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
@@ -10,19 +8,12 @@
#include "net/quic/congestion_control/fix_rate_sender.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/quic_protocol.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace test {
-class FixRateSenderPeer : public FixRateSender {
- public:
- explicit FixRateSenderPeer(const QuicClock* clock)
- : FixRateSender(clock) {
- }
- using FixRateSender::AvailableCongestionWindow;
-};
-
class FixRateReceiverPeer : public FixRateReceiver {
public:
FixRateReceiverPeer()
@@ -37,15 +28,17 @@ class FixRateTest : public ::testing::Test {
protected:
FixRateTest()
: rtt_(QuicTime::Delta::FromMilliseconds(30)),
- sender_(new FixRateSenderPeer(&clock_)),
+ unused_bandwidth_(QuicBandwidth::Zero()),
+ sender_(new FixRateSender(&clock_)),
receiver_(new FixRateReceiverPeer()) {
// Make sure clock does not start at 0.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2));
}
const QuicTime::Delta rtt_;
+ const QuicBandwidth unused_bandwidth_;
MockClock clock_;
- SendAlgorithmInterface::SentPacketsMap not_used_;
- scoped_ptr<FixRateSenderPeer> sender_;
+ SendAlgorithmInterface::SentPacketsMap unused_packet_map_;
+ scoped_ptr<FixRateSender> sender_;
scoped_ptr<FixRateReceiverPeer> receiver_;
};
@@ -63,27 +56,24 @@ TEST_F(FixRateTest, SenderAPI) {
QuicCongestionFeedbackFrame feedback;
feedback.type = kFixRate;
feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(300);
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
+ unused_bandwidth_, unused_packet_map_);
EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond());
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
- EXPECT_EQ(kMaxPacketSize * 2,
- sender_->AvailableCongestionWindow());
- sender_->SentPacket(1, kMaxPacketSize, false);
- EXPECT_EQ(3000u - kMaxPacketSize,
- sender_->AvailableCongestionWindow());
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
- sender_->SentPacket(2, kMaxPacketSize, false);
- sender_->SentPacket(3, 600, false);
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
+ sender_->SentPacket(clock_.Now(), 1, kMaxPacketSize, false);
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
+ sender_->SentPacket(clock_.Now(), 2, kMaxPacketSize, false);
+ sender_->SentPacket(clock_.Now(), 3, 600, false);
EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
- sender_->TimeUntilSend(false));
- EXPECT_EQ(0u, sender_->AvailableCongestionWindow());
+ sender_->TimeUntilSend(clock_.Now(), false));
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2));
- EXPECT_EQ(QuicTime::Delta::Infinite(), sender_->TimeUntilSend(false));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ sender_->TimeUntilSend(clock_.Now(), false));
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
sender_->OnIncomingAck(1, kMaxPacketSize, rtt_);
sender_->OnIncomingAck(2, kMaxPacketSize, rtt_);
sender_->OnIncomingAck(3, 600, rtt_);
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
}
TEST_F(FixRateTest, FixRatePacing) {
@@ -93,17 +83,16 @@ TEST_F(FixRateTest, FixRatePacing) {
QuicCongestionFeedbackFrame feedback;
receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(240));
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
+ unused_bandwidth_, unused_packet_map_);
QuicTime acc_advance_time(QuicTime::Zero());
QuicPacketSequenceNumber sequence_number = 0;
- for (int64 i = 0; i < num_packets; i += 2) {
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
- EXPECT_EQ(kMaxPacketSize * 2,
- sender_->AvailableCongestionWindow());
- sender_->SentPacket(sequence_number++, packet_size, false);
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
- sender_->SentPacket(sequence_number++, packet_size, false);
- QuicTime::Delta advance_time = sender_->TimeUntilSend(false);
+ for (int i = 0; i < num_packets; i += 2) {
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
+ sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, false);
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
+ sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, false);
+ QuicTime::Delta advance_time = sender_->TimeUntilSend(clock_.Now(), false);
clock_.AdvanceTime(advance_time);
sender_->OnIncomingAck(sequence_number - 1, packet_size, rtt_);
sender_->OnIncomingAck(sequence_number - 2, packet_size, rtt_);
diff --git a/net/quic/congestion_control/hybrid_slow_start.cc b/net/quic/congestion_control/hybrid_slow_start.cc
index cbdd1bb..8a42524 100644
--- a/net/quic/congestion_control/hybrid_slow_start.cc
+++ b/net/quic/congestion_control/hybrid_slow_start.cc
@@ -33,7 +33,7 @@ void HybridSlowStart::Restart() {
void HybridSlowStart::Reset(QuicPacketSequenceNumber end_sequence_number) {
DLOG(INFO) << "Reset hybrid slow start @" << end_sequence_number;
- round_start_ = last_time_ = clock_->Now();
+ round_start_ = last_time_ = clock_->ApproximateNow();
end_sequence_number_ = end_sequence_number;
current_rtt_ = QuicTime::Delta::Zero();
sample_count_ = 0;
@@ -52,7 +52,7 @@ void HybridSlowStart::Update(QuicTime::Delta rtt, QuicTime::Delta delay_min) {
if (found_ack_train_ || found_delay_) {
return;
}
- QuicTime current_time = clock_->Now();
+ QuicTime current_time = clock_->ApproximateNow();
// First detection parameter - ack-train detection.
// Since slow start burst out packets we can indirectly estimate the inter-
diff --git a/net/quic/congestion_control/leaky_bucket.cc b/net/quic/congestion_control/leaky_bucket.cc
index 0012ae8..8556810 100644
--- a/net/quic/congestion_control/leaky_bucket.cc
+++ b/net/quic/congestion_control/leaky_bucket.cc
@@ -8,37 +8,35 @@
namespace net {
-LeakyBucket::LeakyBucket(const QuicClock* clock, QuicBandwidth draining_rate)
- : clock_(clock),
- bytes_(0),
+LeakyBucket::LeakyBucket(QuicBandwidth draining_rate)
+ : bytes_(0),
time_last_updated_(QuicTime::Zero()),
draining_rate_(draining_rate) {
}
-void LeakyBucket::SetDrainingRate(QuicBandwidth draining_rate) {
- Update();
+void LeakyBucket::SetDrainingRate(QuicTime now, QuicBandwidth draining_rate) {
+ Update(now);
draining_rate_ = draining_rate;
}
-void LeakyBucket::Add(QuicByteCount bytes) {
- Update();
+void LeakyBucket::Add(QuicTime now, QuicByteCount bytes) {
+ Update(now);
bytes_ += bytes;
}
-QuicTime::Delta LeakyBucket::TimeRemaining() {
- Update();
+QuicTime::Delta LeakyBucket::TimeRemaining(QuicTime now) {
+ Update(now);
return QuicTime::Delta::FromMicroseconds(
(bytes_ * base::Time::kMicrosecondsPerSecond) /
draining_rate_.ToBytesPerSecond());
}
-QuicByteCount LeakyBucket::BytesPending() {
- Update();
+QuicByteCount LeakyBucket::BytesPending(QuicTime now) {
+ Update(now);
return bytes_;
}
-void LeakyBucket::Update() {
- QuicTime now = clock_->Now();
+void LeakyBucket::Update(QuicTime now) {
QuicTime::Delta elapsed_time = now.Subtract(time_last_updated_);
QuicByteCount bytes_cleared = draining_rate_.ToBytesPerPeriod(elapsed_time);
if (bytes_cleared >= bytes_) {
diff --git a/net/quic/congestion_control/leaky_bucket.h b/net/quic/congestion_control/leaky_bucket.h
index be138982..63ef810 100644
--- a/net/quic/congestion_control/leaky_bucket.h
+++ b/net/quic/congestion_control/leaky_bucket.h
@@ -21,25 +21,23 @@ namespace net {
class NET_EXPORT_PRIVATE LeakyBucket {
public:
- // clock is not owned by this class.
- LeakyBucket(const QuicClock* clock, QuicBandwidth draining_rate);
+ explicit LeakyBucket(QuicBandwidth draining_rate);
// Set the rate at which the bytes leave the buffer.
- void SetDrainingRate(QuicBandwidth draining_rate);
+ void SetDrainingRate(QuicTime now, QuicBandwidth draining_rate);
// Add data to the buffer.
- void Add(QuicByteCount bytes);
+ void Add(QuicTime now, QuicByteCount bytes);
- // Time until the buffer is empty in us.
- QuicTime::Delta TimeRemaining();
+ // Time until the buffer is empty.
+ QuicTime::Delta TimeRemaining(QuicTime now);
// Number of bytes in the buffer.
- QuicByteCount BytesPending();
+ QuicByteCount BytesPending(QuicTime now);
private:
- void Update();
+ void Update(QuicTime now);
- const QuicClock* clock_;
QuicByteCount bytes_;
QuicTime time_last_updated_;
QuicBandwidth draining_rate_;
diff --git a/net/quic/congestion_control/leaky_bucket_test.cc b/net/quic/congestion_control/leaky_bucket_test.cc
index 89ab5db..b542370 100644
--- a/net/quic/congestion_control/leaky_bucket_test.cc
+++ b/net/quic/congestion_control/leaky_bucket_test.cc
@@ -13,8 +13,8 @@ namespace test {
class LeakyBucketTest : public ::testing::Test {
protected:
- virtual void SetUp() {
- leaky_bucket_.reset(new LeakyBucket(&clock_, QuicBandwidth::Zero()));
+ void SetUp() {
+ leaky_bucket_.reset(new LeakyBucket(QuicBandwidth::Zero()));
}
MockClock clock_;
scoped_ptr<LeakyBucket> leaky_bucket_;
@@ -22,53 +22,53 @@ class LeakyBucketTest : public ::testing::Test {
TEST_F(LeakyBucketTest, Basic) {
QuicBandwidth draining_rate = QuicBandwidth::FromBytesPerSecond(200000);
- leaky_bucket_->SetDrainingRate(draining_rate);
- leaky_bucket_->Add(2000);
- EXPECT_EQ(2000u, leaky_bucket_->BytesPending());
+ leaky_bucket_->SetDrainingRate(clock_.Now(), draining_rate);
+ leaky_bucket_->Add(clock_.Now(), 2000);
+ EXPECT_EQ(2000u, leaky_bucket_->BytesPending(clock_.Now()));
EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
- leaky_bucket_->TimeRemaining());
+ leaky_bucket_->TimeRemaining(clock_.Now()));
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
- EXPECT_EQ(1000u, leaky_bucket_->BytesPending());
+ EXPECT_EQ(1000u, leaky_bucket_->BytesPending(clock_.Now()));
EXPECT_EQ(QuicTime::Delta::FromMilliseconds(5),
- leaky_bucket_->TimeRemaining());
+ leaky_bucket_->TimeRemaining(clock_.Now()));
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
- EXPECT_EQ(0u, leaky_bucket_->BytesPending());
- EXPECT_TRUE(leaky_bucket_->TimeRemaining().IsZero());
+ EXPECT_EQ(0u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_TRUE(leaky_bucket_->TimeRemaining(clock_.Now()).IsZero());
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
- EXPECT_EQ(0u, leaky_bucket_->BytesPending());
- EXPECT_TRUE(leaky_bucket_->TimeRemaining().IsZero());
- leaky_bucket_->Add(2000);
+ EXPECT_EQ(0u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_TRUE(leaky_bucket_->TimeRemaining(clock_.Now()).IsZero());
+ leaky_bucket_->Add(clock_.Now(), 2000);
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(11));
- EXPECT_EQ(0u, leaky_bucket_->BytesPending());
- EXPECT_TRUE(leaky_bucket_->TimeRemaining().IsZero());
- leaky_bucket_->Add(2000);
+ EXPECT_EQ(0u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_TRUE(leaky_bucket_->TimeRemaining(clock_.Now()).IsZero());
+ leaky_bucket_->Add(clock_.Now(), 2000);
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
- leaky_bucket_->Add(2000);
+ leaky_bucket_->Add(clock_.Now(), 2000);
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
- EXPECT_EQ(2000u, leaky_bucket_->BytesPending());
+ EXPECT_EQ(2000u, leaky_bucket_->BytesPending(clock_.Now()));
EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
- leaky_bucket_->TimeRemaining());
+ leaky_bucket_->TimeRemaining(clock_.Now()));
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
- EXPECT_EQ(0u, leaky_bucket_->BytesPending());
- EXPECT_TRUE(leaky_bucket_->TimeRemaining().IsZero());
+ EXPECT_EQ(0u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_TRUE(leaky_bucket_->TimeRemaining(clock_.Now()).IsZero());
}
TEST_F(LeakyBucketTest, ChangeDrainRate) {
QuicBandwidth draining_rate = QuicBandwidth::FromBytesPerSecond(200000);
- leaky_bucket_->SetDrainingRate(draining_rate);
- leaky_bucket_->Add(2000);
- EXPECT_EQ(2000u, leaky_bucket_->BytesPending());
+ leaky_bucket_->SetDrainingRate(clock_.Now(), draining_rate);
+ leaky_bucket_->Add(clock_.Now(), 2000);
+ EXPECT_EQ(2000u, leaky_bucket_->BytesPending(clock_.Now()));
EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
- leaky_bucket_->TimeRemaining());
+ leaky_bucket_->TimeRemaining(clock_.Now()));
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
- EXPECT_EQ(1000u, leaky_bucket_->BytesPending());
+ EXPECT_EQ(1000u, leaky_bucket_->BytesPending(clock_.Now()));
EXPECT_EQ(QuicTime::Delta::FromMilliseconds(5),
- leaky_bucket_->TimeRemaining());
+ leaky_bucket_->TimeRemaining(clock_.Now()));
draining_rate = draining_rate.Scale(0.5f); // Cut drain rate in half.
- leaky_bucket_->SetDrainingRate(draining_rate);
- EXPECT_EQ(1000u, leaky_bucket_->BytesPending());
+ leaky_bucket_->SetDrainingRate(clock_.Now(), draining_rate);
+ EXPECT_EQ(1000u, leaky_bucket_->BytesPending(clock_.Now()));
EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
- leaky_bucket_->TimeRemaining());
+ leaky_bucket_->TimeRemaining(clock_.Now()));
}
} // namespace test
diff --git a/net/quic/congestion_control/paced_sender.cc b/net/quic/congestion_control/paced_sender.cc
index f44aa33..dd116a8 100644
--- a/net/quic/congestion_control/paced_sender.cc
+++ b/net/quic/congestion_control/paced_sender.cc
@@ -14,21 +14,23 @@ const int64 kMinPacketBurstSize = 2;
// AvailableCongestionWindow.
const int64 kMaxSchedulingDelayUs = 2000;
-PacedSender::PacedSender(const QuicClock* clock, QuicBandwidth estimate)
- : leaky_bucket_(clock, estimate),
+PacedSender::PacedSender(QuicBandwidth estimate)
+ : leaky_bucket_(estimate),
pace_(estimate) {
}
-void PacedSender::UpdateBandwidthEstimate(QuicBandwidth estimate) {
- leaky_bucket_.SetDrainingRate(estimate);
+void PacedSender::UpdateBandwidthEstimate(QuicTime now,
+ QuicBandwidth estimate) {
+ leaky_bucket_.SetDrainingRate(now, estimate);
pace_ = estimate;
}
-void PacedSender::SentPacket(QuicByteCount bytes) {
- leaky_bucket_.Add(bytes);
+void PacedSender::SentPacket(QuicTime now, QuicByteCount bytes) {
+ leaky_bucket_.Add(now, bytes);
}
-QuicTime::Delta PacedSender::TimeUntilSend(QuicTime::Delta time_until_send) {
+QuicTime::Delta PacedSender::TimeUntilSend(QuicTime now,
+ QuicTime::Delta time_until_send) {
if (time_until_send.ToMicroseconds() >= kMaxSchedulingDelayUs) {
return time_until_send;
}
@@ -38,36 +40,11 @@ QuicTime::Delta PacedSender::TimeUntilSend(QuicTime::Delta time_until_send) {
QuicByteCount min_window_size = kMinPacketBurstSize * kMaxPacketSize;
pacing_window = std::max(pacing_window, min_window_size);
- if (pacing_window > leaky_bucket_.BytesPending()) {
+ if (pacing_window > leaky_bucket_.BytesPending(now)) {
// We have not filled our pacing window yet.
return time_until_send;
}
- return leaky_bucket_.TimeRemaining();
-}
-
-QuicByteCount PacedSender::AvailableWindow(
- QuicByteCount available_congestion_window) {
- QuicByteCount accuracy_window = pace_.ToBytesPerPeriod(
- QuicTime::Delta::FromMicroseconds(kMaxSchedulingDelayUs));
- QuicByteCount min_burst_window = kMinPacketBurstSize * kMaxPacketSize;
- DLOG(INFO) << "Available congestion window:" << available_congestion_window
- << " accuracy window:" << accuracy_window
- << " min burst window:" << min_burst_window;
-
- // Should we limit the window to pace the data?
- if (available_congestion_window > min_burst_window &&
- available_congestion_window > accuracy_window) {
- // Max window depends on estimated bandwidth; higher bandwidth => larger
- // burst we also consider our timing accuracy. An accuracy of 1 ms will
- // allow us to send up to 19.2Mbit/s with 2 packets per burst.
- available_congestion_window = std::max(min_burst_window, accuracy_window);
- QuicByteCount bytes_pending = leaky_bucket_.BytesPending();
- if (bytes_pending > available_congestion_window) {
- return 0;
- }
- available_congestion_window -= bytes_pending;
- }
- return available_congestion_window;
+ return leaky_bucket_.TimeRemaining(now);
}
} // namespace net
diff --git a/net/quic/congestion_control/paced_sender.h b/net/quic/congestion_control/paced_sender.h
index af3adaf..5f61b76 100644
--- a/net/quic/congestion_control/paced_sender.h
+++ b/net/quic/congestion_control/paced_sender.h
@@ -11,28 +11,22 @@
#include "net/base/net_export.h"
#include "net/quic/congestion_control/leaky_bucket.h"
#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_clock.h"
#include "net/quic/quic_time.h"
namespace net {
class NET_EXPORT_PRIVATE PacedSender {
public:
- PacedSender(const QuicClock* clock, QuicBandwidth bandwidth_estimate);
+ explicit PacedSender(QuicBandwidth bandwidth_estimate);
// The estimated bandidth from the congestion algorithm changed.
- void UpdateBandwidthEstimate(QuicBandwidth bandwidth_estimate);
+ void UpdateBandwidthEstimate(QuicTime now, QuicBandwidth bandwidth_estimate);
// A packet of size bytes was sent.
- void SentPacket(QuicByteCount bytes);
+ void SentPacket(QuicTime now, QuicByteCount bytes);
// Return time until we can send based on the pacing.
- QuicTime::Delta TimeUntilSend(QuicTime::Delta time_until_send);
-
- // Return the amount of data in bytes we can send based on the pacing.
- // available_congestion_window is the congestion algorithms available
- // congestion window in bytes.
- QuicByteCount AvailableWindow(QuicByteCount available_congestion_window);
+ QuicTime::Delta TimeUntilSend(QuicTime now, QuicTime::Delta time_until_send);
private:
// Helper object to track the rate data can leave the buffer for pacing.
diff --git a/net/quic/congestion_control/paced_sender_test.cc b/net/quic/congestion_control/paced_sender_test.cc
index d2432aa..cc9297f 100644
--- a/net/quic/congestion_control/paced_sender_test.cc
+++ b/net/quic/congestion_control/paced_sender_test.cc
@@ -19,7 +19,7 @@ class PacedSenderTest : public ::testing::Test {
protected:
PacedSenderTest()
: zero_time_(QuicTime::Delta::Zero()),
- paced_sender_(new PacedSender(&clock_,
+ paced_sender_(new PacedSender(
QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS))) {
}
@@ -29,59 +29,48 @@ class PacedSenderTest : public ::testing::Test {
};
TEST_F(PacedSenderTest, Basic) {
- paced_sender_->UpdateBandwidthEstimate(
+ paced_sender_->UpdateBandwidthEstimate(clock_.Now(),
QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS * 10));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero());
- EXPECT_EQ(kMaxPacketSize * 2,
- paced_sender_->AvailableWindow(kMaxPacketSize * 4));
- paced_sender_->SentPacket(kMaxPacketSize);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero());
- paced_sender_->SentPacket(kMaxPacketSize);
+ EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
+ paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize);
+ EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
+ paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize);
EXPECT_EQ(static_cast<int64>(kMaxPacketSize * 2),
- paced_sender_->TimeUntilSend(zero_time_).ToMicroseconds());
- EXPECT_EQ(0u, paced_sender_->AvailableWindow(kMaxPacketSize * 4));
+ paced_sender_->TimeUntilSend(
+ clock_.Now(), zero_time_).ToMicroseconds());
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero());
- EXPECT_EQ(kMaxPacketSize * 2u,
- paced_sender_->AvailableWindow(kMaxPacketSize * 4));
+ EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
}
TEST_F(PacedSenderTest, LowRate) {
- paced_sender_->UpdateBandwidthEstimate(
+ paced_sender_->UpdateBandwidthEstimate(clock_.Now(),
QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero());
- size_t window = paced_sender_->AvailableWindow(kMaxPacketSize * 4);
- EXPECT_EQ(kMaxPacketSize * 2, window);
- paced_sender_->SentPacket(kMaxPacketSize);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero());
- paced_sender_->SentPacket(kMaxPacketSize);
+ EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
+ paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize);
+ EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
+ paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize);
EXPECT_EQ(static_cast<int64>(kMaxPacketSize * 20),
- paced_sender_->TimeUntilSend(zero_time_).ToMicroseconds());
- EXPECT_EQ(0u, paced_sender_->AvailableWindow(kMaxPacketSize * 4));
+ paced_sender_->TimeUntilSend(
+ clock_.Now(), zero_time_).ToMicroseconds());
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero());
- EXPECT_EQ(kMaxPacketSize * 2,
- paced_sender_->AvailableWindow(kMaxPacketSize * 4));
+ EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
}
TEST_F(PacedSenderTest, HighRate) {
QuicBandwidth bandwidth_estimate = QuicBandwidth::FromKBytesPerSecond(
kHundredKBytesPerS * 100);
- paced_sender_->UpdateBandwidthEstimate(bandwidth_estimate);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero());
- EXPECT_EQ(static_cast<uint64>(bandwidth_estimate.ToBytesPerSecond() / 500u),
- paced_sender_->AvailableWindow(kMaxPacketSize * 100));
+ paced_sender_->UpdateBandwidthEstimate(clock_.Now(), bandwidth_estimate);
+ EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
for (int i = 0; i < 16; ++i) {
- paced_sender_->SentPacket(kMaxPacketSize);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero());
+ paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize);
+ EXPECT_TRUE(paced_sender_->TimeUntilSend(
+ clock_.Now(), zero_time_).IsZero());
}
- paced_sender_->SentPacket(kMaxPacketSize);
- EXPECT_EQ(0u, paced_sender_->AvailableWindow(kMaxPacketSize * 100));
- EXPECT_EQ(2040, paced_sender_->TimeUntilSend(zero_time_).ToMicroseconds());
+ paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize);
+ EXPECT_EQ(2040, paced_sender_->TimeUntilSend(
+ clock_.Now(), zero_time_).ToMicroseconds());
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(20400));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero());
- EXPECT_EQ(static_cast<uint64>(bandwidth_estimate.ToBytesPerSecond() / 500u),
- paced_sender_->AvailableWindow(kMaxPacketSize * 100));
+ EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
}
} // namespace test
diff --git a/net/quic/congestion_control/quic_congestion_manager.cc b/net/quic/congestion_control/quic_congestion_manager.cc
index 238b5c7..64e73bb 100644
--- a/net/quic/congestion_control/quic_congestion_manager.cc
+++ b/net/quic/congestion_control/quic_congestion_manager.cc
@@ -12,12 +12,13 @@
#include "net/quic/congestion_control/send_algorithm_interface.h"
namespace {
-const int kBitrateSmoothingPeriodMs = 3000;
+const int kBitrateSmoothingPeriodMs = 1000;
const int kMinBitrateSmoothingPeriodMs = 500;
const int kHistoryPeriodMs = 5000;
const int kDefaultRetransmissionTimeMs = 500;
-const size_t kMaxRetransmissions = 15;
+const size_t kMaxRetransmissions = 10;
+const size_t kTailDropWindowSize = 5;
COMPILE_ASSERT(kHistoryPeriodMs >= kBitrateSmoothingPeriodMs,
history_must_be_longer_or_equal_to_the_smoothing_period);
@@ -33,7 +34,8 @@ QuicCongestionManager::QuicCongestionManager(
CongestionFeedbackType type)
: clock_(clock),
receive_algorithm_(ReceiveAlgorithmInterface::Create(clock, type)),
- send_algorithm_(SendAlgorithmInterface::Create(clock, type)) {
+ send_algorithm_(SendAlgorithmInterface::Create(clock, type)),
+ largest_missing_(0) {
}
QuicCongestionManager::~QuicCongestionManager() {
@@ -41,32 +43,36 @@ QuicCongestionManager::~QuicCongestionManager() {
}
void QuicCongestionManager::SentPacket(QuicPacketSequenceNumber sequence_number,
+ QuicTime sent_time,
QuicByteCount bytes,
bool is_retransmission) {
DCHECK(!ContainsKey(pending_packets_, sequence_number));
- send_algorithm_->SentPacket(sequence_number, bytes, is_retransmission);
+ send_algorithm_->SentPacket(sent_time, sequence_number, bytes,
+ is_retransmission);
packet_history_map_[sequence_number] =
- new class SendAlgorithmInterface::SentPacket(bytes, clock_->Now());
+ new class SendAlgorithmInterface::SentPacket(bytes, sent_time);
pending_packets_[sequence_number] = bytes;
CleanupPacketHistory();
- DLOG(INFO) << "Sent sequence number:" << sequence_number;
}
void QuicCongestionManager::OnIncomingQuicCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& frame) {
- send_algorithm_->OnIncomingQuicCongestionFeedbackFrame(frame,
- packet_history_map_);
+ const QuicCongestionFeedbackFrame& frame, QuicTime feedback_receive_time) {
+ QuicBandwidth sent_bandwidth = SentBandwidth(feedback_receive_time);
+ send_algorithm_->OnIncomingQuicCongestionFeedbackFrame(
+ frame, feedback_receive_time, sent_bandwidth, packet_history_map_);
}
-void QuicCongestionManager::OnIncomingAckFrame(const QuicAckFrame& frame) {
+void QuicCongestionManager::OnIncomingAckFrame(const QuicAckFrame& frame,
+ QuicTime ack_receive_time) {
// We calculate the RTT based on the highest ACKed sequence number, the lower
// sequence numbers will include the ACK aggregation delay.
QuicTime::Delta rtt = QuicTime::Delta::Zero();
SendAlgorithmInterface::SentPacketsMap::iterator history_it =
packet_history_map_.find(frame.received_info.largest_observed);
if (history_it != packet_history_map_.end()) {
- rtt = clock_->Now().Subtract(history_it->second->SendTimestamp());
+ // TODO(pwestin): we need to add the delta to the feedback message.
+ rtt = ack_receive_time.Subtract(history_it->second->SendTimestamp());
}
// We want to.
// * Get all packets lower(including) than largest_observed
@@ -77,23 +83,32 @@ void QuicCongestionManager::OnIncomingAckFrame(const QuicAckFrame& frame) {
it = pending_packets_.begin();
it_upper = pending_packets_.upper_bound(frame.received_info.largest_observed);
+ bool new_packet_loss_reported = false;
while (it != it_upper) {
QuicPacketSequenceNumber sequence_number = it->first;
- if (!frame.received_info.IsAwaitingPacket(sequence_number)) {
+ if (!IsAwaitingPacket(frame.received_info, sequence_number)) {
// Not missing, hence implicitly acked.
send_algorithm_->OnIncomingAck(sequence_number,
it->second,
rtt);
- DLOG(INFO) << "ACKed sequence number:" << sequence_number;
pending_packets_.erase(it++); // Must be incremented post to work.
} else {
+ if (sequence_number > largest_missing_) {
+ // We have a new loss reported.
+ new_packet_loss_reported = true;
+ largest_missing_ = sequence_number;
+ }
++it;
}
}
+ if (new_packet_loss_reported) {
+ send_algorithm_->OnIncomingLoss(ack_receive_time);
+ }
}
-QuicTime::Delta QuicCongestionManager::TimeUntilSend(bool is_retransmission) {
- return send_algorithm_->TimeUntilSend(is_retransmission);
+QuicTime::Delta QuicCongestionManager::TimeUntilSend(QuicTime now,
+ bool is_retransmission) {
+ return send_algorithm_->TimeUntilSend(now, is_retransmission);
}
bool QuicCongestionManager::GenerateCongestionFeedback(
@@ -117,19 +132,24 @@ const QuicTime::Delta QuicCongestionManager::DefaultRetransmissionTime() {
// static
const QuicTime::Delta QuicCongestionManager::GetRetransmissionDelay(
+ size_t unacked_packets_count,
size_t number_retransmissions) {
+ if (unacked_packets_count <= kTailDropWindowSize) {
+ return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs);
+ }
+
return QuicTime::Delta::FromMilliseconds(
kDefaultRetransmissionTimeMs *
(1 << min<size_t>(number_retransmissions, kMaxRetransmissions)));
}
-QuicBandwidth QuicCongestionManager::SentBandwidth() const {
+QuicBandwidth QuicCongestionManager::SentBandwidth(
+ QuicTime feedback_receive_time) const {
const QuicTime::Delta kBitrateSmoothingPeriod =
QuicTime::Delta::FromMilliseconds(kBitrateSmoothingPeriodMs);
const QuicTime::Delta kMinBitrateSmoothingPeriod =
QuicTime::Delta::FromMilliseconds(kMinBitrateSmoothingPeriodMs);
- QuicTime now = clock_->Now();
QuicByteCount sum_bytes_sent = 0;
// Sum packet from new until they are kBitrateSmoothingPeriod old.
@@ -138,7 +158,8 @@ QuicBandwidth QuicCongestionManager::SentBandwidth() const {
QuicTime::Delta max_diff = QuicTime::Delta::Zero();
for (; history_rit != packet_history_map_.rend(); ++history_rit) {
- QuicTime::Delta diff = now.Subtract(history_rit->second->SendTimestamp());
+ QuicTime::Delta diff =
+ feedback_receive_time.Subtract(history_rit->second->SendTimestamp());
if (diff > kBitrateSmoothingPeriod) {
break;
}
@@ -159,16 +180,14 @@ QuicBandwidth QuicCongestionManager::BandwidthEstimate() {
void QuicCongestionManager::CleanupPacketHistory() {
const QuicTime::Delta kHistoryPeriod =
QuicTime::Delta::FromMilliseconds(kHistoryPeriodMs);
- QuicTime Now = clock_->Now();
+ QuicTime now = clock_->ApproximateNow();
SendAlgorithmInterface::SentPacketsMap::iterator history_it =
packet_history_map_.begin();
for (; history_it != packet_history_map_.end(); ++history_it) {
- if (Now.Subtract(history_it->second->SendTimestamp()) <= kHistoryPeriod) {
+ if (now.Subtract(history_it->second->SendTimestamp()) <= kHistoryPeriod) {
return;
}
- DLOG(INFO) << "Clear sequence number:" << history_it->first
- << "from history";
delete history_it->second;
packet_history_map_.erase(history_it);
history_it = packet_history_map_.begin();
diff --git a/net/quic/congestion_control/quic_congestion_manager.h b/net/quic/congestion_control/quic_congestion_manager.h
index 09a9c9c..c8e3a65 100644
--- a/net/quic/congestion_control/quic_congestion_manager.h
+++ b/net/quic/congestion_control/quic_congestion_manager.h
@@ -33,15 +33,18 @@ class QuicCongestionManager {
virtual ~QuicCongestionManager();
// Called when we have received an ack frame from peer.
- virtual void OnIncomingAckFrame(const QuicAckFrame& frame);
+ virtual void OnIncomingAckFrame(const QuicAckFrame& frame,
+ QuicTime ack_receive_time);
// Called when a congestion feedback frame is received from peer.
virtual void OnIncomingQuicCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& frame);
+ const QuicCongestionFeedbackFrame& frame,
+ QuicTime feedback_receive_time);
// Called when we have sent bytes to the peer. This informs the manager both
// the number of bytes sent and if they were retransmitted.
virtual void SentPacket(QuicPacketSequenceNumber sequence_number,
+ QuicTime sent_time,
QuicByteCount bytes,
bool is_retransmission);
@@ -50,7 +53,8 @@ class QuicCongestionManager {
// TimeUntilSend again until we receive an OnIncomingAckFrame event.
// Note 2: Send algorithms may or may not use |retransmit| in their
// calculations.
- virtual QuicTime::Delta TimeUntilSend(bool is_retransmission);
+ virtual QuicTime::Delta TimeUntilSend(QuicTime now,
+ bool is_retransmission);
// Should be called before sending an ACK packet, to decide if we need
// to attach a QuicCongestionFeedbackFrame block.
@@ -73,6 +77,7 @@ class QuicCongestionManager {
const QuicTime::Delta DefaultRetransmissionTime();
const QuicTime::Delta GetRetransmissionDelay(
+ size_t unacked_packets_count,
size_t number_retransmissions);
private:
@@ -80,8 +85,7 @@ class QuicCongestionManager {
friend class test::QuicCongestionManagerPeer;
typedef std::map<QuicPacketSequenceNumber, size_t> PendingPacketsMap;
- // TODO(pwestin): Currently only used for testing. How do we surface this?
- QuicBandwidth SentBandwidth() const;
+ QuicBandwidth SentBandwidth(QuicTime feedback_receive_time) const;
// TODO(pwestin): Currently only used for testing. How do we surface this?
QuicBandwidth BandwidthEstimate();
void CleanupPacketHistory();
@@ -91,6 +95,7 @@ class QuicCongestionManager {
scoped_ptr<SendAlgorithmInterface> send_algorithm_;
SendAlgorithmInterface::SentPacketsMap packet_history_map_;
PendingPacketsMap pending_packets_;
+ QuicPacketSequenceNumber largest_missing_;
DISALLOW_COPY_AND_ASSIGN(QuicCongestionManager);
};
diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h
index efcc172..9478bb4 100644
--- a/net/quic/congestion_control/send_algorithm_interface.h
+++ b/net/quic/congestion_control/send_algorithm_interface.h
@@ -42,6 +42,8 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface {
// Called when we receive congestion feedback from remote peer.
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth,
const SentPacketsMap& sent_packets) = 0;
// Called for each received ACK, with sequence number from remote peer.
@@ -49,18 +51,18 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface {
QuicByteCount acked_bytes,
QuicTime::Delta rtt) = 0;
- virtual void OnIncomingLoss(int number_of_lost_packets) = 0;
+ virtual void OnIncomingLoss(QuicTime ack_receive_time) = 0;
// Inform that we sent x bytes to the wire, and if that was a retransmission.
// Note: this function must be called for every packet sent to the wire.
- virtual void SentPacket(QuicPacketSequenceNumber sequence_number,
+ virtual void SentPacket(QuicTime sent_time,
+ QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
bool is_retransmission) = 0;
// Calculate the time until we can send the next packet.
- // Usage: When this returns 0, CongestionWindow returns the number of bytes
- // of the congestion window.
- virtual QuicTime::Delta TimeUntilSend(bool is_retransmission) = 0;
+ virtual QuicTime::Delta TimeUntilSend(QuicTime now,
+ bool is_retransmission) = 0;
// What's the current estimated bandwidth in bytes per second.
// Returns 0 when it does not have an estimate.
diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc
index f309c77..2b32d24 100644
--- a/net/quic/congestion_control/tcp_cubic_sender.cc
+++ b/net/quic/congestion_control/tcp_cubic_sender.cc
@@ -4,18 +4,18 @@
#include "net/quic/congestion_control/tcp_cubic_sender.h"
+namespace net {
+
namespace {
// Constants based on TCP defaults.
const int64 kHybridStartLowWindow = 16;
-const net::QuicByteCount kMaxSegmentSize = net::kMaxPacketSize;
-const net::QuicByteCount kDefaultReceiveWindow = 64000;
+const QuicByteCount kMaxSegmentSize = kMaxPacketSize;
+const QuicByteCount kDefaultReceiveWindow = 64000;
const int64 kInitialCongestionWindow = 10;
const int64 kMaxCongestionWindow = 10000;
const int kMaxBurstLength = 3;
};
-namespace net {
-
TcpCubicSender::TcpCubicSender(const QuicClock* clock, bool reno)
: hybrid_slow_start_(clock),
cubic_(clock),
@@ -33,6 +33,8 @@ TcpCubicSender::TcpCubicSender(const QuicClock* clock, bool reno)
void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time,
+ QuicBandwidth /*sent_bandwidth*/,
const SentPacketsMap& /*sent_packets*/) {
if (last_received_accumulated_number_of_lost_packets_ !=
feedback.tcp.accumulated_number_of_lost_packets) {
@@ -42,7 +44,7 @@ void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame(
last_received_accumulated_number_of_lost_packets_ =
feedback.tcp.accumulated_number_of_lost_packets;
if (recovered_lost_packets > 0) {
- OnIncomingLoss(recovered_lost_packets);
+ OnIncomingLoss(feedback_receive_time);
}
}
receiver_congestion_window_ = feedback.tcp.receive_window;
@@ -60,7 +62,7 @@ void TcpCubicSender::OnIncomingAck(
}
}
-void TcpCubicSender::OnIncomingLoss(int /*number_of_lost_packets*/) {
+void TcpCubicSender::OnIncomingLoss(QuicTime /*ack_receive_time*/) {
// In a normal TCP we would need to know the lowest missing packet to detect
// if we receive 3 missing packets. Here we get a missing packet for which we
// enter TCP Fast Retransmit immediately.
@@ -76,9 +78,11 @@ void TcpCubicSender::OnIncomingLoss(int /*number_of_lost_packets*/) {
if (congestion_window_ == 0) {
congestion_window_ = 1;
}
+ DLOG(INFO) << "Incoming loss; congestion window:" << congestion_window_;
}
-void TcpCubicSender::SentPacket(QuicPacketSequenceNumber sequence_number,
+void TcpCubicSender::SentPacket(QuicTime /*sent_time*/,
+ QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
bool is_retransmission) {
if (!is_retransmission) {
@@ -93,7 +97,8 @@ void TcpCubicSender::SentPacket(QuicPacketSequenceNumber sequence_number,
}
}
-QuicTime::Delta TcpCubicSender::TimeUntilSend(bool is_retransmission) {
+QuicTime::Delta TcpCubicSender::TimeUntilSend(QuicTime now,
+ bool is_retransmission) {
if (is_retransmission) {
// For TCP we can always send a retransmission immediately.
return QuicTime::Delta::Zero();
@@ -146,7 +151,6 @@ void TcpCubicSender::CongestionAvoidance(QuicPacketSequenceNumber ack) {
if (!IsCwndLimited()) {
// We don't update the congestion window unless we are close to using the
// window we have available.
- DLOG(INFO) << "Congestion avoidance window not limited";
return;
}
if (congestion_window_ < slowstart_threshold_) {
diff --git a/net/quic/congestion_control/tcp_cubic_sender.h b/net/quic/congestion_control/tcp_cubic_sender.h
index c91642c..2761e5e 100644
--- a/net/quic/congestion_control/tcp_cubic_sender.h
+++ b/net/quic/congestion_control/tcp_cubic_sender.h
@@ -15,7 +15,6 @@
#include "net/quic/congestion_control/hybrid_slow_start.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_clock.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
@@ -33,15 +32,19 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface {
// Start implementation of SendAlgorithmInterface.
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth,
const SentPacketsMap& sent_packets) OVERRIDE;
virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number,
QuicByteCount acked_bytes,
QuicTime::Delta rtt) OVERRIDE;
- virtual void OnIncomingLoss(int number_of_lost_packets) OVERRIDE;
- virtual void SentPacket(QuicPacketSequenceNumber sequence_number,
+ virtual void OnIncomingLoss(QuicTime ack_receive_time) OVERRIDE;
+ virtual void SentPacket(QuicTime sent_time,
+ QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
bool is_retransmission) OVERRIDE;
- virtual QuicTime::Delta TimeUntilSend(bool is_retransmission) OVERRIDE;
+ virtual QuicTime::Delta TimeUntilSend(QuicTime now,
+ bool is_retransmission) OVERRIDE;
virtual QuicBandwidth BandwidthEstimate() OVERRIDE;
// End implementation of SendAlgorithmInterface.
diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc
index 5e3edc4..fc9a2d7 100644
--- a/net/quic/congestion_control/tcp_cubic_sender_test.cc
+++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc
@@ -29,6 +29,7 @@ class TcpCubicSenderTest : public ::testing::Test {
TcpCubicSenderTest()
: rtt_(QuicTime::Delta::FromMilliseconds(60)),
one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ fake_bandwidth_(QuicBandwidth::Zero()),
sender_(new TcpCubicSenderPeer(&clock_, true)),
receiver_(new TcpReceiver()),
sequence_number_(1),
@@ -38,10 +39,11 @@ class TcpCubicSenderTest : public ::testing::Test {
QuicByteCount bytes_to_send = sender_->AvailableCongestionWindow();
while (bytes_to_send > 0) {
QuicByteCount bytes_in_packet = std::min(kMaxPacketSize, bytes_to_send);
- sender_->SentPacket(sequence_number_++, bytes_in_packet, false);
+ sender_->SentPacket(clock_.Now(), sequence_number_++, bytes_in_packet,
+ false);
bytes_to_send -= bytes_in_packet;
if (bytes_to_send > 0) {
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
}
}
}
@@ -56,6 +58,7 @@ class TcpCubicSenderTest : public ::testing::Test {
const QuicTime::Delta rtt_;
const QuicTime::Delta one_ms_;
+ const QuicBandwidth fake_bandwidth_;
MockClock clock_;
SendAlgorithmInterface::SentPacketsMap not_used_;
scoped_ptr<TcpCubicSenderPeer> sender_;
@@ -70,29 +73,31 @@ TEST_F(TcpCubicSenderTest, SimpleSender) {
EXPECT_EQ(kDefaultWindowTCP,
sender_->AvailableCongestionWindow());
// At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
+ fake_bandwidth_, not_used_);
// Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
// And that window is un-affected.
EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableCongestionWindow());
// A retransmitt should always retun 0.
- EXPECT_TRUE(sender_->TimeUntilSend(true).IsZero());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), true).IsZero());
}
TEST_F(TcpCubicSenderTest, ExponentialSlowStart) {
const int kNumberOfAck = 20;
QuicCongestionFeedbackFrame feedback;
// At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
+ fake_bandwidth_, not_used_);
// Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
for (int n = 0; n < kNumberOfAck; ++n) {
// Send our full congestion window.
@@ -113,12 +118,13 @@ TEST_F(TcpCubicSenderTest, SlowStartAckTrain) {
const int kNumberOfAck = 65;
QuicCongestionFeedbackFrame feedback;
// At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
+ fake_bandwidth_, not_used_);
// Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
for (int n = 0; n < kNumberOfAck; ++n) {
// Send our full congestion window.
@@ -153,12 +159,13 @@ TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) {
const int kNumberOfAck = 10;
QuicCongestionFeedbackFrame feedback;
// At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
+ fake_bandwidth_, not_used_);
// Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero());
for (int i = 0; i < kNumberOfAck; ++i) {
// Send our full congestion window.
@@ -170,10 +177,10 @@ TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) {
(kMaxPacketSize * 2 * kNumberOfAck);
EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
- sender_->OnIncomingLoss(1);
+ sender_->OnIncomingLoss(clock_.Now());
// Make sure that we should not send right now.
- EXPECT_TRUE(sender_->TimeUntilSend(false).IsInfinite());
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsInfinite());
// We should now have fallen out of slow start.
// We expect window to be cut in half.
diff --git a/net/quic/crypto/null_decrypter.cc b/net/quic/crypto/null_decrypter.cc
index 072c684..92b9204 100644
--- a/net/quic/crypto/null_decrypter.cc
+++ b/net/quic/crypto/null_decrypter.cc
@@ -11,7 +11,16 @@ using std::string;
namespace net {
-QuicData* NullDecrypter::Decrypt(StringPiece associated_data,
+bool NullDecrypter::SetKey(StringPiece key) {
+ return key.empty();
+}
+
+bool NullDecrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ return nonce_prefix.empty();
+}
+
+QuicData* NullDecrypter::Decrypt(QuicPacketSequenceNumber /*sequence_number*/,
+ StringPiece associated_data,
StringPiece ciphertext) {
QuicDataReader reader(ciphertext.data(), ciphertext.length());
diff --git a/net/quic/crypto/null_decrypter.h b/net/quic/crypto/null_decrypter.h
index 5495092..93315a8 100644
--- a/net/quic/crypto/null_decrypter.h
+++ b/net/quic/crypto/null_decrypter.h
@@ -19,7 +19,10 @@ class NET_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter {
virtual ~NullDecrypter() {}
// QuicDecrypter implementation
- virtual QuicData* Decrypt(base::StringPiece associated_data,
+ virtual bool SetKey(base::StringPiece key) OVERRIDE;
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
+ virtual QuicData* Decrypt(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
base::StringPiece ciphertext) OVERRIDE;
};
diff --git a/net/quic/crypto/null_decrypter_test.cc b/net/quic/crypto/null_decrypter_test.cc
index ad73cea..3d201fe 100644
--- a/net/quic/crypto/null_decrypter_test.cc
+++ b/net/quic/crypto/null_decrypter_test.cc
@@ -23,7 +23,7 @@ TEST(NullDecrypterTest, Decrypt) {
};
NullDecrypter decrypter;
scoped_ptr<QuicData> decrypted(
- decrypter.Decrypt("hello world!",
+ decrypter.Decrypt(0, "hello world!",
StringPiece(reinterpret_cast<const char*>(expected),
arraysize(expected))));
ASSERT_TRUE(decrypted.get());
@@ -43,7 +43,7 @@ TEST(NullDecrypterTest, BadHash) {
};
NullDecrypter decrypter;
scoped_ptr<QuicData> decrypted(
- decrypter.Decrypt("hello world!",
+ decrypter.Decrypt(0, "hello world!",
StringPiece(reinterpret_cast<const char*>(expected),
arraysize(expected))));
ASSERT_FALSE(decrypted.get());
@@ -59,7 +59,7 @@ TEST(NullDecrypterTest, ShortInput) {
};
NullDecrypter decrypter;
scoped_ptr<QuicData> decrypted(
- decrypter.Decrypt("hello world!",
+ decrypter.Decrypt(0, "hello world!",
StringPiece(reinterpret_cast<const char*>(expected),
arraysize(expected))));
ASSERT_FALSE(decrypted.get());
diff --git a/net/quic/crypto/null_encrypter.cc b/net/quic/crypto/null_encrypter.cc
index fda844b..49fc927 100644
--- a/net/quic/crypto/null_encrypter.cc
+++ b/net/quic/crypto/null_encrypter.cc
@@ -13,7 +13,16 @@ namespace net {
const size_t kHashSize = 16; // size of uint128 serialized
-QuicData* NullEncrypter::Encrypt(StringPiece associated_data,
+bool NullEncrypter::SetKey(StringPiece key) {
+ return key.empty();
+}
+
+bool NullEncrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ return nonce_prefix.empty();
+}
+
+QuicData* NullEncrypter::Encrypt(QuicPacketSequenceNumber /*sequence_number*/,
+ StringPiece associated_data,
StringPiece plaintext) {
// TODO(rch): avoid buffer copy here
string buffer = associated_data.as_string();
@@ -26,11 +35,19 @@ QuicData* NullEncrypter::Encrypt(StringPiece associated_data,
return new QuicData(writer.take(), len, true);
}
-size_t NullEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) {
+size_t NullEncrypter::GetKeySize() const {
+ return 0;
+}
+
+size_t NullEncrypter::GetNoncePrefixSize() const {
+ return 0;
+}
+
+size_t NullEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
return ciphertext_size - kHashSize;
}
-size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) {
+size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) const {
return plaintext_size + kHashSize;
}
diff --git a/net/quic/crypto/null_encrypter.h b/net/quic/crypto/null_encrypter.h
index e73423d..62f4ef6 100644
--- a/net/quic/crypto/null_encrypter.h
+++ b/net/quic/crypto/null_encrypter.h
@@ -19,10 +19,15 @@ class NET_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter {
virtual ~NullEncrypter() {}
// QuicEncrypter implementation
- virtual QuicData* Encrypt(base::StringPiece associated_data,
+ virtual bool SetKey(base::StringPiece key) OVERRIDE;
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
+ virtual QuicData* Encrypt(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
base::StringPiece plaintext) OVERRIDE;
- virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) OVERRIDE;
- virtual size_t GetCiphertextSize(size_t plaintext_size) OVERRIDE;
+ virtual size_t GetKeySize() const OVERRIDE;
+ virtual size_t GetNoncePrefixSize() const OVERRIDE;
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE;
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE;
};
} // namespace net
diff --git a/net/quic/crypto/null_encrypter_test.cc b/net/quic/crypto/null_encrypter_test.cc
index 9f2cec2..328c738 100644
--- a/net/quic/crypto/null_encrypter_test.cc
+++ b/net/quic/crypto/null_encrypter_test.cc
@@ -22,7 +22,7 @@ TEST(NullEncrypterTest, Encrypt) {
'b', 'y', 'e', '!',
};
NullEncrypter encrypter;
- scoped_ptr<QuicData> encrypted(encrypter.Encrypt("hello world!",
+ scoped_ptr<QuicData> encrypted(encrypter.Encrypt(0, "hello world!",
"goodbye!"));
ASSERT_TRUE(encrypted.get());
test::CompareCharArraysWithHexError(
diff --git a/net/quic/crypto/quic_decrypter.h b/net/quic/crypto/quic_decrypter.h
index e5c5635..c8b691d 100644
--- a/net/quic/crypto/quic_decrypter.h
+++ b/net/quic/crypto/quic_decrypter.h
@@ -17,9 +17,37 @@ class NET_EXPORT_PRIVATE QuicDecrypter {
static QuicDecrypter* Create(CryptoTag algorithm);
+ // Sets the encryption key. Returns true on success, false on failure.
+ //
+ // NOTE: The key is the client_write_key or server_write_key derived from
+ // the master secret.
+ virtual bool SetKey(base::StringPiece key) = 0;
+
+ // Sets the fixed initial bytes of the nonce. Returns true on success,
+ // false on failure.
+ //
+ // NOTE: The nonce prefix is the client_write_iv or server_write_iv
+ // derived from the master secret. A 64-bit packet sequence number will
+ // be appended to form the nonce.
+ //
+ // <------------ 64 bits ----------->
+ // +---------------------+----------------------------------+
+ // | Fixed prefix | Packet sequence number |
+ // +---------------------+----------------------------------+
+ // Nonce format
+ //
+ // The security of the nonce format requires that QUIC never reuse a
+ // packet sequence number, even when retransmitting a lost packet.
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) = 0;
+
// Returns a newly created QuicData object containing the decrypted
- // |ciphertext| or NULL if there is an error.
- virtual QuicData* Decrypt(base::StringPiece associated_data,
+ // |ciphertext| or NULL if there is an error. |sequence_number| is
+ // appended to the |nonce_prefix| value provided in SetNoncePrefix()
+ // to form the nonce.
+ // TODO(wtc): add a way for Decrypt to report decryption failure due
+ // to non-authentic inputs, as opposed to other reasons for failure.
+ virtual QuicData* Decrypt(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
base::StringPiece ciphertext) = 0;
};
diff --git a/net/quic/crypto/quic_encrypter.h b/net/quic/crypto/quic_encrypter.h
index f077c1f..214ce2d 100644
--- a/net/quic/crypto/quic_encrypter.h
+++ b/net/quic/crypto/quic_encrypter.h
@@ -17,20 +17,55 @@ class NET_EXPORT_PRIVATE QuicEncrypter {
static QuicEncrypter* Create(CryptoTag algorithm);
+ // Sets the encryption key. Returns true on success, false on failure.
+ //
+ // NOTE: The key is the client_write_key or server_write_key derived from
+ // the master secret.
+ virtual bool SetKey(base::StringPiece key) = 0;
+
+ // Sets the fixed initial bytes of the nonce. Returns true on success,
+ // false on failure.
+ //
+ // NOTE: The nonce prefix is the client_write_iv or server_write_iv
+ // derived from the master secret. A 64-bit packet sequence number will
+ // be appended to form the nonce.
+ //
+ // <------------ 64 bits ----------->
+ // +---------------------+----------------------------------+
+ // | Fixed prefix | Packet sequence number |
+ // +---------------------+----------------------------------+
+ // Nonce format
+ //
+ // The security of the nonce format requires that QUIC never reuse a
+ // packet sequence number, even when retransmitting a lost packet.
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) = 0;
+
// Returns a newly created QuicData object containing the encrypted
// |plaintext| as well as a MAC over both |plaintext| and |associated_data|,
- // or NULL if there is an error.
- virtual QuicData* Encrypt(base::StringPiece associated_data,
+ // or NULL if there is an error. |sequence_number| is appended to the
+ // |nonce_prefix| value provided in SetNoncePrefix() to form the nonce.
+ virtual QuicData* Encrypt(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
base::StringPiece plaintext) = 0;
+ // GetKeySize() and GetNoncePrefixSize() tell the HKDF class how many bytes
+ // of key material needs to be derived from the master secret.
+ // NOTE: the sizes returned by GetKeySize() and GetNoncePrefixSize() are
+ // also correct for the QuicDecrypter of the same algorithm. So only
+ // QuicEncrypter has these two methods.
+
+ // Returns the size in bytes of a key for the algorithm.
+ virtual size_t GetKeySize() const = 0;
+ // Returns the size in bytes of the fixed initial part of the nonce.
+ virtual size_t GetNoncePrefixSize() const = 0;
+
// Returns the maximum length of plaintext that can be encrypted
// to ciphertext no larger than |ciphertext_size|.
- virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) = 0;
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const = 0;
// Returns the length of the ciphertext that would be generated by encrypting
// to plaintext of size |plaintext_size|.
- virtual size_t GetCiphertextSize(size_t plaintext_size) = 0;
-
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const = 0;
};
} // namespace net
diff --git a/net/quic/crypto/quic_random.cc b/net/quic/crypto/quic_random.cc
index 9a67918..be1f3c0 100644
--- a/net/quic/crypto/quic_random.cc
+++ b/net/quic/crypto/quic_random.cc
@@ -19,6 +19,7 @@ class DefaultRandom : public QuicRandom {
// QuicRandom implementation
virtual void RandBytes(void* data, size_t len) OVERRIDE;
virtual uint64 RandUint64() OVERRIDE;
+ virtual bool RandBool() OVERRIDE;
virtual void Reseed(const void* additional_entropy,
size_t entropy_len) OVERRIDE;
@@ -44,6 +45,12 @@ uint64 DefaultRandom::RandUint64() {
return value;
}
+bool DefaultRandom::RandBool() {
+ char value;
+ RandBytes(&value, sizeof(value));
+ return (value & 1) == 1;
+}
+
void DefaultRandom::Reseed(const void* additional_entropy, size_t entropy_len) {
// No such function exists in crypto/random.h.
}
diff --git a/net/quic/crypto/quic_random.h b/net/quic/crypto/quic_random.h
index ac69b85..68640c1 100644
--- a/net/quic/crypto/quic_random.h
+++ b/net/quic/crypto/quic_random.h
@@ -27,6 +27,9 @@ class NET_EXPORT_PRIVATE QuicRandom {
// Returns a random number in the range [0, kuint64max].
virtual uint64 RandUint64() = 0;
+ // Returns a random boolean value.
+ virtual bool RandBool() = 0;
+
// Reseeds the random number generator with additional entropy input.
// NOTE: the constructor of a QuicRandom object is responsible for seeding
// itself with enough entropy input.
diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc
index 2154d66..d562080 100644
--- a/net/quic/quic_client_session.cc
+++ b/net/quic/quic_client_session.cc
@@ -53,6 +53,11 @@ QuicReliableClientStream* QuicClientSession::CreateOutgoingReliableStream() {
<< "Already " << GetNumOpenStreams() << " open.";
return NULL;
}
+ if (goaway_received()) {
+ DLOG(INFO) << "Failed to create a new outgoing stream. "
+ << "Already received goaway.";
+ return NULL;
+ }
QuicReliableClientStream* stream =
new QuicReliableClientStream(GetNextStreamId(), this, net_log_);
ActivateStream(stream);
diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc
index 674b0a4..fbacd09 100644
--- a/net/quic/quic_client_session_test.cc
+++ b/net/quic/quic_client_session_test.cc
@@ -77,6 +77,20 @@ TEST_F(QuicClientSessionTest, MaxNumConnections) {
EXPECT_TRUE(session_.CreateOutgoingReliableStream());
}
+TEST_F(QuicClientSessionTest, GoAwayReceived) {
+ // Initialize crypto before the client session will create a stream.
+ ASSERT_TRUE(session_.CryptoConnect(callback_.callback()));
+ // Simulate the server crypto handshake.
+ CryptoHandshakeMessage server_message;
+ server_message.tag = kSHLO;
+ session_.GetCryptoStream()->OnHandshakeMessage(server_message);
+
+ // After receiving a GoAway, I should no longer be able to create outgoing
+ // streams.
+ session_.OnGoAway(QuicGoAwayFrame(QUIC_PEER_GOING_AWAY, 1u, "Going away."));
+ EXPECT_EQ(NULL, session_.CreateOutgoingReliableStream());
+}
+
TEST_F(QuicClientSessionTest, Logging) {
// Initialize crypto before the client session will create a stream.
ASSERT_EQ(ERR_IO_PENDING, session_.CryptoConnect(callback_.callback()));
@@ -91,7 +105,6 @@ TEST_F(QuicClientSessionTest, Logging) {
QuicFramer framer(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL));
QuicRstStreamFrame frame;
frame.stream_id = 2;
- frame.offset = 7;
frame.error_code = QUIC_CONNECTION_TIMED_OUT;
frame.error_details = "doh!";
@@ -99,12 +112,16 @@ TEST_F(QuicClientSessionTest, Logging) {
frames.push_back(QuicFrame(&frame));
QuicPacketHeader header;
header.public_header.guid = 1;
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
header.packet_sequence_number = 1;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_entropy_flag = false;
header.fec_group = 0;
- scoped_ptr<QuicPacket> p(framer.ConstructFrameDataPacket(header, frames));
- scoped_ptr<QuicEncryptedPacket> packet(framer.EncryptPacket(*p));
+ scoped_ptr<QuicPacket> p(
+ framer.ConstructFrameDataPacket(header, frames).packet);
+ scoped_ptr<QuicEncryptedPacket> packet(framer.EncryptPacket(1, *p));
IPAddressNumber ip;
CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip));
IPEndPoint peer_addr = IPEndPoint(ip, 443);
@@ -134,9 +151,6 @@ TEST_F(QuicClientSessionTest, Logging) {
int stream_id;
ASSERT_TRUE(entries[pos].GetIntegerValue("stream_id", &stream_id));
EXPECT_EQ(frame.stream_id, static_cast<QuicStreamId>(stream_id));
- int offset;
- ASSERT_TRUE(entries[pos].GetIntegerValue("offset", &offset));
- EXPECT_EQ(frame.offset, static_cast<QuicStreamOffset>(offset));
int error_code;
ASSERT_TRUE(entries[pos].GetIntegerValue("error_code", &error_code));
EXPECT_EQ(frame.error_code, static_cast<QuicErrorCode>(error_code));
diff --git a/net/quic/quic_clock.cc b/net/quic/quic_clock.cc
index 32d9991..26c1a65 100644
--- a/net/quic/quic_clock.cc
+++ b/net/quic/quic_clock.cc
@@ -13,6 +13,11 @@ QuicClock::QuicClock() {
QuicClock::~QuicClock() {}
+QuicTime QuicClock::ApproximateNow() const {
+ // Chrome does not have a distinct notion of ApproximateNow().
+ return Now();
+}
+
QuicTime QuicClock::Now() const {
return QuicTime(base::TimeTicks::Now());
}
diff --git a/net/quic/quic_clock.h b/net/quic/quic_clock.h
index dca211e..6b6cb31 100644
--- a/net/quic/quic_clock.h
+++ b/net/quic/quic_clock.h
@@ -21,6 +21,10 @@ class NET_EXPORT_PRIVATE QuicClock {
virtual ~QuicClock();
// Returns the approximate current time as a QuicTime object.
+ virtual QuicTime ApproximateNow() const;
+
+ // Returns the current time as a QuicTime object.
+ // Note: this use significant resources please use only if needed.
virtual QuicTime Now() const;
// Returns the current time as an offset from the Unix epoch (1970-01-01
diff --git a/net/quic/quic_clock_test.cc b/net/quic/quic_clock_test.cc
index 9a9914d..1f285942 100644
--- a/net/quic/quic_clock_test.cc
+++ b/net/quic/quic_clock_test.cc
@@ -13,7 +13,7 @@ TEST(QuicClockTest, Now) {
QuicClock clock;
QuicTime start(base::TimeTicks::Now());
- QuicTime now = clock.Now();
+ QuicTime now = clock.ApproximateNow();
QuicTime end(base::TimeTicks::Now());
EXPECT_LE(start, now);
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index 8dc1994..c04c0dd 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -17,6 +17,7 @@ using base::StringPiece;
using std::list;
using std::make_pair;
using std::min;
+using std::max;
using std::vector;
using std::set;
using std::string;
@@ -34,7 +35,12 @@ const QuicPacketSequenceNumber kMaxPacketGap = 5000;
// The maximum number of nacks which can be transmitted in a single ack packet
// without exceeding kMaxPacketSize.
-const QuicPacketSequenceNumber kMaxUnackedPackets = 192u;
+// TODO(satyamshekhar): Get rid of magic numbers and move this to protocol.h
+// 16 - Min ack frame size.
+// 16 - Crypto hash for integrity. Not a static value. Use
+// QuicEncrypter::GetMaxPlaintextSize.
+const QuicPacketSequenceNumber kMaxUnackedPackets =
+ (kMaxPacketSize - kPacketHeaderSize - 16 - 16) / kSequenceNumberSize;
// 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.
@@ -55,9 +61,7 @@ const int kMaxPacketsToSerializeAtOnce = 6;
// eventually cede. 10 is arbitrary.
const int kMaxPacketsPerRetransmissionAlarm = 10;
-// Named constant for WriteQueuedData().
-const bool kFlush = true;
-// Named constant for WritePacket(), SendOrQueuePacket().
+// Named constant for WritePacket()
const bool kForce = true;
// Named constant for CanWrite().
const bool kIsRetransmission = true;
@@ -69,19 +73,6 @@ bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) {
} // namespace
-QuicConnection::UnackedPacket::UnackedPacket(QuicFrames unacked_frames)
- : frames(unacked_frames) {
-}
-
-QuicConnection::UnackedPacket::UnackedPacket(QuicFrames unacked_frames,
- std::string data)
- : frames(unacked_frames),
- data(data) {
-}
-
-QuicConnection::UnackedPacket::~UnackedPacket() {
-}
-
QuicConnection::QuicConnection(QuicGuid guid,
IPEndPoint address,
QuicConnectionHelperInterface* helper)
@@ -91,17 +82,18 @@ QuicConnection::QuicConnection(QuicGuid guid,
random_generator_(helper->GetRandomGenerator()),
guid_(guid),
peer_address_(address),
- should_send_ack_(false),
- should_send_congestion_feedback_(false),
largest_seen_packet_with_ack_(0),
peer_largest_observed_packet_(0),
+ least_packet_awaited_by_peer_(1),
peer_least_packet_awaiting_ack_(0),
handling_retransmission_timeout_(false),
write_blocked_(false),
debug_visitor_(NULL),
- packet_creator_(guid_, &framer_),
+ packet_creator_(guid_, &framer_, random_generator_),
+ packet_generator_(this, &packet_creator_),
timeout_(QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)),
- time_of_last_packet_(clock_->Now()),
+ time_of_last_received_packet_(clock_->ApproximateNow()),
+ time_of_last_sent_packet_(clock_->ApproximateNow()),
congestion_manager_(clock_, kTCP),
connected_(true),
received_truncated_ack_(false),
@@ -109,9 +101,12 @@ QuicConnection::QuicConnection(QuicGuid guid,
helper_->SetConnection(this);
helper_->SetTimeoutAlarm(timeout_);
framer_.set_visitor(this);
+ framer_.set_entropy_calculator(&entropy_manager_);
memset(&last_header_, 0, sizeof(last_header_));
outgoing_ack_.sent_info.least_unacked = 0;
+ outgoing_ack_.sent_info.entropy_hash = 0;
outgoing_ack_.received_info.largest_observed = 0;
+ outgoing_ack_.received_info.entropy_hash = 0;
/*
if (FLAGS_fake_packet_loss_percentage > 0) {
@@ -123,12 +118,6 @@ QuicConnection::QuicConnection(QuicGuid guid,
}
QuicConnection::~QuicConnection() {
- // Call DeleteEnclosedFrames on each QuicPacket because the destructor does
- // not delete enclosed frames.
- for (UnackedPacketMap::iterator it = unacked_packets_.begin();
- it != unacked_packets_.end(); ++it) {
- DeleteEnclosedFrames(it->second);
- }
STLDeleteValues(&unacked_packets_);
STLDeleteValues(&group_map_);
for (QueuedPacketList::iterator it = queued_packets_.begin();
@@ -137,45 +126,14 @@ QuicConnection::~QuicConnection() {
}
}
-void QuicConnection::DeleteEnclosedFrame(QuicFrame* frame) {
- switch (frame->type) {
- case PADDING_FRAME:
- delete frame->padding_frame;
- break;
- case STREAM_FRAME:
- delete frame->stream_frame;
- break;
- case ACK_FRAME:
- delete frame->ack_frame;
- break;
- case CONGESTION_FEEDBACK_FRAME:
- delete frame->congestion_feedback_frame;
- break;
- case RST_STREAM_FRAME:
- delete frame->rst_stream_frame;
- break;
- case CONNECTION_CLOSE_FRAME:
- delete frame->connection_close_frame;
- break;
- case NUM_FRAME_TYPES:
- DCHECK(false) << "Cannot delete type: " << frame->type;
- }
-}
-
-void QuicConnection::DeleteEnclosedFrames(UnackedPacket* unacked) {
- for (QuicFrames::iterator it = unacked->frames.begin();
- it != unacked->frames.end(); ++it) {
- DeleteEnclosedFrame(&(*it));
- }
-}
-
void QuicConnection::OnError(QuicFramer* framer) {
SendConnectionClose(framer->error());
}
void QuicConnection::OnPacket() {
- time_of_last_packet_ = clock_->Now();
- DVLOG(1) << "last packet: " << time_of_last_packet_.ToMicroseconds();
+ time_of_last_received_packet_ = clock_->Now();
+ DVLOG(1) << "time of last received packet: "
+ << time_of_last_received_packet_.ToMicroseconds();
// TODO(alyssar, rch) handle migration!
self_address_ = last_self_address_;
@@ -213,11 +171,12 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
// If this packet has already been seen, or that the sender
// has told us will not be retransmitted, then stop processing the packet.
- if (!outgoing_ack_.received_info.IsAwaitingPacket(
- header.packet_sequence_number)) {
+ if (!IsAwaitingPacket(outgoing_ack_.received_info,
+ header.packet_sequence_number)) {
return false;
}
+ DVLOG(1) << "Received packet header: " << header;
last_header_ = header;
return true;
}
@@ -239,7 +198,7 @@ void QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) {
if (debug_visitor_) {
debug_visitor_->OnAckFrame(incoming_ack);
}
- DVLOG(1) << "Ack packet: " << incoming_ack;
+ DVLOG(1) << "OnAckFrame: " << incoming_ack;
if (last_header_.packet_sequence_number <= largest_seen_packet_with_ack_) {
DLOG(INFO) << "Received an old ack frame: ignoring";
@@ -252,19 +211,28 @@ void QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) {
return;
}
+ // TODO(satyamshekhar): Not true if missing_packets.size() was actually
+ // kMaxUnackedPackets. This can result in a dead connection if all the
+ // missing packets get lost during retransmission. Now the new packets(or the
+ // older packets) will not be retransmitted due to RTO
+ // since received_truncated_ack_ is true and their sequence_number is >
+ // peer_largest_observed_packet. Fix either by resetting it in
+ // MaybeRetransmitPacketForRTO or keeping an explicit flag for ack truncation.
received_truncated_ack_ =
incoming_ack.received_info.missing_packets.size() >= kMaxUnackedPackets;
UpdatePacketInformationReceivedByPeer(incoming_ack);
UpdatePacketInformationSentByPeer(incoming_ack);
- congestion_manager_.OnIncomingAckFrame(incoming_ack);
+ congestion_manager_.OnIncomingAckFrame(incoming_ack,
+ time_of_last_received_packet_);
// Now the we have received an ack, we might be able to send queued packets.
if (queued_packets_.empty()) {
return;
}
- QuicTime::Delta delay = congestion_manager_.TimeUntilSend(false);
+ QuicTime::Delta delay = congestion_manager_.TimeUntilSend(
+ time_of_last_received_packet_, false);
if (delay.IsZero()) {
helper_->UnregisterSendAlarmIfRegistered();
if (!write_blocked_) {
@@ -280,7 +248,8 @@ void QuicConnection::OnCongestionFeedbackFrame(
if (debug_visitor_) {
debug_visitor_->OnCongestionFeedbackFrame(feedback);
}
- congestion_manager_.OnIncomingQuicCongestionFeedbackFrame(feedback);
+ congestion_manager_.OnIncomingQuicCongestionFeedbackFrame(
+ feedback, time_of_last_received_packet_);
}
bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
@@ -308,19 +277,47 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
kMaxUnackedPackets);
if (incoming_ack.sent_info.least_unacked < peer_least_packet_awaiting_ack_) {
- DLOG(INFO) << "Client sent low least_unacked: "
- << incoming_ack.sent_info.least_unacked
- << " vs " << peer_least_packet_awaiting_ack_;
+ DLOG(ERROR) << "Client sent low least_unacked: "
+ << incoming_ack.sent_info.least_unacked
+ << " vs " << peer_least_packet_awaiting_ack_;
// We never process old ack frames, so this number should only increase.
return false;
}
if (incoming_ack.sent_info.least_unacked >
last_header_.packet_sequence_number) {
- DLOG(INFO) << "Client sent least_unacked:"
- << incoming_ack.sent_info.least_unacked
- << " greater than the enclosing packet sequence number:"
- << last_header_.packet_sequence_number;
+ DLOG(ERROR) << "Client sent least_unacked:"
+ << incoming_ack.sent_info.least_unacked
+ << " greater than the enclosing packet sequence number:"
+ << last_header_.packet_sequence_number;
+ return false;
+ }
+
+ if (!incoming_ack.received_info.missing_packets.empty() &&
+ *incoming_ack.received_info.missing_packets.rbegin() >
+ incoming_ack.received_info.largest_observed) {
+ DLOG(ERROR) << "Client sent missing packet: "
+ << *incoming_ack.received_info.missing_packets.rbegin()
+ << " greater than largest observed: "
+ << incoming_ack.received_info.largest_observed;
+ return false;
+ }
+
+ if (!incoming_ack.received_info.missing_packets.empty() &&
+ *incoming_ack.received_info.missing_packets.begin() <
+ least_packet_awaited_by_peer_) {
+ DLOG(ERROR) << "Client sent missing packet: "
+ << *incoming_ack.received_info.missing_packets.begin()
+ << "smaller than least_packet_awaited_by_peer_: "
+ << least_packet_awaited_by_peer_;
+ return false;
+ }
+
+ if (!entropy_manager_.IsValidEntropy(
+ incoming_ack.received_info.largest_observed,
+ incoming_ack.received_info.missing_packets,
+ incoming_ack.received_info.entropy_hash)) {
+ DLOG(ERROR) << "Client sent invalid entropy.";
return false;
}
@@ -329,46 +326,36 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
void QuicConnection::UpdatePacketInformationReceivedByPeer(
const QuicAckFrame& incoming_ack) {
- QuicConnectionVisitorInterface::AckedPackets acked_packets;
+ SequenceNumberSet acked_packets;
// ValidateAck should fail if largest_observed ever shrinks.
DCHECK_LE(peer_largest_observed_packet_,
incoming_ack.received_info.largest_observed);
peer_largest_observed_packet_ = incoming_ack.received_info.largest_observed;
- // Pick an upper bound for the lowest_unacked; we'll then loop through the
- // unacked packets and lower it if necessary.
- QuicPacketSequenceNumber lowest_unacked = min(
- packet_creator_.sequence_number() + 1,
- peer_largest_observed_packet_ + 1);
+ if (incoming_ack.received_info.missing_packets.empty()) {
+ least_packet_awaited_by_peer_ = peer_largest_observed_packet_ + 1;
+ } else {
+ least_packet_awaited_by_peer_ =
+ *(incoming_ack.received_info.missing_packets.begin());
+ }
- int retransmitted_packets = 0;
+ entropy_manager_.ClearSentEntropyBefore(least_packet_awaited_by_peer_ - 1);
+ 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;
- UnackedPacket* unacked = it->second;
- if (!incoming_ack.received_info.IsAwaitingPacket(sequence_number)) {
+ if (sequence_number > peer_largest_observed_packet_) {
+ 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) << "Got an ack for " << sequence_number;
- // TODO(rch): This is inefficient and should be sped up.
- // TODO(ianswett): Ensure this inner loop is applicable now that we're
- // always sending packets with new sequence numbers. I believe it may
- // only be relevant for the first crypto connect packet, which doesn't
- // get a new packet sequence number.
- // The acked packet might be queued (if a retransmission had been
- // attempted).
- for (QueuedPacketList::iterator q = queued_packets_.begin();
- q != queued_packets_.end(); ++q) {
- if (q->sequence_number == sequence_number) {
- queued_packets_.erase(q);
- break;
- }
- }
acked_packets.insert(sequence_number);
- DeleteEnclosedFrames(unacked);
delete unacked;
UnackedPacketMap::iterator it_tmp = it;
++it;
@@ -379,70 +366,66 @@ void QuicConnection::UpdatePacketInformationReceivedByPeer(
// seen at the time of this ack being sent out. See if it's our new
// lowest unacked packet.
DVLOG(1) << "still missing " << sequence_number;
- if (sequence_number < lowest_unacked) {
- lowest_unacked = sequence_number;
- }
++it;
- // Determine if this packet is being explicitly nacked and, if so, if it
- // is worth retransmitting.
- if (sequence_number <= peer_largest_observed_packet_) {
- // 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) << "Trying to retransmit packet " << sequence_number
- << " as it has been nacked 3 or more times.";
- // TODO(satyamshekhar): save in a vector and retransmit after the
- // loop.
- RetransmitPacket(sequence_number);
- }
+ // 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) << "Trying to retransmit packet " << sequence_number
+ << " as it has been nacked 3 or more times.";
+ // TODO(satyamshekhar): save in a vector and retransmit after the
+ // loop.
+ RetransmitPacket(sequence_number);
}
}
}
if (acked_packets.size() > 0) {
visitor_->OnAck(acked_packets);
}
- SetLeastUnacked(lowest_unacked);
-}
-
-void QuicConnection::SetLeastUnacked(QuicPacketSequenceNumber lowest_unacked) {
- // If we've gotten an ack for the lowest packet we were waiting on,
- // update that and the list of packets we advertise we will not retransmit.
- if (lowest_unacked > outgoing_ack_.sent_info.least_unacked) {
- outgoing_ack_.sent_info.least_unacked = lowest_unacked;
- }
}
-void QuicConnection::UpdateLeastUnacked(
- QuicPacketSequenceNumber acked_sequence_number) {
- if (acked_sequence_number != outgoing_ack_.sent_info.least_unacked) {
- return;
- }
- QuicPacketSequenceNumber least_unacked =
- packet_creator_.sequence_number() + 1;
- for (UnackedPacketMap::iterator it = unacked_packets_.begin();
- it != unacked_packets_.end(); ++it) {
- least_unacked = min<int>(least_unacked, it->first);
- }
-
- SetLeastUnacked(least_unacked);
+bool QuicConnection::DontWaitForPacketsBefore(
+ QuicPacketSequenceNumber least_unacked) {
+ size_t missing_packets_count =
+ outgoing_ack_.received_info.missing_packets.size();
+ outgoing_ack_.received_info.missing_packets.erase(
+ outgoing_ack_.received_info.missing_packets.begin(),
+ outgoing_ack_.received_info.missing_packets.lower_bound(least_unacked));
+ return missing_packets_count !=
+ outgoing_ack_.received_info.missing_packets.size();
}
void QuicConnection::UpdatePacketInformationSentByPeer(
const QuicAckFrame& incoming_ack) {
- // Make sure we also don't ack any packets lower than the peer's
- // last-packet-awaiting-ack.
+ // ValidateAck() should fail if peer_least_packet_awaiting_ack_ shrinks.
+ DCHECK_LE(peer_least_packet_awaiting_ack_,
+ incoming_ack.sent_info.least_unacked);
if (incoming_ack.sent_info.least_unacked > peer_least_packet_awaiting_ack_) {
- outgoing_ack_.received_info.ClearMissingBefore(
- incoming_ack.sent_info.least_unacked);
+ bool missed_packets =
+ DontWaitForPacketsBefore(incoming_ack.sent_info.least_unacked);
+ if (missed_packets || incoming_ack.sent_info.least_unacked >
+ outgoing_ack_.received_info.largest_observed + 1) {
+ DVLOG(1) << "Updating entropy hashed since we missed packets";
+ // There were some missing packets that we won't ever get now. Recalculate
+ // the received entropy hash.
+ entropy_manager_.RecalculateReceivedEntropyHash(
+ incoming_ack.sent_info.least_unacked,
+ incoming_ack.sent_info.entropy_hash);
+ }
peer_least_packet_awaiting_ack_ = incoming_ack.sent_info.least_unacked;
- }
-
+ // TODO(satyamshekhar): We get this iterator O(logN) in
+ // RecalculateReceivedEntropyHash also.
+ entropy_manager_.ClearReceivedEntropyBefore(
+ peer_least_packet_awaiting_ack_);
+ }
+ DCHECK(outgoing_ack_.received_info.missing_packets.empty() ||
+ *outgoing_ack_.received_info.missing_packets.begin() >=
+ peer_least_packet_awaiting_ack_);
// Possibly close any FecGroups which are now irrelevant
CloseFecGroupsBefore(incoming_ack.sent_info.least_unacked + 1);
}
@@ -450,7 +433,8 @@ void QuicConnection::UpdatePacketInformationSentByPeer(
void QuicConnection::OnFecData(const QuicFecData& fec) {
DCHECK_NE(0u, last_header_.fec_group);
QuicFecGroup* group = GetFecGroup();
- group->UpdateFec(last_header_.packet_sequence_number, fec);
+ group->UpdateFec(last_header_.packet_sequence_number,
+ last_header_.fec_entropy_flag, fec);
}
void QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
@@ -472,22 +456,31 @@ void QuicConnection::OnConnectionCloseFrame(
CloseConnection(frame.error_code, true);
}
+void QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ DLOG(INFO) << "Go away received with error "
+ << QuicUtils::ErrorToString(frame.error_code)
+ << " and reason:" << frame.reason_phrase;
+ visitor_->OnGoAway(frame);
+}
+
void QuicConnection::OnPacketComplete() {
+ // TODO(satyamshekhar): Don't do anything if this packet closed the
+ // connection.
if (!last_packet_revived_) {
DLOG(INFO) << "Got packet " << last_header_.packet_sequence_number
<< " with " << last_stream_frames_.size()
<< " stream frames for " << last_header_.public_header.guid;
congestion_manager_.RecordIncomingPacket(
last_size_, last_header_.packet_sequence_number,
- clock_->Now(), last_packet_revived_);
+ time_of_last_received_packet_, last_packet_revived_);
} else {
DLOG(INFO) << "Got revived packet with " << last_stream_frames_.size()
<< " frames.";
}
- if (last_stream_frames_.empty() ||
- visitor_->OnPacket(self_address_, peer_address_,
- last_header_, last_stream_frames_)) {
+ if ((last_stream_frames_.empty() ||
+ visitor_->OnPacket(self_address_, peer_address_,
+ last_header_, last_stream_frames_))) {
RecordPacketReceived(last_header_);
}
@@ -495,6 +488,14 @@ void QuicConnection::OnPacketComplete() {
last_stream_frames_.clear();
}
+QuicAckFrame* QuicConnection::CreateAckFrame() {
+ return new QuicAckFrame(outgoing_ack_);
+}
+
+QuicCongestionFeedbackFrame* QuicConnection::CreateFeedbackFrame() {
+ return new QuicCongestionFeedbackFrame(outgoing_congestion_feedback_);
+}
+
void QuicConnection::MaybeSendAckInResponseToPacket() {
if (send_ack_in_response_to_packet_) {
SendAck();
@@ -507,62 +508,16 @@ void QuicConnection::MaybeSendAckInResponseToPacket() {
}
QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id,
- StringPiece data,
- QuicStreamOffset offset,
- bool fin) {
- size_t total_bytes_consumed = 0;
- bool fin_consumed = false;
-
- while (queued_packets_.empty()) {
- packet_creator_.MaybeStartFEC();
- QuicFrame frame;
- size_t bytes_consumed = packet_creator_.CreateStreamFrame(
- id, data, offset, fin, &frame);
- bool success = packet_creator_.AddFrame(frame);
- DCHECK(success);
-
- total_bytes_consumed += bytes_consumed;
- offset += bytes_consumed;
- fin_consumed = fin && bytes_consumed == data.size();
- data.remove_prefix(bytes_consumed);
-
- // TODO(ianswett): Currently this does not pack stream data together,
- // because SendStreamData does not know if there are more streams to write.
- // TODO(ianswett): Restore packet reordering.
- SendOrQueueCurrentPacket();
-
- if (packet_creator_.ShouldSendFec(false)) {
- PacketPair fec_pair = packet_creator_.SerializeFec();
- // Never retransmit FEC packets.
- SendOrQueuePacket(fec_pair.first, fec_pair.second, !kForce);
- }
-
- if (data.empty()) {
- // We're done writing the data. Exit the loop.
- // We don't make this a precondition because we could have 0 bytes of data
- // if we're simply writing a fin.
- break;
- }
- }
- // Ensure the FEC group is closed at the end of this method.
- if (packet_creator_.ShouldSendFec(true)) {
- PacketPair fec_pair = packet_creator_.SerializeFec();
- // Never retransmit FEC packets.
- SendOrQueuePacket(fec_pair.first, fec_pair.second, !kForce);
- }
- return QuicConsumedData(total_bytes_consumed, fin_consumed);
+ base::StringPiece data,
+ QuicStreamOffset offset,
+ bool fin) {
+ return packet_generator_.ConsumeData(id, data, offset, fin);
}
void QuicConnection::SendRstStream(QuicStreamId id,
- QuicErrorCode error,
- QuicStreamOffset offset) {
- queued_control_frames_.push_back(QuicFrame(
- new QuicRstStreamFrame(id, offset, error)));
-
- // Try to write immediately if possible.
- if (CanWrite(!kIsRetransmission)) {
- WriteQueuedData(kFlush);
- }
+ QuicErrorCode error) {
+ packet_generator_.AddControlFrame(
+ QuicFrame(new QuicRstStreamFrame(id, error)));
}
void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address,
@@ -580,25 +535,24 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address,
}
bool QuicConnection::OnCanWrite() {
+ LOG(INFO) << "here!!!";
write_blocked_ = false;
- WriteQueuedData(!kFlush);
+ WriteQueuedPackets();
- // Ensure there's enough room for a StreamFrame before calling the visitor.
- if (packet_creator_.BytesFree() <= kMinStreamFrameLength) {
- SendOrQueueCurrentPacket();
- }
-
- // If we've sent everything we had queued and we're still not blocked, let the
- // visitor know it can write more.
+ // 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.
+ // TODO(rch): shouldn't this be "if (CanWrite(false))"
if (!write_blocked_) {
+ packet_generator_.StartBatchOperations();
bool all_bytes_written = visitor_->OnCanWrite();
- // If the latest write caused a socket-level blockage, return false: we will
- // be rescheduled by the kernel.
- if (write_blocked_) {
- return false;
- }
- if (!all_bytes_written && !helper_->IsSendAlarmSet()) {
+ packet_generator_.FinishBatchOperations();
+
+ // After the visitor writes, it may have caused the socket to become write
+ // blocked or the congestion manager to prohibit sending, so check again.
+ if (!write_blocked_ && !all_bytes_written && !helper_->IsSendAlarmSet()) {
// 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.
@@ -606,20 +560,12 @@ bool QuicConnection::OnCanWrite() {
}
}
- // If a write can still be performed, ensure there are no pending frames,
- // even if they didn't fill a packet.
- if (packet_creator_.HasPendingFrames() && CanWrite(!kIsRetransmission)) {
- SendOrQueueCurrentPacket();
- }
-
return !write_blocked_;
}
-bool QuicConnection::WriteQueuedData(bool flush) {
+bool QuicConnection::WriteQueuedPackets() {
DCHECK(!write_blocked_);
- DCHECK(!packet_creator_.HasPendingFrames());
- // Send all queued packets first.
size_t num_queued_packets = queued_packets_.size() + 1;
QueuedPacketList::iterator packet_iterator = queued_packets_.begin();
while (!write_blocked_ && !helper_->IsSendAlarmSet() &&
@@ -633,52 +579,37 @@ bool QuicConnection::WriteQueuedData(bool flush) {
packet_iterator->packet, !kForce)) {
packet_iterator = queued_packets_.erase(packet_iterator);
} else {
- // TODO(ianswett): Why not break or return false here?
+ // Continue, because some queued packets may still be writable.
++packet_iterator;
}
}
- if (write_blocked_) {
- return false;
- }
-
- while ((!queued_control_frames_.empty() || should_send_ack_ ||
- should_send_congestion_feedback_) && CanWrite(!kIsRetransmission)) {
- bool full_packet = false;
- if (!queued_control_frames_.empty()) {
- full_packet = !packet_creator_.AddFrame(queued_control_frames_.back());
- if (!full_packet) {
- queued_control_frames_.pop_back();
- }
- } else if (should_send_ack_) {
- full_packet = !packet_creator_.AddFrame(QuicFrame(&outgoing_ack_));
- if (!full_packet) {
- should_send_ack_ = false;
- }
- } else if (should_send_congestion_feedback_) {
- full_packet = !packet_creator_.AddFrame(
- QuicFrame(&outgoing_congestion_feedback_));
- if (!full_packet) {
- should_send_congestion_feedback_ = false;
- }
- }
-
- if (full_packet) {
- SendOrQueueCurrentPacket();
- }
- }
-
- if (flush && packet_creator_.HasPendingFrames()) {
- SendOrQueueCurrentPacket();
- }
-
return !write_blocked_;
}
void QuicConnection::RecordPacketReceived(const QuicPacketHeader& header) {
+ DLOG(INFO) << "Recording received packet: " << header.packet_sequence_number;
QuicPacketSequenceNumber sequence_number = header.packet_sequence_number;
- DCHECK(outgoing_ack_.received_info.IsAwaitingPacket(sequence_number));
- outgoing_ack_.received_info.RecordReceived(sequence_number);
+ DCHECK(IsAwaitingPacket(outgoing_ack_.received_info, sequence_number));
+
+ InsertMissingPacketsBetween(
+ &outgoing_ack_.received_info,
+ max(outgoing_ack_.received_info.largest_observed + 1,
+ peer_least_packet_awaiting_ack_),
+ header.packet_sequence_number);
+
+ if (outgoing_ack_.received_info.largest_observed >
+ header.packet_sequence_number) {
+ // We've gotten one of the out of order packets - remove it from our
+ // "missing packets" list.
+ DVLOG(1) << "Removing " << sequence_number << " from missing list";
+ outgoing_ack_.received_info.missing_packets.erase(sequence_number);
+ }
+ outgoing_ack_.received_info.largest_observed = max(
+ outgoing_ack_.received_info.largest_observed,
+ header.packet_sequence_number);
+ entropy_manager_.RecordReceivedPacketEntropyHash(sequence_number,
+ header.entropy_hash);
}
bool QuicConnection::MaybeRetransmitPacketForRTO(
@@ -688,7 +619,7 @@ bool QuicConnection::MaybeRetransmitPacketForRTO(
if (!ContainsKey(unacked_packets_, sequence_number)) {
DVLOG(2) << "alarm fired for " << sequence_number
- << " but it has been acked or already retransmitted with "
+ << " but it has been acked or already retransmitted with"
<< " different sequence number.";
// So no extra delay is added for this packet.
return true;
@@ -719,25 +650,29 @@ void QuicConnection::RetransmitPacket(
// ignored by MaybeRetransmitPacketForRTO.
DCHECK(unacked_it != unacked_packets_.end());
DCHECK(retransmission_it != retransmission_map_.end());
- UnackedPacket* unacked = unacked_it->second;
+ RetransmittableFrames* unacked = unacked_it->second;
// TODO(ianswett): Never change the sequence number of the connect packet.
// Re-packetize the frames with a new sequence number for retransmission.
// Retransmitted data packets do not use FEC, even when it's enabled.
- PacketPair packetpair = packet_creator_.SerializeAllFrames(unacked->frames);
- RetransmissionInfo retransmission_info(packetpair.first);
+ SerializedPacket serialized_packet =
+ packet_creator_.SerializeAllFrames(unacked->frames());
+ RetransmissionInfo retransmission_info(serialized_packet.sequence_number);
retransmission_info.number_retransmissions =
retransmission_it->second.number_retransmissions + 1;
- retransmission_map_.insert(make_pair(packetpair.first, retransmission_info));
+ retransmission_map_.insert(make_pair(serialized_packet.sequence_number,
+ retransmission_info));
// Remove info with old sequence number.
unacked_packets_.erase(unacked_it);
retransmission_map_.erase(retransmission_it);
DVLOG(1) << "Retransmitting unacked packet " << sequence_number << " as "
- << packetpair.first;
- unacked_packets_.insert(make_pair(packetpair.first, unacked));
- // Make sure if this was our least unacked packet, that we update our
- // outgoing ack. If this wasn't the least unacked, this is a no-op.
- UpdateLeastUnacked(sequence_number);
- SendOrQueuePacket(packetpair.first, packetpair.second, !kForce);
+ << 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));
+ SendOrQueuePacket(serialized_packet.sequence_number,
+ serialized_packet.packet,
+ serialized_packet.entropy_hash);
}
bool QuicConnection::CanWrite(bool is_retransmission) {
@@ -746,7 +681,8 @@ bool QuicConnection::CanWrite(bool is_retransmission) {
if (write_blocked_ || helper_->IsSendAlarmSet()) {
return false;
}
- QuicTime::Delta delay = congestion_manager_.TimeUntilSend(is_retransmission);
+ QuicTime::Delta delay = congestion_manager_.TimeUntilSend(clock_->Now(),
+ is_retransmission);
// If the scheduler requires a delay, then we can not send this packet now.
if (!delay.IsZero() && !delay.IsInfinite()) {
// TODO(pwestin): we need to handle delay.IsInfinite() separately.
@@ -774,8 +710,10 @@ void QuicConnection::MaybeSetupRetransmission(
RetransmissionInfo retransmission_info = it->second;
QuicTime::Delta retransmission_delay =
congestion_manager_.GetRetransmissionDelay(
+ unacked_packets_.size(),
retransmission_info.number_retransmissions);
- retransmission_info.scheduled_time = clock_->Now().Add(retransmission_delay);
+ retransmission_info.scheduled_time =
+ clock_->ApproximateNow().Add(retransmission_delay);
retransmission_timeouts_.push(retransmission_info);
// Do not set the retransmisson alarm if we're already handling the
@@ -784,13 +722,8 @@ void QuicConnection::MaybeSetupRetransmission(
if (!handling_retransmission_timeout_) {
helper_->SetRetransmissionAlarm(retransmission_delay);
}
-
- // The second case should never happen in the real world, but does here
- // because we sometimes send out of order to validate corner cases.
- if (outgoing_ack_.sent_info.least_unacked == 0 ||
- sequence_number < outgoing_ack_.sent_info.least_unacked) {
- outgoing_ack_.sent_info.least_unacked = sequence_number;
- }
+ // TODO(satyamshekhar): restore pacekt reordering with Ian's TODO in
+ // SendStreamData().
}
bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number,
@@ -811,23 +744,30 @@ bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number,
return false;
}
- scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet));
- DLOG(INFO) << "Sending packet : "
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.EncryptPacket(sequence_number, *packet));
+ DLOG(INFO) << "Sending packet number " << sequence_number << " : "
<< (packet->is_fec_packet() ? "FEC " :
(ContainsKey(retransmission_map_, sequence_number) ?
- "data bearing " : " ack only "))
- << "packet " << sequence_number;
+ "data bearing " : " ack only "));
+
DCHECK(encrypted->length() <= kMaxPacketSize)
<< "Packet " << sequence_number << " will not be read; too large: "
<< packet->length() << " " << encrypted->length() << " "
<< outgoing_ack_;
int error;
+ QuicTime now = clock_->Now();
int rv = helper_->WritePacketToWire(*encrypted, &error);
if (rv == -1 && error == ERR_IO_PENDING) {
+ // TODO(satyashekhar): It might be more efficient (fewer system calls), if
+ // all connections share this variable i.e this becomes a part of
+ // PacketWriterInterface.
write_blocked_ = true;
return false;
}
+ time_of_last_sent_packet_ = now;
+ DVLOG(1) << "time of last sent packet: " << now.ToMicroseconds();
// TODO(wtc): Is it correct to continue if the write failed.
// Set the retransmit alarm only when we have sent the packet to the client
@@ -835,44 +775,36 @@ bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number,
// an entry to retransmission_timeout_ every time we attempt a write.
MaybeSetupRetransmission(sequence_number);
- time_of_last_packet_ = clock_->Now();
- DVLOG(1) << "last packet: " << time_of_last_packet_.ToMicroseconds();
-
- congestion_manager_.SentPacket(sequence_number, packet->length(),
+ congestion_manager_.SentPacket(sequence_number, now, packet->length(),
is_retransmission);
delete packet;
return true;
}
-void QuicConnection::SendOrQueueCurrentPacket() {
- QuicFrames retransmittable_frames;
- PacketPair pair = packet_creator_.SerializePacket(&retransmittable_frames);
- const bool should_retransmit = !retransmittable_frames.empty();
- if (should_retransmit) {
- UnackedPacket* unacked = new UnackedPacket(retransmittable_frames);
- for (size_t i = 0; i < retransmittable_frames.size(); ++i) {
- if (retransmittable_frames[i].type == STREAM_FRAME) {
- DCHECK(unacked->data.empty());
- // Make an owned copy of the StringPiece.
- unacked->data =
- retransmittable_frames[i].stream_frame->data.as_string();
- // Ensure the frame's StringPiece points to the owned copy of the data.
- retransmittable_frames[i].stream_frame->data =
- StringPiece(unacked->data);
- }
- }
- unacked_packets_.insert(make_pair(pair.first, unacked));
+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);
+ unacked_packets_.insert(
+ make_pair(serialized_packet.sequence_number,
+ serialized_packet.retransmittable_frames));
// All unacked packets might be retransmitted.
- retransmission_map_.insert(make_pair(pair.first,
- RetransmissionInfo(pair.first)));
+ retransmission_map_.insert(
+ make_pair(serialized_packet.sequence_number,
+ RetransmissionInfo(serialized_packet.sequence_number)));
}
- SendOrQueuePacket(pair.first, pair.second, !kForce);
+ return SendOrQueuePacket(serialized_packet.sequence_number,
+ serialized_packet.packet,
+ serialized_packet.entropy_hash);
}
bool QuicConnection::SendOrQueuePacket(QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
- bool force) {
- if (!WritePacket(sequence_number, packet, force)) {
+ QuicPacketEntropyHash entropy_hash) {
+ entropy_manager_.RecordSentPacketEntropyHash(sequence_number, entropy_hash);
+ if (!WritePacket(sequence_number, packet, !kForce)) {
queued_packets_.push_back(QueuedPacket(sequence_number, packet));
return false;
}
@@ -888,28 +820,39 @@ bool QuicConnection::ShouldSimulateLostPacket() {
*/
}
-void QuicConnection::SendAck() {
- helper_->ClearAckAlarm();
-
- if (!ContainsKey(unacked_packets_, outgoing_ack_.sent_info.least_unacked)) {
- // At some point, all packets were acked, and we set least_unacked to a
- // packet we will not retransmit. Make sure we update it.
- UpdateLeastUnacked(outgoing_ack_.sent_info.least_unacked);
+void QuicConnection::UpdateOutgoingAck() {
+ if (!unacked_packets_.empty()) {
+ outgoing_ack_.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.
+ outgoing_ack_.sent_info.least_unacked =
+ packet_creator_.sequence_number() + 1;
}
+ outgoing_ack_.sent_info.entropy_hash = entropy_manager_.SentEntropyHash(
+ outgoing_ack_.sent_info.least_unacked - 1);
+ outgoing_ack_.received_info.entropy_hash =
+ entropy_manager_.ReceivedEntropyHash(
+ outgoing_ack_.received_info.largest_observed);
+}
- DVLOG(1) << "Sending ack " << outgoing_ack_;
-
- should_send_ack_ = true;
+void QuicConnection::SendAck() {
+ helper_->ClearAckAlarm();
+ UpdateOutgoingAck();
+ DVLOG(1) << "Sending ack: " << outgoing_ack_;
+ // TODO(rch): delay this until the CreateFeedbackFrame
+ // method is invoked. This requires changes SetShouldSendAck
+ // to be a no-arg method, and re-jiggering its implementation.
+ bool send_feedback = false;
if (congestion_manager_.GenerateCongestionFeedback(
&outgoing_congestion_feedback_)) {
DVLOG(1) << "Sending feedback " << outgoing_congestion_feedback_;
- should_send_congestion_feedback_ = true;
- }
- // Try to write immediately if possible.
- if (CanWrite(!kIsRetransmission)) {
- WriteQueuedData(kFlush);
+ send_feedback = true;
}
+
+ packet_generator_.SetShouldSendAck(send_feedback);
}
QuicTime QuicConnection::OnRetransmissionTimeout() {
@@ -926,7 +869,7 @@ QuicTime QuicConnection::OnRetransmissionTimeout() {
!retransmission_timeouts_.empty(); ++i) {
RetransmissionInfo retransmission_info = retransmission_timeouts_.top();
DCHECK(retransmission_info.scheduled_time.IsInitialized());
- if (retransmission_info.scheduled_time > clock_->Now()) {
+ if (retransmission_info.scheduled_time > clock_->ApproximateNow()) {
break;
}
retransmission_timeouts_.pop();
@@ -934,7 +877,7 @@ QuicTime QuicConnection::OnRetransmissionTimeout() {
DLOG(INFO) << "MaybeRetransmitPacketForRTO failed: "
<< "adding an extra delay for "
<< retransmission_info.sequence_number;
- retransmission_info.scheduled_time = clock_->Now().Add(
+ retransmission_info.scheduled_time = clock_->ApproximateNow().Add(
congestion_manager_.DefaultRetransmissionTime());
retransmission_timeouts_.push(retransmission_info);
}
@@ -960,8 +903,9 @@ void QuicConnection::MaybeProcessRevivedPacket() {
char revived_payload[kMaxPacketSize];
size_t len = group->Revive(&revived_header, revived_payload, kMaxPacketSize);
revived_header.public_header.guid = guid_;
- revived_header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- revived_header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ revived_header.public_header.version_flag = false;
+ revived_header.public_header.reset_flag = false;
+ revived_header.fec_flag = false;
revived_header.fec_group = kNoFecOffset;
group_map_.erase(last_header_.fec_group);
delete group;
@@ -971,7 +915,7 @@ void QuicConnection::MaybeProcessRevivedPacket() {
debug_visitor_->OnRevivedPacket(revived_header,
StringPiece(revived_payload, len));
}
- framer_.ProcessRevivedPacket(revived_header,
+ framer_.ProcessRevivedPacket(&revived_header,
StringPiece(revived_payload, len));
}
@@ -991,19 +935,25 @@ void QuicConnection::SendConnectionClose(QuicErrorCode error) {
SendConnectionCloseWithDetails(error, string());
}
-void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error,
- const string& details) {
+void QuicConnection::SendConnectionClosePacket(QuicErrorCode error,
+ const string& details) {
DLOG(INFO) << "Force closing with error " << QuicUtils::ErrorToString(error)
<< " (" << error << ")";
QuicConnectionCloseFrame frame;
frame.error_code = error;
frame.error_details = details;
+ UpdateOutgoingAck();
frame.ack_frame = outgoing_ack_;
- PacketPair packetpair = packet_creator_.CloseConnection(&frame);
- // There's no point in retransmitting/queueing this: we're closing the
- // connection.
- WritePacket(packetpair.first, packetpair.second, kForce);
+ SerializedPacket serialized_packet =
+ packet_creator_.SerializeConnectionClose(&frame);
+ SendOrQueuePacket(serialized_packet.sequence_number, serialized_packet.packet,
+ serialized_packet.entropy_hash);
+}
+
+void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error,
+ const string& details) {
+ SendConnectionClosePacket(error, details);
CloseConnection(error, false);
}
@@ -1014,6 +964,15 @@ void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) {
visitor_->ConnectionClose(error, from_peer);
}
+void QuicConnection::SendGoAway(QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const string& reason) {
+ DLOG(INFO) << "Going away with error " << QuicUtils::ErrorToString(error)
+ << " (" << error << ")";
+ packet_generator_.AddControlFrame(
+ QuicFrame(new QuicGoAwayFrame(error, last_good_stream_id, reason)));
+}
+
void QuicConnection::CloseFecGroupsBefore(
QuicPacketSequenceNumber sequence_number) {
FecGroupMap::iterator it = group_map_.begin();
@@ -1036,14 +995,16 @@ void QuicConnection::CloseFecGroupsBefore(
}
bool QuicConnection::HasQueuedData() const {
- return !queued_packets_.empty() || should_send_ack_ ||
- should_send_congestion_feedback_;
+ return !queued_packets_.empty() || packet_generator_.HasQueuedData();
}
bool QuicConnection::CheckForTimeout() {
- QuicTime now = clock_->Now();
- QuicTime::Delta delta = now.Subtract(time_of_last_packet_);
- DVLOG(1) << "last_packet " << time_of_last_packet_.ToMicroseconds()
+ QuicTime now = clock_->ApproximateNow();
+ QuicTime time_of_last_packet = std::max(time_of_last_received_packet_,
+ time_of_last_sent_packet_);
+
+ QuicTime::Delta delta = now.Subtract(time_of_last_packet);
+ DVLOG(1) << "last packet " << time_of_last_packet.ToMicroseconds()
<< " now:" << now.ToMicroseconds()
<< " delta:" << delta.ToMicroseconds();
if (delta >= timeout_) {
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
index 5e046ad..dd009ae 100644
--- a/net/quic/quic_connection.h
+++ b/net/quic/quic_connection.h
@@ -17,17 +17,21 @@
#define NET_QUIC_QUIC_CONNECTION_H_
#include <list>
+#include <map>
#include <queue>
#include <set>
#include <vector>
#include "base/hash_tables.h"
#include "net/base/ip_endpoint.h"
+#include "net/base/linked_hash_map.h"
#include "net/quic/congestion_control/quic_congestion_manager.h"
#include "net/quic/quic_blocked_writer_interface.h"
#include "net/quic/quic_fec_group.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_packet_entropy_manager.h"
+#include "net/quic/quic_packet_generator.h"
#include "net/quic/quic_protocol.h"
namespace net {
@@ -44,8 +48,6 @@ class QuicConnectionPeer;
class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
public:
- typedef std::set<QuicPacketSequenceNumber> AckedPackets;
-
virtual ~QuicConnectionVisitorInterface() {}
// A simple visitor interface for dealing with data frames. The session
@@ -60,12 +62,15 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
// Called when the stream is reset by the peer.
virtual void OnRstStream(const QuicRstStreamFrame& frame) = 0;
+ // Called when the connection is going away according to the peer.
+ virtual void OnGoAway(const QuicGoAwayFrame& frame) = 0;
+
// Called when the connection is closed either locally by the framer, or
// remotely by the peer.
virtual void ConnectionClose(QuicErrorCode error, bool from_peer) = 0;
// Called when packets are acked by the peer.
- virtual void OnAck(AckedPackets acked_packets) = 0;
+ virtual void OnAck(const SequenceNumberSet& acked_packets) = 0;
// Called when a blocked socket becomes writable. If all pending bytes for
// this visitor are consumed by the connection successfully this should
@@ -170,8 +175,10 @@ class NET_EXPORT_PRIVATE QuicConnectionHelperInterface {
virtual void ClearAckAlarm() = 0;
};
-class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface,
- public QuicBlockedWriterInterface {
+class NET_EXPORT_PRIVATE QuicConnection
+ : public QuicFramerVisitorInterface,
+ public QuicBlockedWriterInterface,
+ public QuicPacketGenerator::DelegateInterface {
public:
// Constructs a new QuicConnection for the specified |guid| and |address|.
// |helper| will be owned by this connection.
@@ -193,8 +200,14 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface,
bool fin);
// Send a stream reset frame to the peer.
virtual void SendRstStream(QuicStreamId id,
- QuicErrorCode error,
- QuicStreamOffset offset);
+ QuicErrorCode error);
+
+ // Sends the connection close packet without affecting the state of the
+ // connection. This should only be called if the session is actively being
+ // destroyed: otherwise call SendConnectionCloseWithDetails instead.
+ virtual void SendConnectionClosePacket(QuicErrorCode error,
+ const std::string& details);
+
// Sends a connection close frame to the peer, and closes the connection by
// calling CloseConnection(notifying the visitor as it does so).
virtual void SendConnectionClose(QuicErrorCode error);
@@ -202,6 +215,9 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface,
const std::string& details);
// Notifies the visitor of the close and marks the connection as disconnected.
void CloseConnection(QuicErrorCode error, bool from_peer);
+ virtual void SendGoAway(QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const std::string& reason);
// Processes an incoming UDP packet (consisting of a QuicEncryptedPacket) from
// the peer. If processing this packet permits a packet to be revived from
@@ -228,11 +244,17 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface,
virtual void OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) OVERRIDE;
virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
virtual void OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) OVERRIDE;
virtual void OnFecData(const QuicFecData& fec) OVERRIDE;
virtual void OnPacketComplete() OVERRIDE;
+ // QuicPacketGenerator::DelegateInterface
+ virtual QuicAckFrame* CreateAckFrame() OVERRIDE;
+ virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() OVERRIDE;
+ virtual bool OnSerializedPacket(const SerializedPacket& packet) OVERRIDE;
+
// Accessors
void set_visitor(QuicConnectionVisitorInterface* visitor) {
visitor_ = visitor;
@@ -286,19 +308,22 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface,
QuicTime OnRetransmissionTimeout();
protected:
- // Serializes then sends or queues the packet currently open.
- void SendOrQueueCurrentPacket();
+ // Deletes all missing packets before least unacked. The connection won't
+ // process any packets with sequence number before |least_unacked| that it
+ // received after this call. Returns true if there were missing packets before
+ // |least_unacked| unacked, false otherwise.
+ bool DontWaitForPacketsBefore(QuicPacketSequenceNumber least_unacked);
// Send a packet to the peer. If |sequence_number| 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. If |force| is true, then the packet
- // will be sent immediately and the send scheduler will not be consulted.
+ // ultimately deleted via WritePacket. Also, it updates the entropy map
+ // corresponding to |sequence_number| using |entropy_hash|.
// TODO(wtc): none of the callers check the return value.
virtual bool SendOrQueuePacket(QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
- bool force);
+ QuicPacketEntropyHash entropy_hash);
// Writes the given packet to socket with the help of helper. Returns true on
// successful write, false otherwise. However, behavior is undefined if
@@ -324,15 +349,6 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface,
// Updates internal state based in incoming_ack.sent_info
void UpdatePacketInformationSentByPeer(const QuicAckFrame& incoming_ack);
- // Utility which sets SetLeastUnacked to least_unacked, and updates the list
- // of non-retransmitting packets accordingly.
- void SetLeastUnacked(QuicPacketSequenceNumber least_unacked);
-
- // Helper to update least unacked. If acked_sequence_number was not the least
- // unacked packet, this is a no-op. If it was the least unacked packet,
- // this finds the new least unacked packet and updates the outgoing ack frame.
- void UpdateLeastUnacked(QuicPacketSequenceNumber acked_sequence_number);
-
QuicConnectionHelperInterface* helper() { return helper_; }
private:
@@ -351,16 +367,6 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface,
QuicPacket* packet;
};
- struct UnackedPacket {
- explicit UnackedPacket(QuicFrames unacked_frames);
- UnackedPacket(QuicFrames unacked_frames, std::string data);
- ~UnackedPacket();
-
- QuicFrames frames;
- // Data referenced by the StringPiece of a QuicStreamFrame.
- std::string data;
- };
-
struct RetransmissionInfo {
explicit RetransmissionInfo(QuicPacketSequenceNumber sequence_number)
: sequence_number(sequence_number),
@@ -386,8 +392,8 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface,
};
typedef std::list<QueuedPacket> QueuedPacketList;
- typedef base::hash_map<QuicPacketSequenceNumber,
- UnackedPacket*> UnackedPacketMap;
+ typedef linked_hash_map<QuicPacketSequenceNumber,
+ RetransmittableFrames*> UnackedPacketMap;
typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap;
typedef base::hash_map<QuicPacketSequenceNumber,
RetransmissionInfo> RetransmissionMap;
@@ -396,24 +402,22 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface,
RetransmissionInfoComparator>
RetransmissionTimeouts;
- static void DeleteEnclosedFrames(UnackedPacket* unacked);
-
// Checks if a packet can be written now, and sets the timer if necessary.
- bool CanWrite(bool is_retransmission);
+ virtual bool CanWrite(bool is_retransmission) OVERRIDE;
void MaybeSetupRetransmission(QuicPacketSequenceNumber sequence_number);
bool IsRetransmission(QuicPacketSequenceNumber sequence_number);
- // Writes as much queued data as possible. The connection must not be
- // blocked when this is called. Will leave queued frames in the PacketCreator
- // if the queued data was not enough to fill a packet and |force_send| is
- // false.
- bool WriteQueuedData(bool flush);
+ // Writes as many queued packets as possible. The connection must not be
+ // blocked when this is called.
+ bool WriteQueuedPackets();
// If a packet can be revived from the current FEC group, then
// revive and process the packet.
void MaybeProcessRevivedPacket();
+ void UpdateOutgoingAck();
+
void MaybeSendAckInResponseToPacket();
// Get the FEC group associate with the last processed packet.
@@ -442,14 +446,19 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface,
QuicPacketHeader last_header_;
std::vector<QuicStreamFrame> last_stream_frames_;
- bool should_send_ack_;
- bool should_send_congestion_feedback_;
QuicAckFrame outgoing_ack_;
QuicCongestionFeedbackFrame outgoing_congestion_feedback_;
- // Track some client state so we can do less bookkeeping
+ // Track some peer state so we can do less bookkeeping
+ // Largest sequence sent by the peer which had an ack frame (latest ack info).
QuicPacketSequenceNumber largest_seen_packet_with_ack_;
+ // Largest sequence number that the peer has observed. Mostly received,
+ // missing in case of truncated acks.
QuicPacketSequenceNumber peer_largest_observed_packet_;
+ // Least sequence number which the peer is still waiting for.
+ QuicPacketSequenceNumber least_packet_awaited_by_peer_;
+ // Least sequence number of the the packet sent by the peer for which it
+ // hasn't received an ack.
QuicPacketSequenceNumber peer_least_packet_awaiting_ack_;
// When new packets are created which may be retransmitted, they are added
@@ -476,22 +485,26 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface,
// unacked_packets_ if they are to be retransmitted.
QueuedPacketList queued_packets_;
- // Pending control frames, besides the ack and congestion control frames.
- QuicFrames queued_control_frames_;
-
// True when the socket becomes unwritable.
bool write_blocked_;
FecGroupMap group_map_;
+ QuicPacketEntropyManager entropy_manager_;
+
QuicConnectionVisitorInterface* visitor_;
QuicConnectionDebugVisitorInterface* debug_visitor_;
QuicPacketCreator packet_creator_;
+ QuicPacketGenerator packet_generator_;
// Network idle time before we kill of this connection.
const QuicTime::Delta timeout_;
- // The time that we got or tried to send a packet for this connection.
- QuicTime time_of_last_packet_;
+
+ // The time that we got a packet for this connection.
+ QuicTime time_of_last_received_packet_;
+
+ // The time that we last sent a packet for this connection.
+ QuicTime time_of_last_sent_packet_;
// Congestion manager which controls the rate the connection sends packets
// as well as collecting and generating congestion feedback.
diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc
index f64f867..ea0637b 100644
--- a/net/quic/quic_connection_helper_test.cc
+++ b/net/quic/quic_connection_helper_test.cc
@@ -37,6 +37,8 @@ class TestConnection : public QuicConnection {
void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) {
QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm);
}
+
+ using QuicConnection::SendOrQueuePacket;
};
class QuicConnectionHelperTest : public ::testing::Test {
@@ -55,7 +57,7 @@ class QuicConnectionHelperTest : public ::testing::Test {
QuicConnectionHelperTest()
: guid_(2),
framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)),
- creator_(guid_, &framer_),
+ creator_(guid_, &framer_, QuicRandom::GetInstance()),
net_log_(BoundNetLog()),
frame_(1, false, 0, kData) {
Initialize();
@@ -100,7 +102,7 @@ class QuicConnectionHelperTest : public ::testing::Test {
helper_.reset(new QuicConnectionHelper(runner_.get(), &clock_,
&random_generator_, socket));
send_algorithm_ = new testing::StrictMock<MockSendAlgorithm>();
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_)).
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _)).
WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
connection_.reset(new TestConnection(guid_, IPEndPoint(), helper_.get()));
connection_->set_visitor(&visitor_);
@@ -115,12 +117,24 @@ class QuicConnectionHelperTest : public ::testing::Test {
return ConstructPacket(header_, QuicFrame(&frame_));
}
+ // Returns a newly created packet to send kData on stream 1.
+ QuicPacket* ConstructRawDataPacket(
+ QuicPacketSequenceNumber sequence_number) {
+ InitializeHeader(sequence_number);
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&frame_));
+ return framer_.ConstructFrameDataPacket(header_, frames).packet;
+ }
+
// Returns a newly created packet to send ack data.
QuicEncryptedPacket* ConstructAckPacket(
QuicPacketSequenceNumber sequence_number) {
InitializeHeader(sequence_number);
QuicAckFrame ack(0, sequence_number);
+ ack.sent_info.entropy_hash = 0;
+ ack.received_info.entropy_hash = 0;
QuicCongestionFeedbackFrame feedback;
feedback.type = kTCP;
@@ -131,8 +145,8 @@ class QuicConnectionHelperTest : public ::testing::Test {
frames.push_back(QuicFrame(&ack));
frames.push_back(QuicFrame(&feedback));
scoped_ptr<QuicPacket> packet(
- framer_.ConstructFrameDataPacket(header_, frames));
- return framer_.EncryptPacket(*packet);
+ framer_.ConstructFrameDataPacket(header_, frames).packet);
+ return framer_.EncryptPacket(header_.packet_sequence_number, *packet);
}
// Returns a newly created packet to send a connection close frame.
@@ -142,7 +156,9 @@ class QuicConnectionHelperTest : public ::testing::Test {
InitializeHeader(sequence_number);
QuicFrames frames;
- QuicAckFrame ack(0, least_waiting);
+ QuicAckFrame ack(0, least_waiting + 1);
+ ack.sent_info.entropy_hash = 0;
+ ack.received_info.entropy_hash = 0;
QuicConnectionCloseFrame close;
close.error_code = QUIC_CONNECTION_TIMED_OUT;
close.ack_frame = ack;
@@ -162,9 +178,12 @@ class QuicConnectionHelperTest : public ::testing::Test {
private:
void InitializeHeader(QuicPacketSequenceNumber sequence_number) {
header_.public_header.guid = guid_;
- header_.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
header_.packet_sequence_number = sequence_number;
- header_.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header_.entropy_flag = false;
+ header_.fec_entropy_flag = false;
+ header_.fec_flag = false;
header_.fec_group = 0;
}
@@ -173,8 +192,8 @@ class QuicConnectionHelperTest : public ::testing::Test {
QuicFrames frames;
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer_.ConstructFrameDataPacket(header_, frames));
- return framer_.EncryptPacket(*packet);
+ framer_.ConstructFrameDataPacket(header_, frames).packet);
+ return framer_.EncryptPacket(header_.packet_sequence_number, *packet);
}
QuicGuid guid_;
@@ -225,9 +244,9 @@ TEST_F(QuicConnectionHelperTest, SetAckAlarm) {
EXPECT_EQ(base::TimeDelta::FromMicroseconds(delta.ToMicroseconds()),
runner_->GetPostedTasks()[1].delay);
- EXPECT_CALL(*send_algorithm_, SentPacket(1, _, false));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false));
runner_->RunNextTask();
- EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.Now());
+ EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.ApproximateNow());
}
TEST_F(QuicConnectionHelperTest, ClearAckAlarm) {
@@ -243,7 +262,7 @@ TEST_F(QuicConnectionHelperTest, ClearAckAlarm) {
// When the AckAlarm actually fires, no ack will be sent.
runner_->RunNextTask();
- EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.Now());
+ EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.ApproximateNow());
}
TEST_F(QuicConnectionHelperTest, ResetAckAlarm) {
@@ -264,14 +283,14 @@ TEST_F(QuicConnectionHelperTest, ResetAckAlarm) {
// The task will execute at delta1, but will not send and ack,
// but it will reschedule itself for delta2
runner_->RunNextTask();
- EXPECT_EQ(QuicTime::Zero().Add(delta1), clock_.Now());
+ EXPECT_EQ(QuicTime::Zero().Add(delta1), clock_.ApproximateNow());
// Verify that the ack alarm task has been re-posted.
ASSERT_EQ(2u, runner_->GetPostedTasks().size());
- EXPECT_CALL(*send_algorithm_, SentPacket(1, _, false));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false));
runner_->RunNextTask();
- EXPECT_EQ(QuicTime::Zero().Add(delta2), clock_.Now());
+ EXPECT_EQ(QuicTime::Zero().Add(delta2), clock_.ApproximateNow());
}
TEST_F(QuicConnectionHelperTest, TestRetransmission) {
@@ -281,17 +300,18 @@ TEST_F(QuicConnectionHelperTest, TestRetransmission) {
QuicTime::Delta kDefaultRetransmissionTime =
QuicTime::Delta::FromMilliseconds(500);
- QuicTime start = clock_.Now();
+ QuicTime start = clock_.ApproximateNow();
- EXPECT_CALL(*send_algorithm_, SentPacket(1, _, false));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false));
// Send a packet.
connection_->SendStreamData(1, kData, 0, false);
- EXPECT_CALL(*send_algorithm_, SentPacket(2, _, true));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, true));
// Since no ack was received, the retransmission alarm will fire and
// retransmit it.
runner_->RunNextTask();
- EXPECT_EQ(kDefaultRetransmissionTime, clock_.Now().Subtract(start));
+ EXPECT_EQ(kDefaultRetransmissionTime,
+ clock_.ApproximateNow().Subtract(start));
EXPECT_TRUE(AtEof());
}
@@ -304,12 +324,13 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) {
EXPECT_EQ(base::TimeDelta::FromMicroseconds(kDefaultTimeoutUs),
runner_->GetPostedTasks().front().delay);
- EXPECT_CALL(*send_algorithm_, SentPacket(1, _, false));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false));
// After we run the next task, we should close the connection.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
runner_->RunNextTask();
- EXPECT_EQ(QuicTime::FromMicroseconds(kDefaultTimeoutUs), clock_.Now());
+ EXPECT_EQ(QuicTime::FromMicroseconds(kDefaultTimeoutUs),
+ clock_.ApproximateNow());
EXPECT_FALSE(connection_->connected());
EXPECT_TRUE(AtEof());
}
@@ -343,12 +364,12 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) {
Initialize();
EXPECT_TRUE(connection_->connected());
- EXPECT_EQ(0u, clock_.Now().ToMicroseconds());
+ EXPECT_EQ(0u, clock_.ApproximateNow().ToMicroseconds());
// When we send a packet, the timeout will change to 5000 + kDefaultTimeout.
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(5000));
- EXPECT_EQ(5000u, clock_.Now().ToMicroseconds());
- EXPECT_CALL(*send_algorithm_, SentPacket(1, _, false));
+ EXPECT_EQ(5000u, clock_.ApproximateNow().ToMicroseconds());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false));
// Send an ack so we don't set the retransmission alarm.
connection_->SendAck();
@@ -357,14 +378,15 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) {
// network event at t=5000. The alarm will reregister.
runner_->RunNextTask();
- EXPECT_EQ(QuicTime::FromMicroseconds(kDefaultTimeoutUs), clock_.Now());
+ EXPECT_EQ(QuicTime::FromMicroseconds(kDefaultTimeoutUs),
+ clock_.ApproximateNow());
EXPECT_TRUE(connection_->connected());
// This time, we should time out.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(2, _, false));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, false));
runner_->RunNextTask();
- EXPECT_EQ(kDefaultTimeoutUs + 5000, clock_.Now().ToMicroseconds());
+ EXPECT_EQ(kDefaultTimeoutUs + 5000, clock_.ApproximateNow().ToMicroseconds());
EXPECT_FALSE(connection_->connected());
EXPECT_TRUE(AtEof());
}
@@ -374,17 +396,18 @@ TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) {
Initialize();
// Test that if we send a packet with a delay, it ends up queued.
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::FromMicroseconds(1)));
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, false)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(1)));
- connection_->SendStreamData(1, kData, 0, false);
- EXPECT_CALL(*send_algorithm_, SentPacket(1, _, false));
+ QuicPacket* packet = ConstructRawDataPacket(1);
+ connection_->SendOrQueuePacket(1, packet, 0);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false));
EXPECT_EQ(1u, connection_->NumQueuedPackets());
// Advance the clock to fire the alarm, and configure the scheduler
// to permit the packet to be sent.
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, false)).WillOnce(
+ testing::Return(QuicTime::Delta::Zero()));
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true));
runner_->RunNextTask();
EXPECT_EQ(0u, connection_->NumQueuedPackets());
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
index 42077ef..6545f2d 100644
--- a/net/quic/quic_connection_logger.cc
+++ b/net/quic/quic_connection_logger.cc
@@ -30,10 +30,13 @@ Value* NetLogQuicPacketHeaderCallback(const QuicPacketHeader* header,
DictionaryValue* dict = new DictionaryValue();
dict->SetString("guid",
base::Uint64ToString(header->public_header.guid));
- dict->SetInteger("public_flags", header->public_header.flags);
+ dict->SetInteger("reset_flag", header->public_header.reset_flag);
+ dict->SetInteger("version_flag", header->public_header.version_flag);
dict->SetString("packet_sequence_number",
base::Uint64ToString(header->packet_sequence_number));
- dict->SetInteger("private_flags", header->private_flags);
+ dict->SetInteger("entropy_flag", header->entropy_flag);
+ dict->SetInteger("fec_flag", header->fec_flag);
+ dict->SetInteger("fec_entropy_flag", header->fec_entropy_flag);
dict->SetInteger("fec_group", header->fec_group);
return dict;
}
@@ -62,8 +65,9 @@ Value* NetLogQuicAckFrameCallback(const QuicAckFrame* frame,
base::Uint64ToString(frame->received_info.largest_observed));
ListValue* missing = new ListValue();
received_info->Set("missing_packets", missing);
- const SequenceSet& missing_packets = frame->received_info.missing_packets;
- for (SequenceSet::const_iterator it = missing_packets.begin();
+ const SequenceNumberSet& missing_packets =
+ frame->received_info.missing_packets;
+ for (SequenceNumberSet::const_iterator it = missing_packets.begin();
it != missing_packets.end(); ++it) {
missing->Append(new base::StringValue(base::Uint64ToString(*it)));
}
@@ -110,7 +114,6 @@ Value* NetLogQuicRstStreamFrameCallback(const QuicRstStreamFrame* frame,
NetLog::LogLevel /* log_level */) {
DictionaryValue* dict = new DictionaryValue();
dict->SetInteger("stream_id", frame->stream_id);
- dict->SetInteger("offset", frame->offset);
dict->SetInteger("error_code", frame->error_code);
dict->SetString("details", frame->error_details);
return dict;
diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc
index 51a9784..a6e4e60 100644
--- a/net/quic/quic_connection_test.cc
+++ b/net/quic/quic_connection_test.cc
@@ -4,13 +4,16 @@
#include "net/quic/quic_connection.h"
+#include "base/bind.h"
#include "net/base/net_errors.h"
#include "net/quic/congestion_control/receive_algorithm_interface.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/crypto/null_encrypter.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_random.h"
#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/mock_random.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/quic_utils.h"
@@ -19,11 +22,14 @@
using base::StringPiece;
using std::map;
+using std::vector;
using testing::_;
using testing::AnyNumber;
using testing::Between;
using testing::ContainerEq;
+using testing::DoAll;
using testing::InSequence;
+using testing::InvokeWithoutArgs;
using testing::Return;
using testing::StrictMock;
using testing::SaveArg;
@@ -35,6 +41,13 @@ namespace {
const char data1[] = "foo";
const char data2[] = "bar";
+const bool kFin = true;
+const bool kForce = true;
+const bool kIsRetransmission = true;
+const bool kEntropyFlag = true;
+const bool kFecEntropyFlag = true;
+const QuicPacketEntropyHash kTestEntropyHash = 76;
+
class TestReceiveAlgorithm : public ReceiveAlgorithmInterface {
public:
explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback)
@@ -65,7 +78,7 @@ class TestConnectionHelper : public QuicConnectionHelperInterface {
: clock_(clock),
random_generator_(random_generator),
retransmission_alarm_(QuicTime::Zero()),
- send_alarm_(QuicTime::Zero()),
+ send_alarm_(QuicTime::FromMilliseconds(-1)),
timeout_alarm_(QuicTime::Zero()),
blocked_(false) {
}
@@ -85,16 +98,20 @@ class TestConnectionHelper : public QuicConnectionHelperInterface {
int* error) OVERRIDE {
QuicFramer framer(QuicDecrypter::Create(kNULL),
QuicEncrypter::Create(kNULL));
- FramerVisitorCapturingAcks visitor;
+ FramerVisitorCapturingFrames visitor;
framer.set_visitor(&visitor);
EXPECT_TRUE(framer.ProcessPacket(packet));
header_ = *visitor.header();
+ frame_count_ = visitor.frame_count();
if (visitor.ack()) {
ack_.reset(new QuicAckFrame(*visitor.ack()));
}
if (visitor.feedback()) {
feedback_.reset(new QuicCongestionFeedbackFrame(*visitor.feedback()));
}
+ if (visitor.stream_frames() != NULL && !visitor.stream_frames()->empty()) {
+ stream_frames_ = *visitor.stream_frames();
+ }
if (blocked_) {
*error = ERR_IO_PENDING;
return -1;
@@ -104,23 +121,23 @@ class TestConnectionHelper : public QuicConnectionHelperInterface {
}
virtual void SetRetransmissionAlarm(QuicTime::Delta delay) OVERRIDE {
- retransmission_alarm_ = clock_->Now().Add(delay);
+ retransmission_alarm_ = clock_->ApproximateNow().Add(delay);
}
virtual void SetSendAlarm(QuicTime::Delta delay) OVERRIDE {
- send_alarm_ = clock_->Now().Add(delay);
+ send_alarm_ = clock_->ApproximateNow().Add(delay);
}
virtual void SetTimeoutAlarm(QuicTime::Delta delay) OVERRIDE {
- timeout_alarm_ = clock_->Now().Add(delay);
+ timeout_alarm_ = clock_->ApproximateNow().Add(delay);
}
virtual bool IsSendAlarmSet() OVERRIDE {
- return send_alarm_ > clock_->Now();
+ return send_alarm_ >= clock_->ApproximateNow();
}
virtual void UnregisterSendAlarmIfRegistered() OVERRIDE {
- send_alarm_ = QuicTime::Zero();
+ send_alarm_ = QuicTime::FromMilliseconds(-1);
}
virtual void SetAckAlarm(QuicTime::Delta delay) OVERRIDE {}
@@ -134,10 +151,14 @@ class TestConnectionHelper : public QuicConnectionHelperInterface {
QuicPacketHeader* header() { return &header_; }
+ size_t frame_count() { return frame_count_; }
+
QuicAckFrame* ack() { return ack_.get(); }
QuicCongestionFeedbackFrame* feedback() { return feedback_.get(); }
+ const vector<QuicStreamFrame>* stream_frames() { return &stream_frames_; }
+
void set_blocked(bool blocked) { blocked_ = blocked; }
private:
@@ -147,8 +168,10 @@ class TestConnectionHelper : public QuicConnectionHelperInterface {
QuicTime send_alarm_;
QuicTime timeout_alarm_;
QuicPacketHeader header_;
+ size_t frame_count_;
scoped_ptr<QuicAckFrame> ack_;
scoped_ptr<QuicCongestionFeedbackFrame> feedback_;
+ vector<QuicStreamFrame> stream_frames_;
bool blocked_;
DISALLOW_COPY_AND_ASSIGN(TestConnectionHelper);
@@ -174,7 +197,16 @@ class TestConnection : public QuicConnection {
QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm);
}
+ QuicConsumedData SendStreamData1() {
+ return SendStreamData(1u, "food", 0, !kFin);
+ }
+
+ QuicConsumedData SendStreamData2() {
+ return SendStreamData(2u, "food2", 0, !kFin);
+ }
+
using QuicConnection::SendOrQueuePacket;
+ using QuicConnection::DontWaitForPacketsBefore;
private:
DISALLOW_COPY_AND_ASSIGN(TestConnection);
@@ -185,7 +217,7 @@ class QuicConnectionTest : public ::testing::Test {
QuicConnectionTest()
: guid_(42),
framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)),
- creator_(guid_, &framer_),
+ creator_(guid_, &framer_, QuicRandom::GetInstance()),
send_algorithm_(new StrictMock<MockSendAlgorithm>),
helper_(new TestConnectionHelper(&clock_, &random_generator_)),
connection_(guid_, IPEndPoint(), helper_.get()),
@@ -196,11 +228,11 @@ class QuicConnectionTest : public ::testing::Test {
connection_.SetSendAlgorithm(send_algorithm_);
// Simplify tests by not sending feedback unless specifically configured.
SetFeedback(NULL);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_)).WillRepeatedly(Return(
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _)).WillRepeatedly(Return(
QuicTime::Delta::Zero()));
EXPECT_CALL(*receive_algorithm_,
RecordIncomingPacket(_, _, _, _)).Times(AnyNumber());
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber());
}
QuicAckFrame* outgoing_ack() {
@@ -222,7 +254,18 @@ class QuicConnectionTest : public ::testing::Test {
void ProcessPacket(QuicPacketSequenceNumber number) {
EXPECT_CALL(visitor_, OnPacket(_, _, _, _))
.WillOnce(Return(accept_packet_));
- ProcessDataPacket(number, 0);
+ ProcessDataPacket(number, 0, !kEntropyFlag);
+ }
+
+ QuicPacketEntropyHash ProcessFramePacket(QuicFrame frame) {
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ SerializedPacket serialized_packet = creator_.SerializeAllFrames(frames);
+ scoped_ptr<QuicPacket> packet(serialized_packet.packet);
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.EncryptPacket(serialized_packet.sequence_number, *packet));
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+ return serialized_packet.entropy_hash;
}
void ProcessFecProtectedPacket(QuicPacketSequenceNumber number,
@@ -234,39 +277,59 @@ class QuicConnectionTest : public ::testing::Test {
EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(
Return(accept_packet_));
}
- ProcessDataPacket(number, 1);
+ ProcessDataPacket(number, 1, !kEntropyFlag);
}
void ProcessDataPacket(QuicPacketSequenceNumber number,
- QuicFecGroupNumber fec_group) {
- scoped_ptr<QuicPacket> packet(ConstructDataPacket(number, fec_group));
- scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet));
+ QuicFecGroupNumber fec_group,
+ bool entropy_flag) {
+ scoped_ptr<QuicPacket> packet(ConstructDataPacket(number, fec_group,
+ entropy_flag));
+ scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(number,
+ *packet));
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
}
void ProcessClosePacket(QuicPacketSequenceNumber number,
QuicFecGroupNumber fec_group) {
scoped_ptr<QuicPacket> packet(ConstructClosePacket(number, fec_group));
- scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet));
+ scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(number,
+ *packet));
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
}
+ void 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_, OnPacket(_, _, _, _)).WillOnce(Return(accept_packet_))
+ .RetiresOnSaturation();
+ ProcessDataPacket(number, 1, entropy_flag);
+ }
+
// Sends an FEC packet that covers the packets that would have been sent.
void ProcessFecPacket(QuicPacketSequenceNumber number,
QuicPacketSequenceNumber min_protected_packet,
- bool expect_revival) {
+ bool expect_revival,
+ bool fec_entropy_flag) {
if (expect_revival) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(
- Return(accept_packet_));
+ EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(DoAll(
+ SaveArg<2>(&revived_header_), Return(accept_packet_)));
}
// Construct the decrypted data packet so we can compute the correct
// redundancy.
- scoped_ptr<QuicPacket> data_packet(ConstructDataPacket(number, 1));
+ scoped_ptr<QuicPacket> data_packet(ConstructDataPacket(number, 1,
+ !kEntropyFlag));
header_.public_header.guid = guid_;
- header_.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header_.private_flags = PACKET_PRIVATE_FLAGS_FEC;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
+ header_.entropy_flag = kEntropyFlag;
+ header_.fec_flag = true;
+ header_.fec_entropy_flag = fec_entropy_flag;
header_.packet_sequence_number = number;
header_.fec_group = min_protected_packet;
QuicFecData fec_data;
@@ -282,9 +345,9 @@ class QuicConnectionTest : public ::testing::Test {
}
fec_data.redundancy = data_packet->FecProtectedData();
scoped_ptr<QuicPacket> fec_packet(
- framer_.ConstructFecPacket(header_, fec_data));
+ framer_.ConstructFecPacket(header_, fec_data).packet);
scoped_ptr<QuicEncryptedPacket> encrypted(
- framer_.EncryptPacket(*fec_packet));
+ framer_.EncryptPacket(number, *fec_packet));
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
}
@@ -292,46 +355,50 @@ class QuicConnectionTest : public ::testing::Test {
void SendStreamDataToPeer(QuicStreamId id, StringPiece data,
QuicStreamOffset offset, bool fin,
QuicPacketSequenceNumber* last_packet) {
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
connection_.SendStreamData(id, data, offset, fin);
if (last_packet != NULL) {
*last_packet =
QuicConnectionPeer::GetPacketCreator(&connection_)->sequence_number();
}
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber());
}
void SendAckPacketToPeer() {
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1);
connection_.SendAck();
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber());
}
- void ProcessAckPacket(QuicAckFrame* frame) {
- QuicFrames frames;
- frames.push_back(QuicFrame(frame));
- PacketPair pair = creator_.SerializeAllFrames(frames);
- scoped_ptr<QuicPacket> packet(pair.second);
- scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet));
- connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+ QuicPacketEntropyHash ProcessAckPacket(QuicAckFrame* frame) {
+ return ProcessFramePacket(QuicFrame(frame));
+ }
+
+ QuicPacketEntropyHash ProcessGoAwayPacket(QuicGoAwayFrame* frame) {
+ return ProcessFramePacket(QuicFrame(frame));
}
bool IsMissing(QuicPacketSequenceNumber number) {
- return outgoing_ack()->received_info.IsAwaitingPacket(number);
+ return IsAwaitingPacket(outgoing_ack()->received_info, number);
}
QuicPacket* ConstructDataPacket(QuicPacketSequenceNumber number,
- QuicFecGroupNumber fec_group) {
+ QuicFecGroupNumber fec_group,
+ bool entropy_flag) {
header_.public_header.guid = guid_;
- header_.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header_.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
+ header_.entropy_flag = entropy_flag;
+ header_.fec_flag = false;
+ header_.fec_entropy_flag = false;
header_.packet_sequence_number = number;
header_.fec_group = fec_group;
QuicFrames frames;
QuicFrame frame(&frame1_);
frames.push_back(frame);
- QuicPacket* packet = framer_.ConstructFrameDataPacket(header_, frames);
+ QuicPacket* packet =
+ framer_.ConstructFrameDataPacket(header_, frames).packet;
EXPECT_TRUE(packet != NULL);
return packet;
}
@@ -340,17 +407,22 @@ class QuicConnectionTest : public ::testing::Test {
QuicFecGroupNumber fec_group) {
header_.public_header.guid = guid_;
header_.packet_sequence_number = number;
- header_.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
+ header_.entropy_flag = false;
+ header_.fec_flag = false;
+ header_.fec_entropy_flag = false;
header_.fec_group = fec_group;
QuicConnectionCloseFrame qccf;
- qccf.error_code = QUIC_CLIENT_GOING_AWAY;
+ qccf.error_code = QUIC_PEER_GOING_AWAY;
qccf.ack_frame = QuicAckFrame(0, 1);
QuicFrames frames;
QuicFrame frame(&qccf);
frames.push_back(frame);
- QuicPacket* packet = framer_.ConstructFrameDataPacket(header_, frames);
+ QuicPacket* packet =
+ framer_.ConstructFrameDataPacket(header_, frames).packet;
EXPECT_TRUE(packet != NULL);
return packet;
}
@@ -373,6 +445,7 @@ class QuicConnectionTest : public ::testing::Test {
testing::StrictMock<MockConnectionVisitor> visitor_;
QuicPacketHeader header_;
+ QuicPacketHeader revived_header_;
QuicStreamFrame frame1_;
QuicStreamFrame frame2_;
bool accept_packet_;
@@ -432,7 +505,7 @@ TEST_F(QuicConnectionTest, DuplicatePacket) {
// Send packet 3 again, but do not set the expectation that
// the visitor OnPacket() will be called.
- ProcessDataPacket(3, 0);
+ ProcessDataPacket(3, 0, !kEntropyFlag);
EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed);
EXPECT_TRUE(IsMissing(2));
EXPECT_TRUE(IsMissing(1));
@@ -469,7 +542,7 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
TEST_F(QuicConnectionTest, RejectPacketTooFarOut) {
// Call ProcessDataPacket rather than ProcessPacket, as we should not get a
// packet call to the visitor.
- ProcessDataPacket(6000, 0);
+ ProcessDataPacket(6000, 0, !kEntropyFlag);
SendAckPacketToPeer(); // Packet 2
EXPECT_EQ(0u, outgoing_ack()->received_info.largest_observed);
@@ -478,26 +551,35 @@ TEST_F(QuicConnectionTest, RejectPacketTooFarOut) {
TEST_F(QuicConnectionTest, TruncatedAck) {
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) {
- SendStreamDataToPeer(1, "foo", i * 3, false, NULL);
+ SendStreamDataToPeer(1, "foo", i * 3, !kFin, NULL);
}
QuicAckFrame frame(0, 1);
- frame.received_info.RecordReceived(193);
+ frame.received_info.largest_observed = 192;
+ InsertMissingPacketsBetween(&frame.received_info, 1, 192);
+ frame.received_info.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 192) ^
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 191);
+
ProcessAckPacket(&frame);
EXPECT_TRUE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_));
- frame.received_info.missing_packets.erase(192);
+ frame.received_info.missing_packets.erase(191);
+ frame.received_info.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 192) ^
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 190);
ProcessAckPacket(&frame);
EXPECT_FALSE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_));
}
TEST_F(QuicConnectionTest, LeastUnackedLower) {
- SendStreamDataToPeer(1, "foo", 0, false, NULL);
- SendStreamDataToPeer(1, "bar", 3, false, NULL);
- SendStreamDataToPeer(1, "eep", 6, false, NULL);
+ SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
+ SendStreamDataToPeer(1, "bar", 3, !kFin, NULL);
+ SendStreamDataToPeer(1, "eep", 6, !kFin, NULL);
// Start out saying the least unacked is 2
creator_.set_sequence_number(5);
@@ -514,19 +596,21 @@ TEST_F(QuicConnectionTest, LeastUnackedLower) {
// Now claim it's one, but set the ordering so it was sent "after" the first
// one. This should cause a connection error.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
creator_.set_sequence_number(7);
ProcessAckPacket(&frame2);
}
TEST_F(QuicConnectionTest, LargestObservedLower) {
- SendStreamDataToPeer(1, "foo", 0, false, NULL);
- SendStreamDataToPeer(1, "bar", 3, false, NULL);
- SendStreamDataToPeer(1, "eep", 6, false, NULL);
+ SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
+ SendStreamDataToPeer(1, "bar", 3, !kFin, NULL);
+ SendStreamDataToPeer(1, "eep", 6, !kFin, NULL);
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2);
// Start out saying the largest observed is 2.
QuicAckFrame frame(2, 0);
+ frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
+ &connection_, 2);
EXPECT_CALL(visitor_, OnAck(_));
ProcessAckPacket(&frame);
@@ -538,7 +622,7 @@ TEST_F(QuicConnectionTest, LargestObservedLower) {
TEST_F(QuicConnectionTest, LeastUnackedGreaterThanPacketSequenceNumber) {
EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
// Create an ack with least_unacked is 2 in packet number 1.
creator_.set_sequence_number(0);
QuicAckFrame frame(0, 2);
@@ -546,13 +630,13 @@ TEST_F(QuicConnectionTest, LeastUnackedGreaterThanPacketSequenceNumber) {
}
TEST_F(QuicConnectionTest,
- DISABLED_NackSequenceNumberGreaterThanLargestReceived) {
- SendStreamDataToPeer(1, "foo", 0, false, NULL);
- SendStreamDataToPeer(1, "bar", 3, false, NULL);
- SendStreamDataToPeer(1, "eep", 6, false, NULL);
+ NackSequenceNumberGreaterThanLargestReceived) {
+ SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
+ SendStreamDataToPeer(1, "bar", 3, !kFin, NULL);
+ SendStreamDataToPeer(1, "eep", 6, !kFin, NULL);
EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
QuicAckFrame frame(0, 1);
frame.received_info.missing_packets.insert(3);
ProcessAckPacket(&frame);
@@ -561,7 +645,7 @@ TEST_F(QuicConnectionTest,
TEST_F(QuicConnectionTest, AckUnsentData) {
// Ack a packet which has not been sent.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
QuicAckFrame frame(1, 0);
ProcessAckPacket(&frame);
}
@@ -574,10 +658,17 @@ TEST_F(QuicConnectionTest, AckAll) {
ProcessAckPacket(&frame1);
}
+TEST_F(QuicConnectionTest, DontWaitForPacketsBefore) {
+ ProcessPacket(2);
+ ProcessPacket(7);
+ EXPECT_TRUE(connection_.DontWaitForPacketsBefore(4));
+ EXPECT_EQ(3u, outgoing_ack()->received_info.missing_packets.size());
+}
+
TEST_F(QuicConnectionTest, BasicSending) {
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(6);
QuicPacketSequenceNumber last_packet;
- SendStreamDataToPeer(1, "foo", 0, false, &last_packet); // Packet 1
+ SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
EXPECT_EQ(1u, last_packet);
SendAckPacketToPeer(); // Packet 2
@@ -586,17 +677,19 @@ TEST_F(QuicConnectionTest, BasicSending) {
SendAckPacketToPeer(); // Packet 3
EXPECT_EQ(1u, last_ack()->sent_info.least_unacked);
- SendStreamDataToPeer(1u, "bar", 3, false, &last_packet); // Packet 4
+ SendStreamDataToPeer(1u, "bar", 3, !kFin, &last_packet); // Packet 4
EXPECT_EQ(4u, last_packet);
SendAckPacketToPeer(); // Packet 5
EXPECT_EQ(1u, last_ack()->sent_info.least_unacked);
- QuicConnectionVisitorInterface::AckedPackets expected_acks;
+ SequenceNumberSet expected_acks;
expected_acks.insert(1);
// Client acks up to packet 3
EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
QuicAckFrame frame(3, 0);
+ frame.received_info.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 3);
ProcessAckPacket(&frame);
SendAckPacketToPeer(); // Packet 6
@@ -610,6 +703,8 @@ TEST_F(QuicConnectionTest, BasicSending) {
// Client acks up to packet 4, the last packet
EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
QuicAckFrame frame2(6, 0);
+ frame2.received_info.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 6);
ProcessAckPacket(&frame2); // Even parity triggers ack packet 7
// The least packet awaiting ack should now be 7
@@ -620,7 +715,7 @@ TEST_F(QuicConnectionTest, BasicSending) {
EXPECT_EQ(8u, last_ack()->sent_info.least_unacked);
// But if we send more data it should.
- SendStreamDataToPeer(1, "eep", 6, false, &last_packet); // Packet 9
+ SendStreamDataToPeer(1, "eep", 6, !kFin, &last_packet); // Packet 9
EXPECT_EQ(9u, last_packet);
SendAckPacketToPeer(); // Packet10
EXPECT_EQ(9u, last_ack()->sent_info.least_unacked);
@@ -635,8 +730,8 @@ TEST_F(QuicConnectionTest, FECSending) {
connection_.options()->max_packets_per_fec_group = 2;
// Send 4 data packets and 2 FEC packets.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(6);
- connection_.SendStreamData(1, "food", 0, false);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(6);
+ connection_.SendStreamData(1, "food", 0, !kFin);
// Expect the FEC group to be closed after SendStreamData.
EXPECT_FALSE(creator_.ShouldSendFec(true));
}
@@ -651,26 +746,110 @@ TEST_F(QuicConnectionTest, FECQueueing) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
helper_->set_blocked(true);
- connection_.SendStreamData(1, "food", 0, false);
+ connection_.SendStreamData(1, "food", 0, !kFin);
EXPECT_FALSE(creator_.ShouldSendFec(true));
// Expect the first data packet and the fec packet to be queued.
EXPECT_EQ(2u, connection_.NumQueuedPackets());
}
+TEST_F(QuicConnectionTest, FramePacking) {
+ // Block the connection.
+ helper_->SetSendAlarm(QuicTime::Delta::FromSeconds(1));
+
+ // Send an ack and two stream frames in 1 packet by queueing them.
+ connection_.SendAck();
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData1)),
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData2)),
+ Return(true)));
+
+ // Unblock the connection.
+ helper_->UnregisterSendAlarmIfRegistered();
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, !kIsRetransmission))
+ .Times(1);
+ connection_.OnCanWrite();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's an ack and two stream frames from
+ // two different streams.
+ EXPECT_EQ(3u, helper_->frame_count());
+ EXPECT_TRUE(helper_->ack());
+ EXPECT_EQ(2u, helper_->stream_frames()->size());
+ EXPECT_EQ(1u, (*helper_->stream_frames())[0].stream_id);
+ EXPECT_EQ(2u, (*helper_->stream_frames())[1].stream_id);
+}
+
+TEST_F(QuicConnectionTest, FramePackingFEC) {
+ // Enable fec.
+ connection_.options()->max_packets_per_fec_group = 6;
+ // Block the connection.
+ helper_->SetSendAlarm(QuicTime::Delta::FromSeconds(1));
+
+ // Send an ack and two stream frames in 1 packet by queueing them.
+ connection_.SendAck();
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData1)),
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData2)),
+ Return(true)));
+
+ // Unblock the connection.
+ helper_->UnregisterSendAlarmIfRegistered();
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, !kIsRetransmission))
+ .Times(2);
+ connection_.OnCanWrite();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's in an fec group.
+ EXPECT_EQ(1u, helper_->header()->fec_group);
+ EXPECT_EQ(0u, helper_->frame_count());
+}
+
+TEST_F(QuicConnectionTest, OnCanWrite) {
+ // Visitor's OnCanWill send data, but will return false.
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData1)),
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData2)),
+ Return(false)));
+
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, !kIsRetransmission)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::Zero()));
+
+ // Unblock the connection.
+ connection_.OnCanWrite();
+ // Parse the last packet and ensure it's the two stream frames from
+ // two different streams.
+ EXPECT_EQ(2u, helper_->frame_count());
+ EXPECT_EQ(2u, helper_->stream_frames()->size());
+ EXPECT_EQ(1u, (*helper_->stream_frames())[0].stream_id);
+ EXPECT_EQ(2u, (*helper_->stream_frames())[1].stream_id);
+}
+
TEST_F(QuicConnectionTest, RetransmitOnNack) {
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
QuicPacketSequenceNumber last_packet;
- SendStreamDataToPeer(1, "foo", 0, false, &last_packet); // Packet 1
- SendStreamDataToPeer(1, "foos", 3, false, &last_packet); // Packet 2
- SendStreamDataToPeer(1, "fooos", 7, false, &last_packet); // Packet 3
+ SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
+ SendStreamDataToPeer(1, "foos", 3, !kFin, &last_packet); // Packet 2
+ SendStreamDataToPeer(1, "fooos", 7, !kFin, &last_packet); // Packet 3
- QuicConnectionVisitorInterface::AckedPackets expected_acks;
+ SequenceNumberSet expected_acks;
expected_acks.insert(1);
EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
// Client acks one but not two or three. Right now we only retransmit on
// explicit nack, so it should not trigger a retransimission.
QuicAckFrame ack_one(1, 0);
+ ack_one.received_info.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
ProcessAckPacket(&ack_one);
ProcessAckPacket(&ack_one);
ProcessAckPacket(&ack_one);
@@ -683,37 +862,47 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) {
// change.
QuicAckFrame nack_two(3, 0);
nack_two.received_info.missing_packets.insert(2);
+ nack_two.received_info.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
ProcessAckPacket(&nack_two);
ProcessAckPacket(&nack_two);
// The third nack should trigger a retransimission.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 37, true)).Times(1);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, 37, kIsRetransmission))
+ .Times(1);
ProcessAckPacket(&nack_two);
}
TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) {
+ EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
QuicPacketSequenceNumber largest_observed;
QuicByteCount packet_size;
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, false)).WillOnce(DoAll(
- SaveArg<0>(&largest_observed), SaveArg<1>(&packet_size)));
- connection_.SendStreamData(1, "foo", 0, false);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, !kIsRetransmission))
+ .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size)));
+ connection_.SendStreamData(1, "foo", 0, !kFin);
QuicAckFrame frame(1, largest_observed);
frame.received_info.missing_packets.insert(largest_observed);
+ frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
+ &connection_, largest_observed - 1);
ProcessAckPacket(&frame);
// Second udp packet will force an ack frame.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, false));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, !kIsRetransmission));
ProcessAckPacket(&frame);
// Third nack should retransmit the largest observed packet.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, packet_size, true));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, packet_size,
+ kIsRetransmission));
ProcessAckPacket(&frame);
}
TEST_F(QuicConnectionTest, LimitPacketsPerNack) {
EXPECT_CALL(*send_algorithm_, OnIncomingAck(12, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
int offset = 0;
// Send packets 1 to 12
for (int i = 0; i < 12; ++i) {
- SendStreamDataToPeer(1, "foo", offset, false, NULL);
+ SendStreamDataToPeer(1, "foo", offset, !kFin, NULL);
offset += 3;
}
@@ -723,47 +912,55 @@ TEST_F(QuicConnectionTest, LimitPacketsPerNack) {
nack.received_info.missing_packets.insert(i);
}
- QuicConnectionVisitorInterface::AckedPackets expected_acks;
+ 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);
// The second call will trigger an ack.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1);
ProcessAckPacket(&nack);
// The third call should trigger retransmitting 10 packets.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(10);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(10);
ProcessAckPacket(&nack);
// The fourth call should trigger retransmitting the 11th packet and an ack.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(2);
ProcessAckPacket(&nack);
}
// Test sending multiple acks from the connection to the session.
TEST_F(QuicConnectionTest, MultipleAcks) {
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(6);
+ EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
QuicPacketSequenceNumber last_packet;
- SendStreamDataToPeer(1u, "foo", 0, false, &last_packet); // Packet 1
+ SendStreamDataToPeer(1u, "foo", 0, !kFin, &last_packet); // Packet 1
EXPECT_EQ(1u, last_packet);
- SendStreamDataToPeer(3u, "foo", 0, false, &last_packet); // Packet 2
+ SendStreamDataToPeer(3u, "foo", 0, !kFin, &last_packet); // Packet 2
EXPECT_EQ(2u, last_packet);
SendAckPacketToPeer(); // Packet 3
- SendStreamDataToPeer(5u, "foo", 0, false, &last_packet); // Packet 4
+ SendStreamDataToPeer(5u, "foo", 0, !kFin, &last_packet); // Packet 4
EXPECT_EQ(4u, last_packet);
- SendStreamDataToPeer(1u, "foo", 3, false, &last_packet); // Packet 5
+ SendStreamDataToPeer(1u, "foo", 3, !kFin, &last_packet); // Packet 5
EXPECT_EQ(5u, last_packet);
- SendStreamDataToPeer(3u, "foo", 3, false, &last_packet); // Packet 6
+ SendStreamDataToPeer(3u, "foo", 3, !kFin, &last_packet); // Packet 6
EXPECT_EQ(6u, last_packet);
// Client will ack packets 1, [!2], 3, 4, 5
QuicAckFrame frame1(5, 0);
frame1.received_info.missing_packets.insert(2);
+ frame1.received_info.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 5) ^
+ 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.
- QuicConnectionVisitorInterface::AckedPackets expected_acks;
+ SequenceNumberSet expected_acks;
expected_acks.insert(1);
expected_acks.insert(4);
expected_acks.insert(5);
@@ -773,6 +970,8 @@ TEST_F(QuicConnectionTest, MultipleAcks) {
// Now the client implicitly acks 2, and explicitly acks 6
QuicAckFrame frame2(6, 0);
+ frame2.received_info.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 6);
expected_acks.clear();
// Both acks should be passed up.
expected_acks.insert(2);
@@ -784,110 +983,122 @@ TEST_F(QuicConnectionTest, MultipleAcks) {
TEST_F(QuicConnectionTest, DontLatchUnackedPacket) {
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
- SendStreamDataToPeer(1, "foo", 0, false, NULL); // Packet 1;
+ SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); // Packet 1;
SendAckPacketToPeer(); // Packet 2
- // This sets least unacked to 2, the ack packet.
- QuicConnectionVisitorInterface::AckedPackets expected_acks;
+ // 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);
// Client acks packet 1
EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
QuicAckFrame frame(1, 0);
+ frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
+ &connection_, 1);
ProcessAckPacket(&frame);
- // Verify that our internal state has least-unacked as 2.
- QuicAckFrame* outgoing_ack = QuicConnectionPeer::GetOutgoingAck(&connection_);
- EXPECT_EQ(2u, outgoing_ack->sent_info.least_unacked);
+ // Verify that our internal state has least-unacked as 3.
+ EXPECT_EQ(3u, outgoing_ack()->sent_info.least_unacked);
// When we send an ack, we make sure our least-unacked makes sense. In this
// case since we're not waiting on an ack for 2 and all packets are acked, we
// set it to 3.
SendAckPacketToPeer(); // Packet 3
- EXPECT_EQ(3u, outgoing_ack->sent_info.least_unacked);
+ // Since this was an ack packet, we set least_unacked to 4.
+ EXPECT_EQ(4u, outgoing_ack()->sent_info.least_unacked);
+ // Check that the outgoing ack had its sequence number as least_unacked.
EXPECT_EQ(3u, last_ack()->sent_info.least_unacked);
+
+ SendStreamDataToPeer(1, "bar", 3, false, NULL); // Packet 4
+ EXPECT_EQ(4u, outgoing_ack()->sent_info.least_unacked);
+ SendAckPacketToPeer(); // Packet 5
+ EXPECT_EQ(4u, last_ack()->sent_info.least_unacked);
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) {
// Don't send missing packet 1.
- ProcessFecPacket(2, 1, true);
+ ProcessFecPacket(2, 1, true, !kFecEntropyFlag);
+ EXPECT_FALSE(revived_header_.entropy_flag);
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) {
- ProcessFecProtectedPacket(1, false);
+ ProcessFecProtectedPacket(1, false, kEntropyFlag);
// Don't send missing packet 2.
- ProcessFecPacket(3, 1, true);
+ ProcessFecPacket(3, 1, true, !kFecEntropyFlag);
+ EXPECT_TRUE(revived_header_.entropy_flag);
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) {
- ProcessFecProtectedPacket(1, false);
+ ProcessFecProtectedPacket(1, false, !kEntropyFlag);
// Don't send missing packet 2.
- ProcessFecProtectedPacket(3, false);
- ProcessFecPacket(4, 1, true);
+ ProcessFecProtectedPacket(3, false, !kEntropyFlag);
+ ProcessFecPacket(4, 1, true, kFecEntropyFlag);
+ EXPECT_TRUE(revived_header_.entropy_flag);
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
// Don't send missing packet 1.
- ProcessFecPacket(3, 1, false); // out of order
- ProcessFecProtectedPacket(2, true);
+ ProcessFecPacket(3, 1, false, !kFecEntropyFlag);
+ // out of order
+ ProcessFecProtectedPacket(2, true, !kEntropyFlag);
+ EXPECT_FALSE(revived_header_.entropy_flag);
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) {
- ProcessFecProtectedPacket(1, false);
+ ProcessFecProtectedPacket(1, false, !kEntropyFlag);
// Don't send missing packet 2.
- ProcessFecPacket(6, 1, false);
- ProcessFecProtectedPacket(3, false);
- ProcessFecProtectedPacket(4, false);
- ProcessFecProtectedPacket(5, true);
+ ProcessFecPacket(6, 1, false, kFecEntropyFlag);
+ ProcessFecProtectedPacket(3, false, kEntropyFlag);
+ ProcessFecProtectedPacket(4, false, kEntropyFlag);
+ ProcessFecProtectedPacket(5, true, !kEntropyFlag);
+ EXPECT_TRUE(revived_header_.entropy_flag);
}
TEST_F(QuicConnectionTest, TestRetransmit) {
- // TODO(rch): make this work
- // FLAGS_fake_packet_loss_percentage = 100;
const QuicTime::Delta kDefaultRetransmissionTime =
QuicTime::Delta::FromMilliseconds(500);
- QuicTime default_retransmission_time = clock_.Now().Add(
+ QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
kDefaultRetransmissionTime);
-
- QuicAckFrame* outgoing_ack = QuicConnectionPeer::GetOutgoingAck(&connection_);
- SendStreamDataToPeer(1, "foo", 0, false, NULL);
- EXPECT_EQ(1u, outgoing_ack->sent_info.least_unacked);
+ SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
+ EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
EXPECT_EQ(1u, last_header()->packet_sequence_number);
EXPECT_EQ(default_retransmission_time, helper_->retransmission_alarm());
// Simulate the retransimission alarm firing
clock_.AdvanceTime(kDefaultRetransmissionTime);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
connection_.RetransmitPacket(1);
EXPECT_EQ(2u, last_header()->packet_sequence_number);
- EXPECT_EQ(2u, outgoing_ack->sent_info.least_unacked);
+ EXPECT_EQ(2u, outgoing_ack()->sent_info.least_unacked);
}
TEST_F(QuicConnectionTest, TestRetransmitOrder) {
QuicByteCount first_packet_size;
- EXPECT_CALL(*send_algorithm_, SentPacket(_,_,_)).WillOnce(
- SaveArg<1>(&first_packet_size));
- connection_.SendStreamData(1, "first_packet", 0, false);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).WillOnce(
+ SaveArg<2>(&first_packet_size));
+ connection_.SendStreamData(1, "first_packet", 0, !kFin);
QuicByteCount second_packet_size;
- EXPECT_CALL(*send_algorithm_, SentPacket(_,_,_)).WillOnce(
- SaveArg<1>(&second_packet_size));
- connection_.SendStreamData(1, "second_packet", 12, false);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).WillOnce(
+ SaveArg<2>(&second_packet_size));
+ connection_.SendStreamData(1, "second_packet", 12, !kFin);
EXPECT_NE(first_packet_size, second_packet_size);
// Advance the clock by huge time to make sure packets will be retransmitted.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
{
InSequence s;
- EXPECT_CALL(*send_algorithm_, SentPacket(_, first_packet_size, _));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, second_packet_size, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, first_packet_size, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, second_packet_size, _));
}
connection_.OnRetransmissionTimeout();
}
TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) {
+ EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
QuicPacketSequenceNumber original_sequence_number;
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, false)).WillOnce(
- SaveArg<0>(&original_sequence_number));
- connection_.SendStreamData(1, "foo", 0, false);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, !kIsRetransmission))
+ .WillOnce(SaveArg<1>(&original_sequence_number));
+ connection_.SendStreamData(1, "foo", 0, !kFin);
EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission(
&connection_, original_sequence_number));
EXPECT_EQ(0u, QuicConnectionPeer::GetRetransmissionCount(
@@ -895,8 +1106,8 @@ TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) {
// Force retransmission due to RTO.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
QuicPacketSequenceNumber rto_sequence_number;
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, true)).WillOnce(
- SaveArg<0>(&rto_sequence_number));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, kIsRetransmission))
+ .WillOnce(SaveArg<1>(&rto_sequence_number));
connection_.OnRetransmissionTimeout();
EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission(
&connection_, original_sequence_number));
@@ -908,13 +1119,16 @@ TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) {
QuicPacketSequenceNumber nack_sequence_number;
// Ack packets might generate some other packets, which are not
// retransmissions. (More ack packets).
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, false)).Times(AnyNumber());
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, true)).WillOnce(
- SaveArg<0>(&nack_sequence_number));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, !kIsRetransmission))
+ .Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, kIsRetransmission))
+ .WillOnce(SaveArg<1>(&nack_sequence_number));
QuicAckFrame ack(rto_sequence_number, 0);
// Ack the retransmitted packet.
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
ack.received_info.missing_packets.insert(rto_sequence_number);
+ ack.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
+ &connection_, rto_sequence_number - 1);
for (int i = 0; i < 3; i++) {
ProcessAckPacket(&ack);
}
@@ -928,7 +1142,7 @@ TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) {
TEST_F(QuicConnectionTest, SetRTOAfterWritingToSocket) {
helper_->set_blocked(true);
- connection_.SendStreamData(1, "foo", 0, false);
+ connection_.SendStreamData(1, "foo", 0, !kFin);
// Make sure that RTO is not started when the packet is queued.
EXPECT_EQ(0u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
@@ -942,7 +1156,7 @@ TEST_F(QuicConnectionTest, SetRTOAfterWritingToSocket) {
TEST_F(QuicConnectionTest, TestQueued) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
helper_->set_blocked(true);
- connection_.SendStreamData(1, "foo", 0, false);
+ connection_.SendStreamData(1, "foo", 0, !kFin);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
// Attempt to send all packets, but since we're actually still
@@ -960,7 +1174,7 @@ TEST_F(QuicConnectionTest, TestQueued) {
TEST_F(QuicConnectionTest, CloseFecGroup) {
// Don't send missing packet 1
// Don't send missing packet 2
- ProcessFecProtectedPacket(3, false);
+ ProcessFecProtectedPacket(3, false, !kEntropyFlag);
// Don't send missing FEC packet 3
ASSERT_EQ(1u, connection_.NumFecGroups());
@@ -998,15 +1212,15 @@ TEST_F(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) {
// Process an FEC packet, and revive the missing data packet
// but only contact the receive_algorithm once.
EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _));
- ProcessFecPacket(2, 1, true);
+ ProcessFecPacket(2, 1, true, !kEntropyFlag);
}
TEST_F(QuicConnectionTest, InitialTimeout) {
EXPECT_TRUE(connection_.connected());
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
- QuicTime default_timeout = clock_.Now().Add(
+ QuicTime default_timeout = clock_.ApproximateNow().Add(
QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs));
EXPECT_EQ(default_timeout, helper_->timeout_alarm());
@@ -1019,7 +1233,7 @@ TEST_F(QuicConnectionTest, InitialTimeout) {
TEST_F(QuicConnectionTest, TimeoutAfterSend) {
EXPECT_TRUE(connection_.connected());
- QuicTime default_timeout = clock_.Now().Add(
+ QuicTime default_timeout = clock_.ApproximateNow().Add(
QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs));
// When we send a packet, the timeout will change to 5000 + kDefaultTimeout.
@@ -1033,7 +1247,7 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) {
// network event at t=5000. The alarm will reregister.
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(
kDefaultTimeoutUs - 5000));
- EXPECT_EQ(default_timeout, clock_.Now());
+ EXPECT_EQ(default_timeout, clock_.ApproximateNow());
EXPECT_FALSE(connection_.CheckForTimeout());
EXPECT_TRUE(connection_.connected());
EXPECT_EQ(default_timeout.Add(QuicTime::Delta::FromMilliseconds(5)),
@@ -1041,10 +1255,10 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) {
// This time, we should time out.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
EXPECT_EQ(default_timeout.Add(QuicTime::Delta::FromMilliseconds(5)),
- clock_.Now());
+ clock_.ApproximateNow());
EXPECT_TRUE(connection_.CheckForTimeout());
EXPECT_FALSE(connection_.connected());
}
@@ -1052,118 +1266,121 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) {
// TODO(ianswett): Add scheduler tests when should_retransmit is false.
TEST_F(QuicConnectionTest, SendScheduler) {
// Test that if we send a packet without delay, it is not queued.
- QuicPacket* packet = ConstructDataPacket(1, 0);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
- connection_.SendOrQueuePacket(1, packet, false);
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ connection_.SendOrQueuePacket(1, packet, kTestEntropyHash);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
TEST_F(QuicConnectionTest, SendSchedulerDelay) {
// Test that if we send a packet with a delay, it ends up queued.
- QuicPacket* packet = ConstructDataPacket(1, 0);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::FromMicroseconds(1)));
- EXPECT_CALL(*send_algorithm_, SentPacket(1, _, _)).Times(0);
- connection_.SendOrQueuePacket(1, packet, false);
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(1)));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0);
+ connection_.SendOrQueuePacket(1, packet, kTestEntropyHash);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
}
TEST_F(QuicConnectionTest, SendSchedulerForce) {
// Test that if we force send a packet, it is not queued.
- QuicPacket* packet = ConstructDataPacket(1, 0);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(true)).Times(0);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
- connection_.SendOrQueuePacket(1, packet, true);
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, kIsRetransmission)).Times(0);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ connection_.SendOrQueuePacket(1, packet, kTestEntropyHash);
+ // XXX: fixme. was: connection_.SendOrQueuePacket(1, packet, kForce);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) {
- QuicPacket* packet = ConstructDataPacket(1, 0);
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
helper_->set_blocked(true);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, SentPacket(1, _, _)).Times(0);
- connection_.SendOrQueuePacket(1, packet, false);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0);
+ connection_.SendOrQueuePacket(1, packet, kTestEntropyHash);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
}
TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) {
// Test that if we send a packet with a delay, it ends up queued.
- QuicPacket* packet = ConstructDataPacket(1, 0);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::FromMicroseconds(1)));
- connection_.SendOrQueuePacket(1, packet, false);
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(1)));
+ connection_.SendOrQueuePacket(1, packet, kTestEntropyHash);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
// Advance the clock to fire the alarm, and configure the scheduler
// to permit the packet to be sent.
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::Zero()));
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
+ helper_->UnregisterSendAlarmIfRegistered();
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
EXPECT_CALL(visitor_, OnCanWrite());
connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) {
- // Fake packet loss.
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, SentPacket(1, _, false));
- connection_.SendStreamData(1, "foo", 0, false);
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, !kIsRetransmission)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, !kIsRetransmission));
+ connection_.SendStreamData(1, "foo", 0, !kFin);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
// Advance the time for retransmission of lost packet.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(501));
// Test that if we send a retransmit with a delay, it ends up queued.
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(true)).WillOnce(testing::Return(
- QuicTime::Delta::FromMicroseconds(1)));
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(1)));
connection_.OnRetransmissionTimeout();
EXPECT_EQ(1u, connection_.NumQueuedPackets());
// Advance the clock to fire the alarm, and configure the scheduler
// to permit the packet to be sent.
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(true)).WillOnce(testing::Return(
- QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::Zero()));
// Ensure the scheduler is notified this is a retransmit.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, true));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, kIsRetransmission));
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1));
+ helper_->UnregisterSendAlarmIfRegistered();
EXPECT_CALL(visitor_, OnCanWrite());
connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) {
- QuicPacket* packet = ConstructDataPacket(1, 0);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::FromMicroseconds(1)));
- connection_.SendOrQueuePacket(1, packet, false);
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(1)));
+ connection_.SendOrQueuePacket(1, packet, kTestEntropyHash);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
// Attempt to send another packet and make sure that it gets queued.
- packet = ConstructDataPacket(2, 0);
- connection_.SendOrQueuePacket(2, packet, false);
+ packet = ConstructDataPacket(2, 0, !kEntropyFlag);
+ connection_.SendOrQueuePacket(2, packet, kTestEntropyHash);
EXPECT_EQ(2u, connection_.NumQueuedPackets());
}
TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
- QuicPacket* packet = ConstructDataPacket(1, 0);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::FromMicroseconds(10)));
- connection_.SendOrQueuePacket(1, packet, false);
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(10)));
+ connection_.SendOrQueuePacket(1, packet, kTestEntropyHash);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
// Now send non-retransmitting information, that we're not going to
// retransmit 3. The far end should stop waiting for it.
QuicAckFrame frame(0, 1);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(false)).WillRepeatedly(
+ TimeUntilSend(_, !kIsRetransmission)).WillRepeatedly(
testing::Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _));
- EXPECT_CALL(visitor_, OnCanWrite());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(Return(true));
ProcessAckPacket(&frame);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
@@ -1172,27 +1389,27 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
}
TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) {
- QuicPacket* packet = ConstructDataPacket(1, 0);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::FromMicroseconds(10)));
- connection_.SendOrQueuePacket(1, packet, false);
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(10)));
+ connection_.SendOrQueuePacket(1, packet, kTestEntropyHash);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
// Now send non-retransmitting information, that we're not going to
// retransmit 3. The far end should stop waiting for it.
QuicAckFrame frame(0, 1);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::FromMicroseconds(1)));
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(1)));
ProcessAckPacket(&frame);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
}
TEST_F(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) {
- QuicPacket* packet = ConstructDataPacket(1, 0);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::FromMicroseconds(10)));
- connection_.SendOrQueuePacket(1, packet, false);
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(10)));
+ connection_.SendOrQueuePacket(1, packet, kTestEntropyHash);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
// OnCanWrite should not send the packet (because of the delay)
@@ -1209,11 +1426,11 @@ TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) {
ciphertext_size + QuicUtils::StreamFramePacketOverhead(1);
// Queue the first packet.
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return(
- QuicTime::Delta::FromMicroseconds(10)));
- EXPECT_EQ(1u, connection_.SendStreamData(
- 1, "EnoughDataToQueue", 0, false).bytes_consumed);
- EXPECT_EQ(1u, connection_.NumQueuedPackets());
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(10)));
+ EXPECT_EQ(0u, connection_.SendStreamData(
+ 1, "EnoughDataToQueue", 0, !kFin).bytes_consumed);
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
TEST_F(QuicConnectionTest, LoopThroughSendingPackets) {
@@ -1223,33 +1440,34 @@ TEST_F(QuicConnectionTest, LoopThroughSendingPackets) {
ciphertext_size + QuicUtils::StreamFramePacketOverhead(1);
// Queue the first packet.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(17);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(17);
EXPECT_EQ(17u, connection_.SendStreamData(
- 1, "EnoughDataToQueue", 0, false).bytes_consumed);
+ 1, "EnoughDataToQueue", 0, !kFin).bytes_consumed);
}
TEST_F(QuicConnectionTest, NoAckForClose) {
ProcessPacket(1);
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(0);
- EXPECT_CALL(visitor_, ConnectionClose(QUIC_CLIENT_GOING_AWAY, true));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(0);
+ EXPECT_CALL(visitor_, ConnectionClose(QUIC_PEER_GOING_AWAY, true));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(0);
ProcessClosePacket(2, 0);
}
TEST_F(QuicConnectionTest, SendWhenDisconnected) {
EXPECT_TRUE(connection_.connected());
- EXPECT_CALL(visitor_, ConnectionClose(QUIC_CLIENT_GOING_AWAY, false));
- connection_.CloseConnection(QUIC_CLIENT_GOING_AWAY, false);
+ EXPECT_CALL(visitor_, ConnectionClose(QUIC_PEER_GOING_AWAY, false));
+ connection_.CloseConnection(QUIC_PEER_GOING_AWAY, false);
EXPECT_FALSE(connection_.connected());
- QuicPacket* packet = ConstructDataPacket(1, 0);
- EXPECT_CALL(*send_algorithm_, SentPacket(1, _, _)).Times(0);
- connection_.SendOrQueuePacket(1, packet, false);
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0);
+ connection_.SendOrQueuePacket(1, packet, kTestEntropyHash);
}
TEST_F(QuicConnectionTest, PublicReset) {
QuicPublicResetPacket header;
header.public_header.guid = guid_;
- header.public_header.flags = PACKET_PUBLIC_FLAGS_RST;
+ header.public_header.reset_flag = true;
+ header.public_header.version_flag = false;
header.rejected_sequence_number = 10101;
scoped_ptr<QuicEncryptedPacket> packet(
framer_.ConstructPublicResetPacket(header));
@@ -1257,6 +1475,123 @@ TEST_F(QuicConnectionTest, PublicReset) {
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *packet);
}
+TEST_F(QuicConnectionTest, GoAway) {
+ QuicGoAwayFrame goaway;
+ goaway.last_good_stream_id = 1;
+ goaway.error_code = QUIC_PEER_GOING_AWAY;
+ goaway.reason_phrase = "Going away.";
+ EXPECT_CALL(visitor_, OnGoAway(_));
+ ProcessGoAwayPacket(&goaway);
+}
+
+TEST_F(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) {
+ QuicAckFrame ack(0, 4);
+ // Set the sequence number of the ack packet to be least unacked (4)
+ creator_.set_sequence_number(3);
+ ProcessAckPacket(&ack);
+ EXPECT_TRUE(outgoing_ack()->received_info.missing_packets.empty());
+}
+
+TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) {
+ EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ ProcessDataPacket(1, 1, kEntropyFlag);
+ ProcessDataPacket(4, 1, kEntropyFlag);
+ ProcessDataPacket(3, 1, !kEntropyFlag);
+ ProcessDataPacket(7, 1, kEntropyFlag);
+ EXPECT_EQ(146u, outgoing_ack()->received_info.entropy_hash);
+}
+
+TEST_F(QuicConnectionTest, UpdateEntropyForReceivedPackets) {
+ EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ ProcessDataPacket(1, 1, kEntropyFlag);
+ ProcessDataPacket(5, 1, kEntropyFlag);
+ ProcessDataPacket(4, 1, !kEntropyFlag);
+ EXPECT_EQ(34u, outgoing_ack()->received_info.entropy_hash);
+ // Make 4th packet my least unacked, and update entropy for 2, 3 packets.
+ QuicAckFrame ack(0, 4);
+ QuicPacketEntropyHash kRandomEntropyHash = 129u;
+ ack.sent_info.entropy_hash = kRandomEntropyHash;
+ creator_.set_sequence_number(5);
+ QuicPacketEntropyHash six_packet_entropy_hash = 0;
+ if (ProcessAckPacket(&ack)) {
+ six_packet_entropy_hash = 1 << 6;
+ };
+
+ EXPECT_EQ((kRandomEntropyHash + (1 << 5) + six_packet_entropy_hash),
+ outgoing_ack()->received_info.entropy_hash);
+}
+
+TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
+ EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ ProcessDataPacket(1, 1, kEntropyFlag);
+ ProcessDataPacket(5, 1, !kEntropyFlag);
+ ProcessDataPacket(22, 1, kEntropyFlag);
+ EXPECT_EQ(66u, outgoing_ack()->received_info.entropy_hash);
+ creator_.set_sequence_number(22);
+ QuicPacketEntropyHash kRandomEntropyHash = 85u;
+ // Current packet is the least unacked packet.
+ QuicAckFrame ack(0, 23);
+ ack.sent_info.entropy_hash = kRandomEntropyHash;
+ QuicPacketEntropyHash ack_entropy_hash = ProcessAckPacket(&ack);
+ EXPECT_EQ((kRandomEntropyHash + ack_entropy_hash),
+ outgoing_ack()->received_info.entropy_hash);
+ ProcessDataPacket(25, 1, kEntropyFlag);
+ EXPECT_EQ((kRandomEntropyHash + ack_entropy_hash + (1 << (25 % 8))),
+ outgoing_ack()->received_info.entropy_hash);
+}
+
+TEST_F(QuicConnectionTest, EntropyCalculationForTruncatedAck) {
+ EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ QuicPacketEntropyHash entropy[51];
+ entropy[0] = 0;
+ for (int i = 1; i < 51; ++i) {
+ bool should_send = i % 10 != 0;
+ bool entropy_flag = (i & (i - 1)) != 0;
+ if (!should_send) {
+ entropy[i] = entropy[i - 1];
+ continue;
+ }
+ if (entropy_flag) {
+ entropy[i] = entropy[i - 1] ^ (1 << (i % 8));
+ } else {
+ entropy[i] = entropy[i - 1];
+ }
+ ProcessDataPacket(i, 1, entropy_flag);
+ }
+ // Till 50 since 50th packet is not sent.
+ for (int i = 1; i < 50; ++i) {
+ EXPECT_EQ(entropy[i], QuicConnectionPeer::ReceivedEntropyHash(
+ &connection_, i));
+ }
+}
+
+TEST_F(QuicConnectionTest, CheckSentEntropyHash) {
+ creator_.set_sequence_number(1);
+ SequenceNumberSet missing_packets;
+ QuicPacketEntropyHash entropy_hash = 0;
+ QuicPacketSequenceNumber max_sequence_number = 51;
+ for (QuicPacketSequenceNumber i = 1; i <= max_sequence_number; ++i) {
+ bool is_missing = i % 10 != 0;
+ bool entropy_flag = (i & (i - 1)) != 0;
+ QuicPacketEntropyHash packet_entropy_hash = 0;
+ if (entropy_flag) {
+ packet_entropy_hash = 1 << (i % 8);
+ }
+ QuicPacket* packet = ConstructDataPacket(i, 0, entropy_flag);
+ connection_.SendOrQueuePacket(i, packet, packet_entropy_hash);
+
+ if (is_missing) {
+ missing_packets.insert(i);
+ continue;
+ }
+
+ entropy_hash ^= packet_entropy_hash;
+ }
+ EXPECT_TRUE(QuicConnectionPeer::IsValidEntropy(
+ &connection_, max_sequence_number, missing_packets, entropy_hash))
+ << "";
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/net/quic/quic_fec_group.cc b/net/quic/quic_fec_group.cc
index af976a3..8431ff3 100644
--- a/net/quic/quic_fec_group.cc
+++ b/net/quic/quic_fec_group.cc
@@ -21,7 +21,8 @@ const QuicPacketSequenceNumber kNoSequenceNumber = kuint64max;
QuicFecGroup::QuicFecGroup()
: min_protected_packet_(kNoSequenceNumber),
max_protected_packet_(kNoSequenceNumber),
- parity_len_(0) {
+ payload_parity_len_(0),
+ entropy_parity_(false) {
}
QuicFecGroup::~QuicFecGroup() {}
@@ -39,7 +40,7 @@ bool QuicFecGroup::Update(const QuicPacketHeader& header,
<< header.packet_sequence_number;
return false;
}
- if (!UpdateParity(decrypted_payload)) {
+ if (!UpdateParity(decrypted_payload, header.entropy_flag)) {
return false;
}
received_packets_.insert(header.packet_sequence_number);
@@ -48,11 +49,12 @@ bool QuicFecGroup::Update(const QuicPacketHeader& header,
bool QuicFecGroup::UpdateFec(
QuicPacketSequenceNumber fec_packet_sequence_number,
+ bool fec_entropy_flag,
const QuicFecData& fec) {
if (min_protected_packet_ != kNoSequenceNumber) {
return false;
}
- set<QuicPacketSequenceNumber>::const_iterator it = received_packets_.begin();
+ SequenceNumberSet::const_iterator it = received_packets_.begin();
while (it != received_packets_.end()) {
if ((*it < fec.fec_group) ||
(*it >= fec_packet_sequence_number)) {
@@ -61,7 +63,7 @@ bool QuicFecGroup::UpdateFec(
}
++it;
}
- if (!UpdateParity(fec.redundancy)) {
+ if (!UpdateParity(fec.redundancy, fec_entropy_flag)) {
return false;
}
min_protected_packet_ = fec.fec_group;
@@ -98,16 +100,19 @@ size_t QuicFecGroup::Revive(QuicPacketHeader* header,
}
DCHECK_NE(kNoSequenceNumber, missing);
- DCHECK_LE(parity_len_, decrypted_payload_len);
- if (parity_len_ > decrypted_payload_len) {
+ DCHECK_LE(payload_parity_len_, decrypted_payload_len);
+ if (payload_parity_len_ > decrypted_payload_len) {
return 0;
}
- for (size_t i = 0; i < parity_len_; ++i) {
- decrypted_payload[i] = parity_[i];
+ for (size_t i = 0; i < payload_parity_len_; ++i) {
+ decrypted_payload[i] = payload_parity_[i];
}
+
header->packet_sequence_number = missing;
+ header->entropy_flag = entropy_parity_;
+
received_packets_.insert(missing);
- return parity_len_;
+ return payload_parity_len_;
}
bool QuicFecGroup::ProtectsPacketsBefore(QuicPacketSequenceNumber num) const {
@@ -119,32 +124,35 @@ bool QuicFecGroup::ProtectsPacketsBefore(QuicPacketSequenceNumber num) const {
return *received_packets_.begin() < num;
}
-bool QuicFecGroup::UpdateParity(StringPiece payload) {
+bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) {
DCHECK_LE(payload.size(), kMaxPacketSize);
if (payload.size() > kMaxPacketSize) {
DLOG(ERROR) << "Illegal payload size: " << payload.size();
return false;
}
- if (parity_len_ < payload.size()) {
- parity_len_ = payload.size();
+ if (payload_parity_len_ < payload.size()) {
+ payload_parity_len_ = payload.size();
}
DCHECK_LE(payload.size(), kMaxPacketSize);
if (received_packets_.size() == 0 &&
min_protected_packet_ == kNoSequenceNumber) {
// Initialize the parity to the value of this payload
- memcpy(parity_, payload.data(), payload.size());
+ memcpy(payload_parity_, payload.data(), payload.size());
if (payload.size() < kMaxPacketSize) {
// TODO(rch): expand as needed.
- memset(parity_ + payload.size(), 0,
+ memset(payload_parity_ + payload.size(), 0,
kMaxPacketSize - payload.size());
}
+ entropy_parity_ = entropy;
return true;
}
// Update the parity by XORing in the data (padding with 0s if necessary).
for (size_t i = 0; i < kMaxPacketSize; ++i) {
uint8 byte = i < payload.size() ? payload[i] : 0x00;
- parity_[i] ^= byte;
+ payload_parity_[i] ^= byte;
}
+ // xor of boolean values.
+ entropy_parity_ = (entropy_parity_ != entropy);
return true;
}
diff --git a/net/quic/quic_fec_group.h b/net/quic/quic_fec_group.h
index 14fdec8..f9d4e28 100644
--- a/net/quic/quic_fec_group.h
+++ b/net/quic/quic_fec_group.h
@@ -30,6 +30,7 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
// Returns false if this packet has already been seen or if it does
// not claim to protect all the packets previously seen in this group.
bool UpdateFec(QuicPacketSequenceNumber fec_packet_sequence_number,
+ bool fec_entropy_flag,
const QuicFecData& fec);
// Returns true if a packet can be revived from this FEC group.
@@ -51,8 +52,12 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
// numbers less than |num|.
bool ProtectsPacketsBefore(QuicPacketSequenceNumber num) const;
- const base::StringPiece parity() const {
- return base::StringPiece(parity_, parity_len_);
+ const base::StringPiece payload_parity() const {
+ return base::StringPiece(payload_parity_, payload_parity_len_);
+ }
+
+ bool entropy_parity() const {
+ return entropy_parity_;
}
QuicPacketSequenceNumber min_protected_packet() const {
@@ -64,13 +69,13 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
}
private:
- bool UpdateParity(base::StringPiece payload);
+ bool UpdateParity(base::StringPiece payload, bool entropy);
// Returns the number of missing packets, or size_t max if the number
// of missing packets is not known.
size_t NumMissingPackets() const;
// Set of packets that we have recevied.
- std::set<QuicPacketSequenceNumber> received_packets_;
+ SequenceNumberSet received_packets_;
// Sequence number of the first protected packet in this group (the one
// with the lowest packet sequence number). Will only be set once the FEC
// packet has been seen.
@@ -80,8 +85,9 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
// packet has been seen.
QuicPacketSequenceNumber max_protected_packet_;
// The cumulative parity calculation of all received packets.
- char parity_[kMaxPacketSize];
- size_t parity_len_;
+ char payload_parity_[kMaxPacketSize];
+ size_t payload_parity_len_;
+ bool entropy_parity_;
};
} // namespace net
diff --git a/net/quic/quic_fec_group_test.cc b/net/quic/quic_fec_group_test.cc
index 4e4dd20..2dd4a7d 100644
--- a/net/quic/quic_fec_group_test.cc
+++ b/net/quic/quic_fec_group_test.cc
@@ -18,14 +18,25 @@ namespace net {
namespace {
const char* kData[] = {
- "abc12345678",
- "987defg",
- "ghi12345",
- "987jlkmno",
- "mno4567890",
- "789pqrstuvw",
+ "abc12345678",
+ "987defg",
+ "ghi12345",
+ "987jlkmno",
+ "mno4567890",
+ "789pqrstuvw",
};
+const bool kEntropyFlag[] = {
+ false,
+ true,
+ true,
+ false,
+ true,
+ true,
+};
+
+const bool kTestFecEntropy = false;
+
} // namespace
class QuicFecGroupTest : public ::testing::Test {
@@ -33,14 +44,19 @@ class QuicFecGroupTest : public ::testing::Test {
void RunTest(size_t num_packets, size_t lost_packet, bool out_of_order) {
size_t max_len = strlen(kData[0]);
scoped_array<char>redundancy(new char[max_len]);
- for (size_t i = 0; i < max_len; i++) {
- // Initialize to the first packet.
- redundancy[i] = kData[0][i];
- // XOR in the remaining packets.
- for (size_t packet = 1; packet < num_packets; packet++) {
+ bool entropy_redundancy = false;
+ for (size_t packet = 0; packet < num_packets; ++packet) {
+ for (size_t i = 0; i < max_len; i++) {
+ if (packet == 0) {
+ // Initialize to the first packet.
+ redundancy[i] = kData[0][i];
+ continue;
+ }
+ // XOR in the remaining packets.
uint8 byte = i > strlen(kData[packet]) ? 0x00 : kData[packet][i];
redundancy[i] = redundancy[i] ^ byte;
}
+ entropy_redundancy = (entropy_redundancy != kEntropyFlag[packet]);
}
QuicFecGroup group;
@@ -55,10 +71,11 @@ class QuicFecGroupTest : public ::testing::Test {
QuicFecData fec;
fec.fec_group = 0;
fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0]));
- ASSERT_TRUE(group.UpdateFec(num_packets, fec));
+ ASSERT_TRUE(group.UpdateFec(num_packets, entropy_redundancy, fec));
} else {
QuicPacketHeader header;
header.packet_sequence_number = packet;
+ header.entropy_flag = kEntropyFlag[packet];
ASSERT_TRUE(group.Update(header, kData[packet]));
}
ASSERT_TRUE(group.CanRevive() == (packet == num_packets - 1));
@@ -72,6 +89,7 @@ class QuicFecGroupTest : public ::testing::Test {
QuicPacketHeader header;
header.packet_sequence_number = packet;
+ header.entropy_flag = kEntropyFlag[packet];
ASSERT_TRUE(group.Update(header, kData[packet]));
ASSERT_FALSE(group.CanRevive());
}
@@ -82,7 +100,7 @@ class QuicFecGroupTest : public ::testing::Test {
fec.fec_group = 0;
fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0]));
- ASSERT_TRUE(group.UpdateFec(num_packets, fec));
+ ASSERT_TRUE(group.UpdateFec(num_packets, entropy_redundancy, fec));
}
QuicPacketHeader header;
char recovered[kMaxPacketSize];
@@ -94,6 +112,7 @@ class QuicFecGroupTest : public ::testing::Test {
EXPECT_EQ(lost_packet, header.packet_sequence_number)
<< "Failed to revive packet " << lost_packet << " out of "
<< num_packets;
+ EXPECT_EQ(kEntropyFlag[lost_packet], header.entropy_flag);
ASSERT_GE(len, strlen(kData[lost_packet])) << "Incorrect length";
for (size_t i = 0; i < strlen(kData[lost_packet]); i++) {
EXPECT_EQ(kData[lost_packet][i], recovered[i]);
@@ -137,7 +156,8 @@ TEST_F(QuicFecGroupTest, UpdateFecIfReceivedPacketIsNotCovered) {
fec.fec_group = 1;
fec.redundancy = redundancy;
- ASSERT_FALSE(group.UpdateFec(2, fec));
+ header.packet_sequence_number = 2;
+ ASSERT_FALSE(group.UpdateFec(2, kTestFecEntropy, fec));
}
TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) {
@@ -186,7 +206,7 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) {
fec.redundancy = kData[0];
QuicFecGroup group;
- ASSERT_TRUE(group.UpdateFec(3, fec));
+ ASSERT_TRUE(group.UpdateFec(3, kTestFecEntropy, fec));
EXPECT_FALSE(group.ProtectsPacketsBefore(1));
EXPECT_FALSE(group.ProtectsPacketsBefore(2));
diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc
index a794701..db3de8d5 100644
--- a/net/quic/quic_framer.cc
+++ b/net/quic/quic_framer.cc
@@ -86,8 +86,19 @@ size_t QuicFramer::GetSerializedFrameLength(
return frame_len;
}
-QuicPacket* QuicFramer::ConstructFrameDataPacket(const QuicPacketHeader& header,
- const QuicFrames& frames) {
+QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash(
+ const QuicPacketHeader& header) const {
+ if (!header.entropy_flag) {
+ // TODO(satyamshekhar): Return some more better value here (something that
+ // is not a constant).
+ return 0;
+ }
+ return 1 << (header.packet_sequence_number % 8);
+}
+
+SerializedPacket QuicFramer::ConstructFrameDataPacket(
+ const QuicPacketHeader& header,
+ const QuicFrames& frames) {
const size_t max_plaintext_size = GetMaxPlaintextSize(kMaxPacketSize);
size_t packet_size = kPacketHeaderSize;
for (size_t i = 0; i < frames.size(); ++i) {
@@ -100,18 +111,20 @@ QuicPacket* QuicFramer::ConstructFrameDataPacket(const QuicPacketHeader& header,
return ConstructFrameDataPacket(header, frames, packet_size);
}
-QuicPacket* QuicFramer::ConstructFrameDataPacket(const QuicPacketHeader& header,
- const QuicFrames& frames,
- size_t packet_size) {
+SerializedPacket QuicFramer::ConstructFrameDataPacket(
+ const QuicPacketHeader& header,
+ const QuicFrames& frames,
+ size_t packet_size) {
QuicDataWriter writer(packet_size);
+ SerializedPacket kNoPacket = SerializedPacket(0, NULL, 0, NULL);
if (!WritePacketHeader(header, &writer)) {
- return NULL;
+ return kNoPacket;
}
for (size_t i = 0; i < frames.size(); ++i) {
const QuicFrame& frame = frames[i];
if (!writer.WriteUInt8(frame.type)) {
- return NULL;
+ return kNoPacket;
}
switch (frame.type) {
@@ -120,34 +133,39 @@ QuicPacket* QuicFramer::ConstructFrameDataPacket(const QuicPacketHeader& header,
break;
case STREAM_FRAME:
if (!AppendStreamFramePayload(*frame.stream_frame, &writer)) {
- return NULL;
+ return kNoPacket;
}
break;
case ACK_FRAME:
if (!AppendAckFramePayload(*frame.ack_frame, &writer)) {
- return NULL;
+ return kNoPacket;
}
break;
case CONGESTION_FEEDBACK_FRAME:
if (!AppendQuicCongestionFeedbackFramePayload(
*frame.congestion_feedback_frame, &writer)) {
- return NULL;
+ return kNoPacket;
}
break;
case RST_STREAM_FRAME:
if (!AppendRstStreamFramePayload(*frame.rst_stream_frame, &writer)) {
- return NULL;
+ return kNoPacket;
}
break;
case CONNECTION_CLOSE_FRAME:
if (!AppendConnectionCloseFramePayload(
*frame.connection_close_frame, &writer)) {
- return NULL;
+ return kNoPacket;
+ }
+ break;
+ case GOAWAY_FRAME:
+ if (!AppendGoAwayFramePayload(*frame.goaway_frame, &writer)) {
+ return kNoPacket;
}
break;
default:
RaiseError(QUIC_INVALID_FRAME_DATA);
- return NULL;
+ return kNoPacket;
}
}
@@ -163,32 +181,35 @@ QuicPacket* QuicFramer::ConstructFrameDataPacket(const QuicPacketHeader& header,
packet->FecProtectedData());
}
- return packet;
+ return SerializedPacket(header.packet_sequence_number, packet,
+ GetPacketEntropyHash(header), NULL);
}
-QuicPacket* QuicFramer::ConstructFecPacket(const QuicPacketHeader& header,
- const QuicFecData& fec) {
+SerializedPacket QuicFramer::ConstructFecPacket(const QuicPacketHeader& header,
+ const QuicFecData& fec) {
size_t len = kPacketHeaderSize;
len += fec.redundancy.length();
QuicDataWriter writer(len);
-
+ SerializedPacket kNoPacket = SerializedPacket(0, NULL, 0, NULL);
if (!WritePacketHeader(header, &writer)) {
- return NULL;
+ return kNoPacket;
}
if (!writer.WriteBytes(fec.redundancy.data(), fec.redundancy.length())) {
- return NULL;
+ return kNoPacket;
}
- return QuicPacket::NewFecPacket(writer.take(), len, true);
+ return SerializedPacket(header.packet_sequence_number,
+ QuicPacket::NewFecPacket(writer.take(), len, true),
+ GetPacketEntropyHash(header), NULL);
}
// static
QuicEncryptedPacket* QuicFramer::ConstructPublicResetPacket(
const QuicPublicResetPacket& packet) {
- DCHECK_EQ(PACKET_PUBLIC_FLAGS_RST,
- PACKET_PUBLIC_FLAGS_RST & packet.public_header.flags);
+ DCHECK(packet.public_header.reset_flag);
+ DCHECK(!packet.public_header.version_flag);
size_t len = kPublicResetPacketSize;
QuicDataWriter writer(len);
@@ -196,7 +217,7 @@ QuicEncryptedPacket* QuicFramer::ConstructPublicResetPacket(
return NULL;
}
- uint8 flags = static_cast<uint8>(packet.public_header.flags);
+ uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_RST);
if (!writer.WriteUInt8(flags)) {
return NULL;
}
@@ -226,7 +247,7 @@ bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
}
bool rv;
- if (public_header.flags & PACKET_PUBLIC_FLAGS_RST) {
+ if (public_header.reset_flag) {
rv = ProcessPublicResetPacket(public_header);
} else {
rv = ProcessDataPacket(public_header, packet);
@@ -257,7 +278,7 @@ bool QuicFramer::ProcessDataPacket(
}
// Handle the payload.
- if ((header.private_flags & PACKET_PRIVATE_FLAGS_FEC) == 0) {
+ if (!header.fec_flag) {
if (header.fec_group != 0) {
StringPiece payload = reader_->PeekRemainingPayload();
visitor_->OnFecProtectedPayload(payload);
@@ -295,13 +316,16 @@ bool QuicFramer::ProcessPublicResetPacket(
return true;
}
-bool QuicFramer::ProcessRevivedPacket(const QuicPacketHeader& header,
+bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header,
StringPiece payload) {
DCHECK(!reader_.get());
visitor_->OnRevivedPacket();
- visitor_->OnPacketHeader(header);
+ header->entropy_hash = GetPacketEntropyHash(*header);
+
+ // TODO(satyamshekhar): Don't process if the visitor refuses the header.
+ visitor_->OnPacketHeader(*header);
if (payload.length() > kMaxPacketSize) {
set_detailed_error("Revived packet too large.");
@@ -326,7 +350,13 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header,
return false;
}
- uint8 flags = static_cast<uint8>(header.public_header.flags);
+ uint8 flags = 0;
+ if (header.public_header.reset_flag) {
+ flags |= PACKET_PUBLIC_FLAGS_RST;
+ }
+ if (header.public_header.version_flag) {
+ flags |= PACKET_PUBLIC_FLAGS_VERSION;
+ }
if (!writer->WriteUInt8(flags)) {
return false;
}
@@ -335,7 +365,16 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header,
return false;
}
- flags = static_cast<uint8>(header.private_flags);
+ flags = 0;
+ if (header.fec_flag) {
+ flags |= PACKET_PRIVATE_FLAGS_FEC;
+ }
+ if (header.entropy_flag) {
+ flags |= PACKET_PRIVATE_FLAGS_ENTROPY;
+ }
+ if (header.fec_entropy_flag) {
+ flags |= PACKET_PRIVATE_FLAGS_FEC_ENTROPY;
+ }
if (!writer->WriteUInt8(flags)) {
return false;
}
@@ -361,7 +400,7 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header,
QuicPacketSequenceNumber QuicFramer::CalculatePacketSequenceNumberFromWire(
QuicPacketSequenceNumber packet_sequence_number) const {
- // The new sequence number might have wrapped to the next epoc, or
+ // The new sequence number might have wrapped to the next epoch, or
// it might have reverse wrapped to the previous epoch, or it might
// remain in the same epoch. Select the sequence number closest to the
// previous sequence number.
@@ -393,7 +432,10 @@ bool QuicFramer::ProcessPublicHeader(QuicPacketPublicHeader* public_header) {
return false;
}
- public_header->flags = static_cast<QuicPacketPublicFlags>(public_flags);
+ public_header->reset_flag = (public_flags & PACKET_PUBLIC_FLAGS_RST) != 0;
+ public_header->version_flag =
+ (public_flags & PACKET_PUBLIC_FLAGS_VERSION) != 0;
+
return true;
}
@@ -417,7 +459,7 @@ bool QuicFramer::ProcessPacketHeader(
return false;
}
- if (!DecryptPayload(packet)) {
+ if (!DecryptPayload(header->packet_sequence_number, packet)) {
DLOG(WARNING) << "Unable to decrypt payload.";
return RaiseError(QUIC_DECRYPTION_FAILURE);
}
@@ -433,7 +475,10 @@ bool QuicFramer::ProcessPacketHeader(
return false;
}
- header->private_flags = static_cast<QuicPacketPrivateFlags>(private_flags);
+ header->fec_flag = (private_flags & PACKET_PRIVATE_FLAGS_FEC) != 0;
+ header->entropy_flag = (private_flags & PACKET_PRIVATE_FLAGS_ENTROPY) != 0;
+ header->fec_entropy_flag =
+ (private_flags & PACKET_PRIVATE_FLAGS_FEC_ENTROPY) != 0;
uint8 first_fec_protected_packet_offset;
if (!reader_->ReadBytes(&first_fec_protected_packet_offset, 1)) {
@@ -443,6 +488,7 @@ bool QuicFramer::ProcessPacketHeader(
header->fec_group = first_fec_protected_packet_offset == kNoFecOffset ? 0 :
header->packet_sequence_number - first_fec_protected_packet_offset;
+ header->entropy_hash = GetPacketEntropyHash(*header);
// Set the last sequence number after we have decrypted the packet
// so we are confident is not attacker controlled.
last_sequence_number_ = header->packet_sequence_number;
@@ -505,6 +551,11 @@ bool QuicFramer::ProcessFrameData() {
return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA);
}
break;
+ case GOAWAY_FRAME:
+ if (!ProcessGoAwayFrame()) {
+ return RaiseError(QUIC_INVALID_GOAWAY_DATA);
+ }
+ break;
default:
set_detailed_error("Illegal frame type.");
DLOG(WARNING) << "Illegal frame type: "
@@ -560,6 +611,11 @@ bool QuicFramer::ProcessAckFrame(QuicAckFrame* frame) {
}
bool QuicFramer::ProcessReceivedInfo(ReceivedPacketInfo* received_info) {
+ if (!reader_->ReadBytes(&received_info->entropy_hash, 1)) {
+ set_detailed_error("Unable to read entropy hash for received packets.");
+ return false;
+ }
+
if (!ProcessPacketSequenceNumber(&received_info->largest_observed)) {
set_detailed_error("Unable to read largest observed.");
return false;
@@ -584,6 +640,11 @@ bool QuicFramer::ProcessReceivedInfo(ReceivedPacketInfo* received_info) {
}
bool QuicFramer::ProcessSentInfo(SentPacketInfo* sent_info) {
+ if (!reader_->ReadBytes(&sent_info->entropy_hash, 1)) {
+ set_detailed_error("Unable to read entropy hash for sent packets.");
+ return false;
+ }
+
if (!ProcessPacketSequenceNumber(&sent_info->least_unacked)) {
set_detailed_error("Unable to read least unacked.");
return false;
@@ -700,11 +761,6 @@ bool QuicFramer::ProcessRstStreamFrame() {
return false;
}
- if (!reader_->ReadUInt64(&frame.offset)) {
- set_detailed_error("Unable to read offset in rst frame.");
- return false;
- }
-
uint32 error_code;
if (!reader_->ReadUInt32(&error_code)) {
set_detailed_error("Unable to read rst stream error code.");
@@ -749,8 +805,39 @@ bool QuicFramer::ProcessConnectionCloseFrame() {
return true;
}
-QuicEncryptedPacket* QuicFramer::EncryptPacket(const QuicPacket& packet) {
- scoped_ptr<QuicData> out(encrypter_->Encrypt(packet.AssociatedData(),
+bool QuicFramer::ProcessGoAwayFrame() {
+ QuicGoAwayFrame frame;
+
+ uint32 error_code;
+ if (!reader_->ReadUInt32(&error_code)) {
+ set_detailed_error("Unable to read go away error code.");
+ return false;
+ }
+ frame.error_code = static_cast<QuicErrorCode>(error_code);
+
+ uint32 stream_id;
+ if (!reader_->ReadUInt32(&stream_id)) {
+ set_detailed_error("Unable to read last good stream id.");
+ return false;
+ }
+ frame.last_good_stream_id = static_cast<QuicErrorCode>(stream_id);
+
+ StringPiece reason_phrase;
+ if (!reader_->ReadStringPiece16(&reason_phrase)) {
+ set_detailed_error("Unable to read goaway reason.");
+ return false;
+ }
+ frame.reason_phrase = reason_phrase.as_string();
+
+ visitor_->OnGoAwayFrame(frame);
+ return true;
+}
+
+QuicEncryptedPacket* QuicFramer::EncryptPacket(
+ QuicPacketSequenceNumber packet_sequence_number,
+ const QuicPacket& packet) {
+ scoped_ptr<QuicData> out(encrypter_->Encrypt(packet_sequence_number,
+ packet.AssociatedData(),
packet.Plaintext()));
if (out.get() == NULL) {
RaiseError(QUIC_ENCRYPTION_FAILURE);
@@ -768,13 +855,15 @@ size_t QuicFramer::GetMaxPlaintextSize(size_t ciphertext_size) {
return encrypter_->GetMaxPlaintextSize(ciphertext_size);
}
-bool QuicFramer::DecryptPayload(const QuicEncryptedPacket& packet) {
+bool QuicFramer::DecryptPayload(QuicPacketSequenceNumber sequence_number,
+ const QuicEncryptedPacket& packet) {
StringPiece encrypted;
if (!reader_->ReadStringPiece(&encrypted, reader_->BytesRemaining())) {
return false;
}
DCHECK(decrypter_.get() != NULL);
- decrypted_.reset(decrypter_->Decrypt(packet.AssociatedData(), encrypted));
+ decrypted_.reset(decrypter_->Decrypt(sequence_number,
+ packet.AssociatedData(), encrypted));
if (decrypted_.get() == NULL) {
return false;
}
@@ -789,34 +878,36 @@ size_t QuicFramer::ComputeFramePayloadLength(const QuicFrame& frame) {
// same as sizeof(member_wire_format).
switch (frame.type) {
case STREAM_FRAME:
- len += 4; // stream id
- len += 1; // fin
- len += 8; // offset
- len += 2; // space for the 16 bit length
+ len += 4; // Stream id of the frame.
+ len += 1; // A byte for fin.
+ len += 8; // Byte offset.
+ len += 2; // Space for the 16 bit length.
len += frame.stream_frame->data.size();
break;
case ACK_FRAME: {
const QuicAckFrame& ack = *frame.ack_frame;
- len += 6; // largest observed packet sequence number
- len += 1; // num missing packets
+ len += 1; // Commutative hash of entropy of the packets received.
+ len += 6; // Least packet sequence number awaiting an ack.
+ len += 1; // Hash of entropy of the packets sent up to largest observed.
+ len += 6; // Largest observed packet sequence number.
+ len += 1; // Number of missing packets.
len += 6 * ack.received_info.missing_packets.size();
- len += 6; // least packet sequence number awaiting an ack
break;
}
case CONGESTION_FEEDBACK_FRAME: {
const QuicCongestionFeedbackFrame& congestion_feedback =
*frame.congestion_feedback_frame;
- len += 1; // congestion feedback type
+ len += 1; // Congestion feedback type.
switch (congestion_feedback.type) {
case kInterArrival: {
const CongestionFeedbackMessageInterArrival& inter_arrival =
congestion_feedback.inter_arrival;
len += 2;
- len += 1; // num received packets
+ len += 1; // Number received packets.
if (inter_arrival.received_packet_times.size() > 0) {
- len += 6; // smallest received
- len += 8; // time
+ len += 6; // Smallest received.
+ len += 8; // Time.
// 2 bytes per sequence number delta plus 4 bytes per delta time.
len += 6 * (inter_arrival.received_packet_times.size() - 1);
}
@@ -836,19 +927,24 @@ size_t QuicFramer::ComputeFramePayloadLength(const QuicFrame& frame) {
break;
}
case RST_STREAM_FRAME:
- len += 4; // stream id
- len += 8; // offset
- len += 4; // error code
- len += 2; // error details size
+ len += 4; // Stream id of the frame.
+ len += 4; // Error code.
+ len += 2; // Error details size.
len += frame.rst_stream_frame->error_details.size();
break;
case CONNECTION_CLOSE_FRAME:
- len += 4; // error code
- len += 2; // error details size
+ len += 4; // Error code.
+ len += 2; // Error details size.
len += frame.connection_close_frame->error_details.size();
len += ComputeFramePayloadLength(
QuicFrame(&frame.connection_close_frame->ack_frame));
break;
+ case GOAWAY_FRAME:
+ len += 4; // error code
+ len += 4; // last good stream id
+ len += 2; // reason phrase length
+ len += frame.goaway_frame->reason_phrase.size();
+ break;
default:
set_detailed_error("Illegal frame type.");
DLOG(INFO) << "Illegal frame type: " << frame.type;
@@ -891,9 +987,9 @@ bool QuicFramer::AppendStreamFramePayload(
}
QuicPacketSequenceNumber QuicFramer::CalculateLargestObserved(
- const SequenceSet& missing_packets,
- SequenceSet::const_iterator largest_written) {
- SequenceSet::const_iterator it = largest_written;
+ const SequenceNumberSet& missing_packets,
+ SequenceNumberSet::const_iterator largest_written) {
+ SequenceNumberSet::const_iterator it = largest_written;
QuicPacketSequenceNumber previous_missing = *it;
++it;
@@ -911,10 +1007,21 @@ QuicPacketSequenceNumber QuicFramer::CalculateLargestObserved(
bool QuicFramer::AppendAckFramePayload(
const QuicAckFrame& frame,
QuicDataWriter* writer) {
+ // TODO(satyamshekhar): Decide how often we really should send this
+ // entropy_hash update.
+ if (!writer->WriteUInt8(frame.sent_info.entropy_hash)) {
+ return false;
+ }
+
if (!AppendPacketSequenceNumber(frame.sent_info.least_unacked, writer)) {
return false;
}
+ size_t received_entropy_offset = writer->length();
+ if (!writer->WriteUInt8(frame.received_info.entropy_hash)) {
+ return false;
+ }
+
size_t largest_observed_offset = writer->length();
if (!AppendPacketSequenceNumber(frame.received_info.largest_observed,
writer)) {
@@ -929,13 +1036,19 @@ bool QuicFramer::AppendAckFramePayload(
return false;
}
- SequenceSet::const_iterator it = frame.received_info.missing_packets.begin();
+ SequenceNumberSet::const_iterator it =
+ frame.received_info.missing_packets.begin();
int num_missing_packets_written = 0;
for (; it != frame.received_info.missing_packets.end(); ++it) {
if (!AppendPacketSequenceNumber(*it, writer)) {
- // We are truncating. Overwrite largest_observed.
+ // We are truncating.
QuicPacketSequenceNumber largest_observed =
CalculateLargestObserved(frame.received_info.missing_packets, --it);
+ // Overwrite entropy hash for received packets.
+ writer->WriteUInt8ToOffset(
+ entropy_calculator_->ReceivedEntropyHash(largest_observed),
+ received_entropy_offset);
+ // Overwrite largest_observed.
writer->WriteUInt48ToOffset(largest_observed & kSequenceNumberMask,
largest_observed_offset);
writer->WriteUInt8ToOffset(num_missing_packets_written,
@@ -1045,9 +1158,6 @@ bool QuicFramer::AppendRstStreamFramePayload(
if (!writer->WriteUInt32(frame.stream_id)) {
return false;
}
- if (!writer->WriteUInt64(frame.offset)) {
- return false;
- }
uint32 error_code = static_cast<uint32>(frame.error_code);
if (!writer->WriteUInt32(error_code)) {
@@ -1074,6 +1184,22 @@ bool QuicFramer::AppendConnectionCloseFramePayload(
return true;
}
+bool QuicFramer::AppendGoAwayFramePayload(const QuicGoAwayFrame& frame,
+ QuicDataWriter* writer) {
+ uint32 error_code = static_cast<uint32>(frame.error_code);
+ if (!writer->WriteUInt32(error_code)) {
+ return false;
+ }
+ uint32 stream_id = static_cast<uint32>(frame.last_good_stream_id);
+ if (!writer->WriteUInt32(stream_id)) {
+ return false;
+ }
+ if (!writer->WriteStringPiece16(frame.reason_phrase)) {
+ return false;
+ }
+ return true;
+}
+
bool QuicFramer::RaiseError(QuicErrorCode error) {
DLOG(INFO) << detailed_error_;
set_error(error);
diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h
index 297ca05..478f7b3 100644
--- a/net/quic/quic_framer.h
+++ b/net/quic/quic_framer.h
@@ -74,6 +74,9 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface {
virtual void OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) = 0;
+ // Called when a GoAwayFrame has been parsed.
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) = 0;
+
// Called when FEC data has been parsed.
virtual void OnFecData(const QuicFecData& fec) = 0;
@@ -91,6 +94,21 @@ class NET_EXPORT_PRIVATE QuicFecBuilderInterface {
base::StringPiece payload) = 0;
};
+// This class calculates the received entropy of the ack packet being
+// framed, should it get truncated.
+class NET_EXPORT_PRIVATE QuicReceivedEntropyHashCalculatorInterface {
+ public:
+ virtual ~QuicReceivedEntropyHashCalculatorInterface() {}
+
+ // When an ack frame gets truncated while being framed the received
+ // entropy of the ack frame needs to be calculated since the some of the
+ // missing packets are not added and the largest observed might be lowered.
+ // This should return the received entropy hash of the packets received up to
+ // and including |sequence_number|.
+ virtual QuicPacketEntropyHash ReceivedEntropyHash(
+ QuicPacketSequenceNumber sequence_number) const = 0;
+};
+
// Class for parsing and constructing QUIC packets. It has a
// QuicFramerVisitorInterface that is called when packets are parsed.
// It also has a QuicFecBuilder that is called when packets are constructed
@@ -106,8 +124,8 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Frame was truncated. last_written in this case is the iterator for the
// last missing packet which fit in the outgoing ack.
static QuicPacketSequenceNumber CalculateLargestObserved(
- const SequenceSet& missing_packets,
- SequenceSet::const_iterator last_written);
+ const SequenceNumberSet& missing_packets,
+ SequenceNumberSet::const_iterator last_written);
// Set callbacks to be called from the framer. A visitor must be set, or
// else the framer will likely crash. It is acceptable for the visitor
@@ -124,6 +142,15 @@ class NET_EXPORT_PRIVATE QuicFramer {
fec_builder_ = builder;
}
+ // Set entropy calculator to be called from the framer when it needs the
+ // entropy of a truncated ack frame. An entropy calculator must be set or else
+ // the framer will likely crash. If this is called multiple times, only the
+ // last visitor will be used.
+ void set_entropy_calculator(
+ QuicReceivedEntropyHashCalculatorInterface* entropy_calculator) {
+ entropy_calculator_ = entropy_calculator;
+ }
+
QuicErrorCode error() const {
return error_;
}
@@ -139,7 +166,7 @@ class NET_EXPORT_PRIVATE QuicFramer {
// for parsing.
// Return true if the packet was processed succesfully. |payload| must be
// the complete DECRYPTED payload of the revived packet.
- bool ProcessRevivedPacket(const QuicPacketHeader& header,
+ bool ProcessRevivedPacket(QuicPacketHeader* header,
base::StringPiece payload);
// Returns the number of bytes added to the packet for the specified frame,
@@ -148,22 +175,24 @@ class NET_EXPORT_PRIVATE QuicFramer {
size_t GetSerializedFrameLength(
const QuicFrame& frame, size_t free_bytes, bool first_frame);
- // Returns a new QuicPacket, owned by the caller, populated with the fields
- // in |header| and |frames|, or NULL if the packet could not be created.
+ // Returns a SerializedPacket whose |packet| member is owned by the caller,
+ // and is populated with the fields in |header| and |frames|, or is NULL if
+ // the packet could not be created.
// TODO(ianswett): Used for testing only.
- QuicPacket* ConstructFrameDataPacket(const QuicPacketHeader& header,
- const QuicFrames& frames);
-
- // Returns a new QuicPacket from the first |num_frames| frames, owned by the
- // caller or NULL if the packet could not be created. The packet must be of
- // size |packet_size|.
- QuicPacket* ConstructFrameDataPacket(const QuicPacketHeader& header,
- const QuicFrames& frames,
- size_t packet_size);
-
- // Returns a new QuicPacket, owned by the caller, populated with the fields
- // in |header| and |fec|, or NULL if the packet could not be created.
- QuicPacket* ConstructFecPacket(const QuicPacketHeader& header,
+ SerializedPacket ConstructFrameDataPacket(const QuicPacketHeader& header,
+ const QuicFrames& frames);
+
+ // Returns a SerializedPacket whose |packet| member is owned by the caller,
+ // is created from the first |num_frames| frames, or is NULL if the packet
+ // could not be created. The packet must be of size |packet_size|.
+ SerializedPacket ConstructFrameDataPacket(const QuicPacketHeader& header,
+ const QuicFrames& frames,
+ size_t packet_size);
+
+ // Returns a SerializedPacket whose |packet| member is owned by the caller,
+ // and is populated with the fields in |header| and |fec|, or is NULL if the
+ // packet could not be created.
+ SerializedPacket ConstructFecPacket(const QuicPacketHeader& header,
const QuicFecData& fec);
// Returns a new public reset packet, owned by the caller.
@@ -171,7 +200,8 @@ class NET_EXPORT_PRIVATE QuicFramer {
const QuicPublicResetPacket& packet);
// Returns a new encrypted packet, owned by the caller.
- QuicEncryptedPacket* EncryptPacket(const QuicPacket& packet);
+ QuicEncryptedPacket* EncryptPacket(QuicPacketSequenceNumber sequence_number,
+ const QuicPacket& packet);
// Returns the maximum length of plaintext that can be encrypted
// to ciphertext no larger than |ciphertext_size|.
@@ -187,6 +217,9 @@ class NET_EXPORT_PRIVATE QuicFramer {
private:
friend class test::QuicFramerPeer;
+ QuicPacketEntropyHash GetPacketEntropyHash(
+ const QuicPacketHeader& header) const;
+
bool ProcessDataPacket(const QuicPacketPublicHeader& public_header,
const QuicEncryptedPacket& packet);
@@ -210,8 +243,10 @@ class NET_EXPORT_PRIVATE QuicFramer {
QuicCongestionFeedbackFrame* congestion_feedback);
bool ProcessRstStreamFrame();
bool ProcessConnectionCloseFrame();
+ bool ProcessGoAwayFrame();
- bool DecryptPayload(const QuicEncryptedPacket& packet);
+ bool DecryptPayload(QuicPacketSequenceNumber packet_sequence_number,
+ const QuicEncryptedPacket& packet);
// Returns the full packet sequence number from the truncated
// wire format version and the last seen packet sequence number.
@@ -237,6 +272,8 @@ class NET_EXPORT_PRIVATE QuicFramer {
bool AppendConnectionCloseFramePayload(
const QuicConnectionCloseFrame& frame,
QuicDataWriter* builder);
+ bool AppendGoAwayFramePayload(const QuicGoAwayFrame& frame,
+ QuicDataWriter* writer);
bool RaiseError(QuicErrorCode error);
void set_error(QuicErrorCode error) {
@@ -251,6 +288,7 @@ class NET_EXPORT_PRIVATE QuicFramer {
scoped_ptr<QuicDataReader> reader_;
QuicFramerVisitorInterface* visitor_;
QuicFecBuilderInterface* fec_builder_;
+ QuicReceivedEntropyHashCalculatorInterface* entropy_calculator_;
QuicErrorCode error_;
QuicPacketSequenceNumber last_sequence_number_;
// Buffer containing decrypted payload data during parsing.
diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc
index d64df49..da1a665 100644
--- a/net/quic/quic_framer_test.cc
+++ b/net/quic/quic_framer_test.cc
@@ -49,18 +49,33 @@ class QuicFramerPeer {
class TestEncrypter : public QuicEncrypter {
public:
virtual ~TestEncrypter() {}
- virtual QuicData* Encrypt(StringPiece associated_data,
- StringPiece plaintext) OVERRIDE {
+ virtual bool SetKey(StringPiece key) OVERRIDE {
+ return true;
+ }
+ virtual bool SetNoncePrefix(StringPiece nonce_prefix) OVERRIDE {
+ return true;
+ }
+ virtual QuicData* Encrypt(QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece plaintext) OVERRIDE{
+ sequence_number_ = sequence_number;
associated_data_ = associated_data.as_string();
plaintext_ = plaintext.as_string();
return new QuicData(plaintext.data(), plaintext.length());
}
- virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) OVERRIDE {
+ virtual size_t GetKeySize() const OVERRIDE {
+ return 0;
+ }
+ virtual size_t GetNoncePrefixSize() const OVERRIDE{
+ return 0;
+ }
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE {
return ciphertext_size;
}
- virtual size_t GetCiphertextSize(size_t plaintext_size) OVERRIDE {
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE {
return plaintext_size;
}
+ QuicPacketSequenceNumber sequence_number_;
string associated_data_;
string plaintext_;
};
@@ -68,12 +83,21 @@ class TestEncrypter : public QuicEncrypter {
class TestDecrypter : public QuicDecrypter {
public:
virtual ~TestDecrypter() {}
- virtual QuicData* Decrypt(StringPiece associated_data,
- StringPiece ciphertext) OVERRIDE {
+ virtual bool SetKey(StringPiece key) OVERRIDE {
+ return true;
+ }
+ virtual bool SetNoncePrefix(StringPiece nonce_prefix) OVERRIDE {
+ return true;
+ }
+ virtual QuicData* Decrypt(QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece ciphertext) OVERRIDE{
+ sequence_number_ = sequence_number;
associated_data_ = associated_data.as_string();
ciphertext_ = ciphertext.as_string();
return new QuicData(ciphertext.data(), ciphertext.length());
}
+ QuicPacketSequenceNumber sequence_number_;
string associated_data_;
string ciphertext_;
};
@@ -159,6 +183,10 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
connection_close_frame_ = frame;
}
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ goaway_frame_ = frame;
+ }
+
// Counters from the visitor_ callbacks.
int error_count_;
int packet_count_;
@@ -177,6 +205,7 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
string fec_protected_payload_;
QuicRstStreamFrame rst_stream_frame_;
QuicConnectionCloseFrame connection_close_frame_;
+ QuicGoAwayFrame goaway_frame_;
};
class QuicFramerTest : public ::testing::Test {
@@ -186,9 +215,17 @@ class QuicFramerTest : public ::testing::Test {
decrypter_(new test::TestDecrypter()),
framer_(decrypter_, encrypter_) {
framer_.set_visitor(&visitor_);
+ framer_.set_entropy_calculator(&entropy_calculator_);
}
- bool CheckEncryption(StringPiece packet) {
+ bool CheckEncryption(QuicPacketSequenceNumber sequence_number,
+ StringPiece packet) {
+ if (sequence_number != encrypter_->sequence_number_) {
+ LOG(ERROR) << "Encrypted incorrect packet sequence number. expected "
+ << sequence_number << " actual: "
+ << encrypter_->sequence_number_;
+ return false;
+ }
StringPiece associated_data(
packet.substr(kStartOfHashData,
kStartOfEncryptedData - kStartOfHashData));
@@ -209,6 +246,13 @@ class QuicFramerTest : public ::testing::Test {
}
bool CheckDecryption(StringPiece packet) {
+ if (visitor_.header_->packet_sequence_number !=
+ decrypter_->sequence_number_) {
+ LOG(ERROR) << "Decrypted incorrect packet sequence number. expected "
+ << visitor_.header_->packet_sequence_number << " actual: "
+ << decrypter_->sequence_number_;
+ return false;
+ }
StringPiece associated_data(
packet.substr(kStartOfHashData,
kStartOfEncryptedData - kStartOfHashData));
@@ -218,10 +262,10 @@ class QuicFramerTest : public ::testing::Test {
<< decrypter_->associated_data_;
return false;
}
- StringPiece plaintext(packet.substr(kStartOfEncryptedData));
- if (plaintext != decrypter_->ciphertext_) {
+ StringPiece ciphertext(packet.substr(kStartOfEncryptedData));
+ if (ciphertext != decrypter_->ciphertext_) {
LOG(ERROR) << "Decrypted incorrect chipertext data. expected "
- << plaintext << " actual: "
+ << ciphertext << " actual: "
<< decrypter_->ciphertext_;
return false;
}
@@ -266,6 +310,7 @@ class QuicFramerTest : public ::testing::Test {
test::TestDecrypter* decrypter_;
QuicFramer framer_;
test::TestQuicVisitor visitor_;
+ test::TestEntropyCalculator entropy_calculator_;
};
TEST_F(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearEpochStart) {
@@ -347,7 +392,7 @@ TEST_F(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearNextEpoch) {
}
TEST_F(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearNextMax) {
- const uint64 max_number = std::numeric_limits<uint64>::max();
+ const uint64 max_number = numeric_limits<uint64>::max();
const uint64 max_epoch = max_number & ~kMask;
// Cases where the last number was close to the end of the range
@@ -425,8 +470,12 @@ TEST_F(QuicFramerTest, PacketHeader) {
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
visitor_.header_->public_header.guid);
- EXPECT_EQ(0x00, visitor_.header_->public_header.flags);
- EXPECT_EQ(0x00, visitor_.header_->private_flags);
+ EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.header_->public_header.version_flag);
+ EXPECT_FALSE(visitor_.header_->fec_flag);
+ EXPECT_FALSE(visitor_.header_->entropy_flag);
+ EXPECT_FALSE(visitor_.header_->fec_entropy_flag);
+ EXPECT_EQ(0, visitor_.header_->entropy_hash);
EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
visitor_.header_->packet_sequence_number);
EXPECT_EQ(0x00u, visitor_.header_->fec_group);
@@ -499,7 +548,7 @@ TEST_F(QuicFramerTest, InvalidPrivateFlag) {
0xBC, 0x9A, 0x78, 0x56,
0x34, 0x12,
// private flags
- 0x07,
+ 0x08,
// first fec protected packet offset
0xFF,
@@ -558,9 +607,9 @@ TEST_F(QuicFramerTest, PaddingFrame) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
ASSERT_EQ(0u, visitor_.stream_frames_.size());
EXPECT_EQ(0u, visitor_.ack_frames_.size());
@@ -611,9 +660,9 @@ TEST_F(QuicFramerTest, StreamFrame) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
ASSERT_EQ(1u, visitor_.stream_frames_.size());
EXPECT_EQ(0u, visitor_.ack_frames_.size());
@@ -680,9 +729,9 @@ TEST_F(QuicFramerTest, RejectPacket) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
ASSERT_EQ(0u, visitor_.stream_frames_.size());
EXPECT_EQ(0u, visitor_.ack_frames_.size());
@@ -709,13 +758,16 @@ TEST_F(QuicFramerTest, RevivedStreamFrame) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = true;
+ header.entropy_flag = true;
+ header.fec_entropy_flag = false;
header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
header.fec_group = 0;
// Do not encrypt the payload because the revived payload is post-encryption.
- EXPECT_TRUE(framer_.ProcessRevivedPacket(header,
+ EXPECT_TRUE(framer_.ProcessRevivedPacket(&header,
StringPiece(AsChars(payload),
arraysize(payload))));
@@ -724,8 +776,13 @@ TEST_F(QuicFramerTest, RevivedStreamFrame) {
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
visitor_.header_->public_header.guid);
- EXPECT_EQ(0x00, visitor_.header_->public_header.flags);
- EXPECT_EQ(0x00, visitor_.header_->private_flags);
+ EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.header_->public_header.version_flag);
+ EXPECT_TRUE(visitor_.header_->fec_flag);
+ EXPECT_TRUE(visitor_.header_->entropy_flag);
+ EXPECT_FALSE(visitor_.header_->fec_entropy_flag);
+ EXPECT_EQ(1 << (header.packet_sequence_number % 8),
+ visitor_.header_->entropy_hash);
EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
visitor_.header_->packet_sequence_number);
EXPECT_EQ(0x00u, visitor_.header_->fec_group);
@@ -774,9 +831,9 @@ TEST_F(QuicFramerTest, StreamFrameInFecGroup) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(GG_UINT64_C(0x341256789ABA),
visitor_.header_->fec_group);
EXPECT_EQ(string(AsChars(packet) + kStartOfFecProtectedData,
@@ -809,9 +866,13 @@ TEST_F(QuicFramerTest, AckFrame) {
// frame type (ack frame)
0x02,
+ // entropy hash of sent packets till least awaiting - 1.
+ 0xAB,
// least packet sequence number awaiting an ack
0xA0, 0x9A, 0x78, 0x56,
0x34, 0x12,
+ // entropy hash of all received packets.
+ 0xBA,
// largest observed packet sequence number
0xBF, 0x9A, 0x78, 0x56,
0x34, 0x12,
@@ -825,34 +886,41 @@ TEST_F(QuicFramerTest, AckFrame) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(0u, visitor_.stream_frames_.size());
ASSERT_EQ(1u, visitor_.ack_frames_.size());
const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xAB, frame.sent_info.entropy_hash);
+ EXPECT_EQ(0xBA, frame.received_info.entropy_hash);
EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed);
ASSERT_EQ(1u, frame.received_info.missing_packets.size());
- SequenceSet::const_iterator missing_iter =
+ SequenceNumberSet::const_iterator missing_iter =
frame.received_info.missing_packets.begin();
EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked);
// Now test framing boundaries
- for (size_t i = 0; i < 20; ++i) {
+ for (size_t i = 0; i < 22; ++i) {
string expected_error;
if (i < 1) {
expected_error = "Unable to read frame type.";
- } else if (i < 7) {
+ } else if (i < 2) {
+ expected_error = "Unable to read entropy hash for sent packets.";
+ } else if (i < 8) {
expected_error = "Unable to read least unacked.";
- } else if (i < 13) {
+ } else if (i < 9) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < 15) {
expected_error = "Unable to read largest observed.";
- } else if (i < 14) {
+ } else if (i < 16) {
expected_error = "Unable to read num missing packets.";
- } else if (i < 20) {
+ } else {
expected_error = "Unable to read sequence number in missing packets.";
- } CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error,
+ }
+ CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error,
QUIC_INVALID_FRAME_DATA);
}
}
@@ -885,9 +953,9 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameTCP) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(0u, visitor_.stream_frames_.size());
ASSERT_EQ(1u, visitor_.congestion_feedback_frames_.size());
@@ -957,9 +1025,9 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(0u, visitor_.stream_frames_.size());
ASSERT_EQ(1u, visitor_.congestion_feedback_frames_.size());
@@ -1038,9 +1106,9 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameFixRate) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(0u, visitor_.stream_frames_.size());
ASSERT_EQ(1u, visitor_.congestion_feedback_frames_.size());
@@ -1112,9 +1180,6 @@ TEST_F(QuicFramerTest, RstStreamFrame) {
0x04,
// stream id
0x04, 0x03, 0x02, 0x01,
- // offset
- 0x54, 0x76, 0x10, 0x32,
- 0xDC, 0xFE, 0x98, 0xBA,
// error code
0x08, 0x07, 0x06, 0x05,
@@ -1130,26 +1195,22 @@ TEST_F(QuicFramerTest, RstStreamFrame) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.rst_stream_frame_.stream_id);
EXPECT_EQ(0x05060708, visitor_.rst_stream_frame_.error_code);
- EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654),
- visitor_.rst_stream_frame_.offset);
EXPECT_EQ("because I can", visitor_.rst_stream_frame_.error_details);
// Now test framing boundaries
- for (size_t i = 2; i < 32; ++i) {
+ for (size_t i = 2; i < 24; ++i) {
string expected_error;
if (i < 5) {
expected_error = "Unable to read stream_id.";
- } else if (i < 13) {
- expected_error = "Unable to read offset in rst frame.";
- } else if (i < 17) {
+ } else if (i < 9) {
expected_error = "Unable to read rst stream error code.";
- } else if (i < 32) {
+ } else if (i < 24) {
expected_error = "Unable to read rst stream error details.";
}
CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error,
@@ -1186,9 +1247,13 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) {
'n',
// Ack frame.
+ // entropy hash of sent packets till least awaiting - 1.
+ 0xBF,
// least packet sequence number awaiting an ack
0xA0, 0x9A, 0x78, 0x56,
0x34, 0x12,
+ // entropy hash of all received packets.
+ 0xEB,
// largest observed packet sequence number
0xBF, 0x9A, 0x78, 0x56,
0x34, 0x12,
@@ -1202,9 +1267,9 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(0u, visitor_.stream_frames_.size());
@@ -1213,12 +1278,14 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) {
ASSERT_EQ(1u, visitor_.ack_frames_.size());
const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBF, frame.sent_info.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked);
+ EXPECT_EQ(0xEB, frame.received_info.entropy_hash);
EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed);
ASSERT_EQ(1u, frame.received_info.missing_packets.size());
- SequenceSet::const_iterator missing_iter =
+ SequenceNumberSet::const_iterator missing_iter =
frame.received_info.missing_packets.begin();
EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
- EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked);
// Now test framing boundaries
for (size_t i = 2; i < 20; ++i) {
@@ -1233,6 +1300,63 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) {
}
}
+TEST_F(QuicFramerTest, GoAwayFrame) {
+ unsigned char packet[] = {
+ // guid
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // public flags
+ 0x00,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+ // first fec protected packet offset
+ 0xFF,
+
+ // frame type (go away frame)
+ 0x06,
+ // error code
+ 0x08, 0x07, 0x06, 0x05,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // error details length
+ 0x0d, 0x00,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
+
+ EXPECT_EQ(GG_UINT64_C(0x01020304),
+ visitor_.goaway_frame_.last_good_stream_id);
+ EXPECT_EQ(0x05060708, visitor_.goaway_frame_.error_code);
+ EXPECT_EQ("because I can", visitor_.goaway_frame_.reason_phrase);
+
+ // Now test framing boundaries
+ for (size_t i = 2; i < 24; ++i) {
+ string expected_error;
+ if (i < 5) {
+ expected_error = "Unable to read go away error code.";
+ } else if (i < 9) {
+ expected_error = "Unable to read last good stream id.";
+ } else if (i < 24) {
+ expected_error = "Unable to read goaway reason.";
+ }
+ CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error,
+ QUIC_INVALID_GOAWAY_DATA);
+ }
+}
+
TEST_F(QuicFramerTest, PublicResetPacket) {
unsigned char packet[] = {
// guid
@@ -1250,12 +1374,12 @@ TEST_F(QuicFramerTest, PublicResetPacket) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.public_reset_packet_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
visitor_.public_reset_packet_->public_header.guid);
- EXPECT_EQ(PACKET_PUBLIC_FLAGS_RST,
- visitor_.public_reset_packet_->public_header.flags);
+ EXPECT_TRUE(visitor_.public_reset_packet_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.public_reset_packet_->public_header.version_flag);
EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789),
visitor_.public_reset_packet_->nonce_proof);
EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
@@ -1302,9 +1426,9 @@ TEST_F(QuicFramerTest, FecPacket) {
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
- EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet))));
EXPECT_EQ(0u, visitor_.stream_frames_.size());
EXPECT_EQ(0u, visitor_.ack_frames_.size());
@@ -1317,8 +1441,11 @@ TEST_F(QuicFramerTest, FecPacket) {
TEST_F(QuicFramerTest, ConstructPaddingFramePacket) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.fec_entropy_flag = false;
header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
header.fec_group = 0;
@@ -1349,7 +1476,8 @@ TEST_F(QuicFramerTest, ConstructPaddingFramePacket) {
memset(packet + kPacketHeaderSize + 1, 0x00,
kMaxPacketSize - kPacketHeaderSize - 1);
- scoped_ptr<QuicPacket> data(framer_.ConstructFrameDataPacket(header, frames));
+ scoped_ptr<QuicPacket> data(
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1360,8 +1488,11 @@ TEST_F(QuicFramerTest, ConstructPaddingFramePacket) {
TEST_F(QuicFramerTest, ConstructStreamFramePacket) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.fec_entropy_flag = false;
header.packet_sequence_number = GG_UINT64_C(0x77123456789ABC);
header.fec_group = 0;
@@ -1383,8 +1514,8 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacket) {
// packet sequence number
0xBC, 0x9A, 0x78, 0x56,
0x34, 0x12,
- // private flags
- 0x00,
+ // private flags (entropy)
+ 0x02,
// first fec protected packet offset
0xFF,
@@ -1405,7 +1536,8 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacket) {
'r', 'l', 'd', '!',
};
- scoped_ptr<QuicPacket> data(framer_.ConstructFrameDataPacket(header, frames));
+ scoped_ptr<QuicPacket> data(
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1416,15 +1548,20 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacket) {
TEST_F(QuicFramerTest, ConstructAckFramePacket) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.fec_entropy_flag = true;
header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
header.fec_group = 0;
QuicAckFrame ack_frame;
+ ack_frame.received_info.entropy_hash = 0x43;
ack_frame.received_info.largest_observed = GG_UINT64_C(0x770123456789ABF);
ack_frame.received_info.missing_packets.insert(
GG_UINT64_C(0x770123456789ABE));
+ ack_frame.sent_info.entropy_hash = 0x14;
ack_frame.sent_info.least_unacked = GG_UINT64_C(0x770123456789AA0);
QuicFrames frames;
@@ -1439,16 +1576,20 @@ TEST_F(QuicFramerTest, ConstructAckFramePacket) {
// packet sequence number
0xBC, 0x9A, 0x78, 0x56,
0x34, 0x12,
- // private flags
- 0x00,
+ // private flags (entropy & fec_entropy -- not relevant)
+ 0x06,
// first fec protected packet offset
0xFF,
// frame type (ack frame)
0x02,
+ // entropy hash of sent packets till least awaiting - 1.
+ 0x14,
// least packet sequence number awaiting an ack
0xA0, 0x9A, 0x78, 0x56,
0x34, 0x12,
+ // entropy hash of all received packets.
+ 0x43,
// largest observed packet sequence number
0xBF, 0x9A, 0x78, 0x56,
0x34, 0x12,
@@ -1459,7 +1600,8 @@ TEST_F(QuicFramerTest, ConstructAckFramePacket) {
0x34, 0x12,
};
- scoped_ptr<QuicPacket> data(framer_.ConstructFrameDataPacket(header, frames));
+ scoped_ptr<QuicPacket> data(
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1470,8 +1612,11 @@ TEST_F(QuicFramerTest, ConstructAckFramePacket) {
TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.fec_entropy_flag = true;
header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
header.fec_group = 0;
@@ -1493,7 +1638,7 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) {
0xBC, 0x9A, 0x78, 0x56,
0x34, 0x12,
// private flags
- 0x00,
+ 0x04, // (fec_entropy_flag)
// first fec protected packet offset
0xFF,
@@ -1507,7 +1652,8 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) {
0x03, 0x04,
};
- scoped_ptr<QuicPacket> data(framer_.ConstructFrameDataPacket(header, frames));
+ scoped_ptr<QuicPacket> data(
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1518,8 +1664,11 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) {
TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.fec_entropy_flag = false;
header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
header.fec_group = 0;
@@ -1576,7 +1725,8 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) {
0x02, 0x00, 0x00, 0x00,
};
- scoped_ptr<QuicPacket> data(framer_.ConstructFrameDataPacket(header, frames));
+ scoped_ptr<QuicPacket> data(
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1587,8 +1737,11 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) {
TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.fec_entropy_flag = false;
header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
header.fec_group = 0;
@@ -1622,7 +1775,8 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) {
0x01, 0x02, 0x03, 0x04,
};
- scoped_ptr<QuicPacket> data(framer_.ConstructFrameDataPacket(header, frames));
+ scoped_ptr<QuicPacket> data(
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1633,8 +1787,11 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) {
TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInvalidFeedback) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.fec_entropy_flag = false;
header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
header.fec_group = 0;
@@ -1645,22 +1802,25 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInvalidFeedback) {
QuicFrames frames;
frames.push_back(QuicFrame(&congestion_feedback_frame));
- scoped_ptr<QuicPacket> data(framer_.ConstructFrameDataPacket(header, frames));
+ scoped_ptr<QuicPacket> data(
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(data == NULL);
}
TEST_F(QuicFramerTest, ConstructRstFramePacket) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.fec_entropy_flag = false;
header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
header.fec_group = 0;
QuicRstStreamFrame rst_frame;
rst_frame.stream_id = 0x01020304;
rst_frame.error_code = static_cast<QuicErrorCode>(0x05060708);
- rst_frame.offset = GG_UINT64_C(0xBA98FEDC32107654);
rst_frame.error_details = "because I can";
unsigned char packet[] = {
@@ -1681,9 +1841,6 @@ TEST_F(QuicFramerTest, ConstructRstFramePacket) {
0x04,
// stream id
0x04, 0x03, 0x02, 0x01,
- // offset
- 0x54, 0x76, 0x10, 0x32,
- 0xDC, 0xFE, 0x98, 0xBA,
// error code
0x08, 0x07, 0x06, 0x05,
// error details length
@@ -1698,7 +1855,8 @@ TEST_F(QuicFramerTest, ConstructRstFramePacket) {
QuicFrames frames;
frames.push_back(QuicFrame(&rst_frame));
- scoped_ptr<QuicPacket> data(framer_.ConstructFrameDataPacket(header, frames));
+ scoped_ptr<QuicPacket> data(
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1709,8 +1867,11 @@ TEST_F(QuicFramerTest, ConstructRstFramePacket) {
TEST_F(QuicFramerTest, ConstructCloseFramePacket) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.fec_entropy_flag = false;
header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
header.fec_group = 0;
@@ -1719,8 +1880,10 @@ TEST_F(QuicFramerTest, ConstructCloseFramePacket) {
close_frame.error_details = "because I can";
QuicAckFrame* ack_frame = &close_frame.ack_frame;
+ ack_frame->received_info.entropy_hash = 0x43;
ack_frame->received_info.largest_observed = GG_UINT64_C(0x0123456789ABF);
ack_frame->received_info.missing_packets.insert(GG_UINT64_C(0x0123456789ABE));
+ ack_frame->sent_info.entropy_hash = 0xE0;
ack_frame->sent_info.least_unacked = GG_UINT64_C(0x0123456789AA0);
QuicFrames frames;
@@ -1736,7 +1899,7 @@ TEST_F(QuicFramerTest, ConstructCloseFramePacket) {
0xBC, 0x9A, 0x78, 0x56,
0x34, 0x12,
// private flags
- 0x00,
+ 0x02,
// first fec protected packet offset
0xFF,
@@ -1753,9 +1916,13 @@ TEST_F(QuicFramerTest, ConstructCloseFramePacket) {
'n',
// Ack frame.
+ // entropy hash of sent packets till least awaiting - 1.
+ 0xE0,
// least packet sequence number awaiting an ack
0xA0, 0x9A, 0x78, 0x56,
0x34, 0x12,
+ // entropy hash of all received packets.
+ 0x43,
// largest observed packet sequence number
0xBF, 0x9A, 0x78, 0x56,
0x34, 0x12,
@@ -1766,7 +1933,65 @@ TEST_F(QuicFramerTest, ConstructCloseFramePacket) {
0x34, 0x12,
};
- scoped_ptr<QuicPacket> data(framer_.ConstructFrameDataPacket(header, frames));
+ scoped_ptr<QuicPacket> data(
+ framer_.ConstructFrameDataPacket(header, frames).packet);
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_F(QuicFramerTest, ConstructGoAwayPacket) {
+ QuicPacketHeader header;
+ header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.fec_entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicGoAwayFrame goaway_frame;
+ goaway_frame.error_code = static_cast<QuicErrorCode>(0x05060708);
+ goaway_frame.last_good_stream_id = 0x01020304;
+ goaway_frame.reason_phrase = "because I can";
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&goaway_frame));
+
+ unsigned char packet[] = {
+ // guid
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // public flags
+ 0x00,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x02,
+ // first fec protected packet offset
+ 0xFF,
+
+ // frame type (go away frame)
+ 0x06,
+ // error code
+ 0x08, 0x07, 0x06, 0x05,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // error details length
+ 0x0d, 0x00,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ scoped_ptr<QuicPacket> data(
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1777,7 +2002,8 @@ TEST_F(QuicFramerTest, ConstructCloseFramePacket) {
TEST_F(QuicFramerTest, ConstructPublicResetPacket) {
QuicPublicResetPacket reset_packet;
reset_packet.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- reset_packet.public_header.flags = PACKET_PUBLIC_FLAGS_RST;
+ reset_packet.public_header.reset_flag = true;
+ reset_packet.public_header.version_flag = false;
reset_packet.rejected_sequence_number = GG_UINT64_C(0x123456789ABC);
reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789);
@@ -1807,8 +2033,11 @@ TEST_F(QuicFramerTest, ConstructPublicResetPacket) {
TEST_F(QuicFramerTest, ConstructFecPacket) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_FEC;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = true;
+ header.entropy_flag = true;
+ header.fec_entropy_flag = false;
header.packet_sequence_number = (GG_UINT64_C(0x123456789ABC));
header.fec_group = GG_UINT64_C(0x123456789ABB);;
@@ -1826,7 +2055,7 @@ TEST_F(QuicFramerTest, ConstructFecPacket) {
0xBC, 0x9A, 0x78, 0x56,
0x34, 0x12,
// private flags
- 0x01,
+ 0x03,
// first fec protected packet offset
0x01,
@@ -1837,7 +2066,8 @@ TEST_F(QuicFramerTest, ConstructFecPacket) {
'm', 'n', 'o', 'p',
};
- scoped_ptr<QuicPacket> data(framer_.ConstructFecPacket(header, fec_data));
+ scoped_ptr<QuicPacket> data(
+ framer_.ConstructFecPacket(header, fec_data).packet);
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1846,6 +2076,7 @@ TEST_F(QuicFramerTest, ConstructFecPacket) {
}
TEST_F(QuicFramerTest, EncryptPacket) {
+ QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC);
unsigned char packet[] = {
// guid
0x10, 0x32, 0x54, 0x76,
@@ -1869,17 +2100,19 @@ TEST_F(QuicFramerTest, EncryptPacket) {
scoped_ptr<QuicPacket> raw(
QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false));
- scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*raw));
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.EncryptPacket(sequence_number, *raw));
ASSERT_TRUE(encrypted.get() != NULL);
- EXPECT_TRUE(CheckEncryption(StringPiece(AsChars(packet), arraysize(packet))));
+ EXPECT_TRUE(CheckEncryption(sequence_number,
+ StringPiece(AsChars(packet), arraysize(packet))));
}
// TODO(rch): re-enable after https://codereview.chromium.org/11820005/
// lands. Currently this is causing valgrind problems, but it should be
// fixed in the followup CL.
TEST_F(QuicFramerTest, DISABLED_CalculateLargestReceived) {
- SequenceSet missing;
+ SequenceNumberSet missing;
missing.insert(1);
missing.insert(5);
missing.insert(7);
@@ -1900,8 +2133,11 @@ TEST_F(QuicFramerTest, DISABLED_CalculateLargestReceived) {
TEST_F(QuicFramerTest, DISABLED_Truncation) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.fec_entropy_flag = false;
header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
header.fec_group = 0;
@@ -1923,11 +2159,11 @@ TEST_F(QuicFramerTest, DISABLED_Truncation) {
frames.push_back(frame);
scoped_ptr<QuicPacket> raw_ack_packet(
- framer_.ConstructFrameDataPacket(header, frames));
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(raw_ack_packet != NULL);
scoped_ptr<QuicEncryptedPacket> ack_packet(
- framer_.EncryptPacket(*raw_ack_packet));
+ framer_.EncryptPacket(header.packet_sequence_number, *raw_ack_packet));
// Create a packet with just connection close.
frames.clear();
@@ -1936,11 +2172,11 @@ TEST_F(QuicFramerTest, DISABLED_Truncation) {
frames.push_back(frame);
scoped_ptr<QuicPacket> raw_close_packet(
- framer_.ConstructFrameDataPacket(header, frames));
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(raw_close_packet != NULL);
scoped_ptr<QuicEncryptedPacket> close_packet(
- framer_.EncryptPacket(*raw_close_packet));
+ framer_.EncryptPacket(header.packet_sequence_number, *raw_close_packet));
// Now make sure we can turn our ack packet back into an ack frame
ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
@@ -1952,8 +2188,11 @@ TEST_F(QuicFramerTest, DISABLED_Truncation) {
TEST_F(QuicFramerTest, CleanTruncation) {
QuicPacketHeader header;
header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.fec_entropy_flag = false;
header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
header.fec_group = 0;
@@ -1975,11 +2214,11 @@ TEST_F(QuicFramerTest, CleanTruncation) {
frames.push_back(frame);
scoped_ptr<QuicPacket> raw_ack_packet(
- framer_.ConstructFrameDataPacket(header, frames));
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(raw_ack_packet != NULL);
scoped_ptr<QuicEncryptedPacket> ack_packet(
- framer_.EncryptPacket(*raw_ack_packet));
+ framer_.EncryptPacket(header.packet_sequence_number, *raw_ack_packet));
// Create a packet with just connection close.
frames.clear();
@@ -1988,11 +2227,11 @@ TEST_F(QuicFramerTest, CleanTruncation) {
frames.push_back(frame);
scoped_ptr<QuicPacket> raw_close_packet(
- framer_.ConstructFrameDataPacket(header, frames));
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(raw_close_packet != NULL);
scoped_ptr<QuicEncryptedPacket> close_packet(
- framer_.EncryptPacket(*raw_close_packet));
+ framer_.EncryptPacket(header.packet_sequence_number, *raw_close_packet));
// Now make sure we can turn our ack packet back into an ack frame
ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
@@ -2009,7 +2248,7 @@ TEST_F(QuicFramerTest, CleanTruncation) {
size_t original_raw_length = raw_ack_packet->length();
raw_ack_packet.reset(
- framer_.ConstructFrameDataPacket(header, frames));
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(raw_ack_packet != NULL);
EXPECT_EQ(original_raw_length, raw_ack_packet->length());
@@ -2020,10 +2259,93 @@ TEST_F(QuicFramerTest, CleanTruncation) {
original_raw_length = raw_close_packet->length();
raw_close_packet.reset(
- framer_.ConstructFrameDataPacket(header, frames));
+ framer_.ConstructFrameDataPacket(header, frames).packet);
ASSERT_TRUE(raw_ack_packet != NULL);
EXPECT_EQ(original_raw_length, raw_close_packet->length());
}
+TEST_F(QuicFramerTest, EntropyFlagTest) {
+ unsigned char packet[] = {
+ // guid
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // public flags
+ 0x00,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Entropy
+ 0x02,
+ // first fec protected packet offset
+ 0xFF,
+
+ // frame type (stream frame)
+ 0x01,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // fin
+ 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(1 << 4, visitor_.header_->entropy_hash);
+ EXPECT_FALSE(visitor_.header_->fec_flag);
+};
+
+TEST_F(QuicFramerTest, FecEntropyFlagTest) {
+ unsigned char packet[] = {
+ // guid
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // public flags
+ 0x00,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Flags: Entropy, FEC-Entropy, FEC
+ 0x07,
+ // first fec protected packet offset
+ 0xFF,
+
+ // frame type (stream frame)
+ 0x01,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // fin
+ 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(visitor_.header_->fec_flag);
+ EXPECT_TRUE(visitor_.header_->entropy_flag);
+ EXPECT_TRUE(visitor_.header_->fec_entropy_flag);
+ EXPECT_EQ(1 << 4, visitor_.header_->entropy_hash);
+};
+
} // namespace test
} // namespace net
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index 4a5f333..a16f2f9 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -17,6 +17,7 @@
#include "net/quic/quic_connection.h"
#include "net/quic/quic_connection_helper.h"
#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/mock_random.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/test_task_runner.h"
@@ -114,7 +115,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
read_buffer_(new IOBufferWithSize(4096)),
guid_(2),
framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)),
- creator_(guid_, &framer_) {
+ creator_(guid_, &framer_, &random_) {
IPAddressNumber ip;
CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip));
peer_addr_ = IPEndPoint(ip, 443);
@@ -165,7 +166,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
runner_ = new TestTaskRunner(&clock_);
send_algorithm_ = new MockSendAlgorithm();
receive_algorithm_ = new TestReceiveAlgorithm(NULL);
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_)).
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _)).
WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
helper_ = new QuicConnectionHelper(runner_.get(), &clock_,
&random_generator_, socket);
@@ -240,17 +241,19 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
InitializeHeader(sequence_number);
QuicAckFrame ack(largest_received, least_unacked);
+ ack.sent_info.entropy_hash = 0;
+ ack.received_info.entropy_hash = 0;
+
return ConstructPacket(header_, QuicFrame(&ack));
}
// Returns a newly created packet to send ack data.
QuicEncryptedPacket* ConstructRstPacket(
QuicPacketSequenceNumber sequence_number,
- QuicStreamId stream_id,
- QuicStreamOffset offset) {
+ QuicStreamId stream_id) {
InitializeHeader(sequence_number);
- QuicRstStreamFrame rst(stream_id, offset, QUIC_NO_ERROR);
+ QuicRstStreamFrame rst(stream_id, QUIC_NO_ERROR);
return ConstructPacket(header_, QuicFrame(&rst));
}
@@ -278,10 +281,13 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
private:
void InitializeHeader(QuicPacketSequenceNumber sequence_number) {
header_.public_header.guid = guid_;
- header_.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
header_.packet_sequence_number = sequence_number;
header_.fec_group = 0;
- header_.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header_.fec_entropy_flag = false;
+ header_.entropy_flag = false;
+ header_.fec_flag = false;
}
QuicEncryptedPacket* ConstructPacket(const QuicPacketHeader& header,
@@ -289,14 +295,15 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
QuicFrames frames;
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer_.ConstructFrameDataPacket(header_, frames));
- return framer_.EncryptPacket(*packet);
+ framer_.ConstructFrameDataPacket(header_, frames).packet);
+ return framer_.EncryptPacket(header.packet_sequence_number, *packet);
}
const QuicGuid guid_;
QuicFramer framer_;
IPEndPoint self_addr_;
IPEndPoint peer_addr_;
+ MockRandom random_;
QuicPacketCreator creator_;
QuicPacketHeader header_;
scoped_ptr<StaticSocketDataProvider> socket_data_;
@@ -468,7 +475,7 @@ TEST_P(QuicHttpStreamTest, SendPostRequest) {
TEST_P(QuicHttpStreamTest, DestroyedEarly) {
SetRequestString("GET", "/");
AddWrite(SYNCHRONOUS, ConstructDataPacket(1, kFin, 0, request_data_));
- AddWrite(SYNCHRONOUS, ConstructRstPacket(2, 3, request_data_.length()));
+ AddWrite(SYNCHRONOUS, ConstructRstPacket(2, 3));
AddWrite(SYNCHRONOUS, ConstructAckPacket(3, 2, 2));
use_closing_stream_ = true;
Initialize();
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index b9dae53..35aa947 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -91,14 +91,14 @@ class QuicNetworkTransactionTest : public PlatformTest {
host));
QuicFramer framer(QuicDecrypter::Create(kNULL),
QuicEncrypter::Create(kNULL));
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*chlo));
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(1, *chlo));
}
scoped_ptr<QuicEncryptedPacket> ConstructShlo() {
scoped_ptr<QuicPacket> shlo(ConstructHandshakePacket(0xDEADBEEF, kSHLO));
QuicFramer framer(QuicDecrypter::Create(kNULL),
QuicEncrypter::Create(kNULL));
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*shlo));
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(1, *shlo));
}
scoped_ptr<QuicEncryptedPacket> ConstructRstPacket(
@@ -106,12 +106,15 @@ class QuicNetworkTransactionTest : public PlatformTest {
QuicStreamId stream_id) {
QuicPacketHeader header;
header.public_header.guid = 0xDEADBEEF;
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
header.packet_sequence_number = num;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_entropy_flag = false;
header.fec_group = 0;
- QuicRstStreamFrame rst(stream_id, 0, QUIC_NO_ERROR);
+ QuicRstStreamFrame rst(stream_id, QUIC_NO_ERROR);
return scoped_ptr<QuicEncryptedPacket>(
ConstructPacket(header, QuicFrame(&rst)));
}
@@ -121,9 +124,12 @@ class QuicNetworkTransactionTest : public PlatformTest {
QuicPacketSequenceNumber least_unacked) {
QuicPacketHeader header;
header.public_header.guid = 0xDEADBEEF;
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
header.packet_sequence_number = 3;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_entropy_flag = false;
header.fec_group = 0;
QuicAckFrame ack(largest_received, least_unacked);
@@ -139,8 +145,9 @@ class QuicNetworkTransactionTest : public PlatformTest {
frames.push_back(QuicFrame(&ack));
frames.push_back(QuicFrame(&feedback));
scoped_ptr<QuicPacket> packet(
- framer.ConstructFrameDataPacket(header, frames));
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*packet));
+ framer.ConstructFrameDataPacket(header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(
+ framer.EncryptPacket(header.packet_sequence_number, *packet));
}
std::string GetRequestString(const std::string& method,
@@ -190,16 +197,20 @@ class QuicNetworkTransactionTest : public PlatformTest {
QuicFrames frames;
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer.ConstructFrameDataPacket(header, frames));
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*packet));
+ framer.ConstructFrameDataPacket(header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(
+ framer.EncryptPacket(header.packet_sequence_number, *packet));
}
void InitializeHeader(QuicPacketSequenceNumber sequence_number) {
header_.public_header.guid = random_generator_.RandUint64();
- header_.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
header_.packet_sequence_number = sequence_number;
header_.fec_group = 0;
- header_.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header_.entropy_flag = false;
+ header_.fec_flag = false;
+ header_.fec_entropy_flag = false;
}
void CreateSession() {
diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc
index e80ea85..bc80bd3 100644
--- a/net/quic/quic_packet_creator.cc
+++ b/net/quic/quic_packet_creator.cc
@@ -5,8 +5,11 @@
#include "net/quic/quic_packet_creator.h"
#include "base/logging.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_fec_group.h"
#include "net/quic/quic_utils.h"
+
using base::StringPiece;
using std::make_pair;
using std::min;
@@ -15,9 +18,12 @@ using std::vector;
namespace net {
-QuicPacketCreator::QuicPacketCreator(QuicGuid guid, QuicFramer* framer)
+QuicPacketCreator::QuicPacketCreator(QuicGuid guid,
+ QuicFramer* framer,
+ QuicRandom* random_generator)
: guid_(guid),
framer_(framer),
+ random_generator_(random_generator),
sequence_number_(0),
fec_group_number_(0),
packet_size_(kPacketHeaderSize) {
@@ -48,6 +54,10 @@ void QuicPacketCreator::MaybeStartFEC() {
}
}
+bool QuicPacketCreator::HasRoomForStreamFrame() const {
+ return (BytesFree() > kFrameTypeSize + kMinStreamFrameLength);
+}
+
size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
StringPiece data,
QuicStreamOffset offset,
@@ -55,10 +65,11 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
QuicFrame* frame) {
DCHECK_GT(options_.max_packet_length,
QuicUtils::StreamFramePacketOverhead(1));
- const size_t free_bytes = BytesFree();
- DCHECK_GE(free_bytes, kFrameTypeSize + kMinStreamFrameLength);
+ DCHECK(HasRoomForStreamFrame());
+ const size_t free_bytes = BytesFree();
size_t bytes_consumed = 0;
+
if (data.size() != 0) {
size_t max_data_len = free_bytes - kFrameTypeSize - kMinStreamFrameLength;
bytes_consumed = min<size_t>(max_data_len, data.size());
@@ -75,76 +86,69 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
return bytes_consumed;
}
-PacketPair QuicPacketCreator::SerializeAllFrames(const QuicFrames& frames) {
+SerializedPacket QuicPacketCreator::SerializeAllFrames(
+ const QuicFrames& frames) {
+ // TODO(satyamshekhar): Verify that this DCHECK won't fail. What about queued
+ // frames from SendStreamData()[send_stream_should_flush_ == false &&
+ // data.empty() == true] and retransmit due to RTO.
DCHECK_EQ(0u, queued_frames_.size());
for (size_t i = 0; i < frames.size(); ++i) {
- bool success = AddFrame(frames[i]);
+ bool success = AddFrame(frames[i], false);
DCHECK(success);
}
- return SerializePacket(NULL);
+ SerializedPacket packet = SerializePacket();
+ DCHECK(packet.retransmittable_frames == NULL);
+ return packet;
}
bool QuicPacketCreator::HasPendingFrames() {
return !queued_frames_.empty();
}
-size_t QuicPacketCreator::BytesFree() {
+size_t QuicPacketCreator::BytesFree() const {
const size_t max_plaintext_size =
framer_->GetMaxPlaintextSize(options_.max_packet_length);
if (packet_size_ > max_plaintext_size) {
return 0;
- } else {
- return max_plaintext_size - packet_size_;
}
+ return max_plaintext_size - packet_size_;
}
-bool QuicPacketCreator::AddFrame(const QuicFrame& frame) {
- size_t frame_len = framer_->GetSerializedFrameLength(
- frame, BytesFree(), queued_frames_.empty());
- if (frame_len == 0) {
- return false;
- }
- packet_size_ += frame_len;
- queued_frames_.push_back(frame);
- return true;
+bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame) {
+ return AddFrame(frame, true);
}
-PacketPair QuicPacketCreator::SerializePacket(
- QuicFrames* retransmittable_frames) {
+SerializedPacket QuicPacketCreator::SerializePacket() {
DCHECK_EQ(false, queued_frames_.empty());
QuicPacketHeader header;
- FillPacketHeader(fec_group_number_, PACKET_PRIVATE_FLAGS_NONE, &header);
+ FillPacketHeader(fec_group_number_, false, false, &header);
- QuicPacket* packet = framer_->ConstructFrameDataPacket(
+ SerializedPacket serialized = framer_->ConstructFrameDataPacket(
header, queued_frames_, packet_size_);
- for (size_t i = 0; i < queued_frames_.size(); ++i) {
- if (retransmittable_frames != NULL && ShouldRetransmit(queued_frames_[i])) {
- retransmittable_frames->push_back(queued_frames_[i]);
- }
- }
queued_frames_.clear();
packet_size_ = kPacketHeaderSize;
- return make_pair(header.packet_sequence_number, packet);
+ serialized.retransmittable_frames = queued_retransmittable_frames_.release();
+ return serialized;
}
-QuicPacketCreator::PacketPair QuicPacketCreator::SerializeFec() {
+SerializedPacket QuicPacketCreator::SerializeFec() {
DCHECK_LT(0u, fec_group_->NumReceivedPackets());
DCHECK_EQ(0u, queued_frames_.size());
QuicPacketHeader header;
- FillPacketHeader(fec_group_number_, PACKET_PRIVATE_FLAGS_FEC, &header);
-
+ FillPacketHeader(fec_group_number_, true,
+ fec_group_->entropy_parity(), &header);
QuicFecData fec_data;
fec_data.fec_group = fec_group_->min_protected_packet();
- fec_data.redundancy = fec_group_->parity();
- QuicPacket* packet = framer_->ConstructFecPacket(header, fec_data);
+ fec_data.redundancy = fec_group_->payload_parity();
+ SerializedPacket serialized = framer_->ConstructFecPacket(header, fec_data);
fec_group_.reset(NULL);
fec_group_number_ = 0;
- DCHECK(packet);
- DCHECK_GE(options_.max_packet_length, packet->length());
- return make_pair(header.packet_sequence_number, packet);
+ DCHECK(serialized.packet);
+ DCHECK_GE(options_.max_packet_length, serialized.packet->length());
+ return serialized;
}
-QuicPacketCreator::PacketPair QuicPacketCreator::CloseConnection(
+SerializedPacket QuicPacketCreator::SerializeConnectionClose(
QuicConnectionCloseFrame* close_frame) {
QuicFrames frames;
frames.push_back(QuicFrame(close_frame));
@@ -152,12 +156,22 @@ QuicPacketCreator::PacketPair QuicPacketCreator::CloseConnection(
}
void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group,
- QuicPacketPrivateFlags flags,
+ bool fec_flag,
+ bool fec_entropy_flag,
QuicPacketHeader* header) {
header->public_header.guid = guid_;
- header->public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
- header->private_flags = flags;
+ header->public_header.reset_flag = false;
+ header->public_header.version_flag = false;
+ header->fec_flag = fec_flag;
+ header->fec_entropy_flag = fec_entropy_flag;
header->packet_sequence_number = ++sequence_number_;
+ if (header->packet_sequence_number == 1) {
+ // TODO(satyamshekhar): No entropy in the first message.
+ // For crypto tests to pass. Fix this by using deterministic QuicRandom.
+ header->entropy_flag = 0;
+ } else {
+ header->entropy_flag = random_generator_->RandBool();
+ }
header->fec_group = fec_group;
}
@@ -166,4 +180,30 @@ bool QuicPacketCreator::ShouldRetransmit(const QuicFrame& frame) {
frame.type != PADDING_FRAME;
}
+bool QuicPacketCreator::AddFrame(const QuicFrame& frame,
+ bool save_retransmittable_frames) {
+ size_t frame_len = framer_->GetSerializedFrameLength(
+ frame, BytesFree(), queued_frames_.empty());
+ if (frame_len == 0) {
+ return false;
+ }
+ packet_size_ += frame_len;
+
+ if (save_retransmittable_frames && ShouldRetransmit(frame)) {
+ if (queued_retransmittable_frames_.get() == NULL) {
+ queued_retransmittable_frames_.reset(new RetransmittableFrames());
+ }
+ if (frame.type == STREAM_FRAME) {
+ queued_frames_.push_back(
+ queued_retransmittable_frames_->AddStreamFrame(frame.stream_frame));
+ } else {
+ queued_frames_.push_back(
+ queued_retransmittable_frames_->AddNonStreamFrame(frame));
+ }
+ } else {
+ queued_frames_.push_back(frame);
+ }
+ return true;
+}
+
} // namespace net
diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h
index b5eb9b8..23301ec 100644
--- a/net/quic/quic_packet_creator.h
+++ b/net/quic/quic_packet_creator.h
@@ -20,10 +20,10 @@
namespace net {
+class QuicRandom;
+
class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
public:
- typedef std::pair<QuicPacketSequenceNumber, QuicPacket*> PacketPair;
-
// Options for controlling how packets are created.
struct Options {
Options()
@@ -38,7 +38,10 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
size_t max_packets_per_fec_group;
};
- QuicPacketCreator(QuicGuid guid, QuicFramer* framer);
+ // QuicRandom* required for packet entropy.
+ QuicPacketCreator(QuicGuid guid,
+ QuicFramer* framer,
+ QuicRandom* random_generator);
virtual ~QuicPacketCreator();
@@ -54,6 +57,8 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// and there is not already an FEC group open.
void MaybeStartFEC();
+ bool HasRoomForStreamFrame() const;
+
// Converts a raw payload to a frame which fits into the currently open
// packet if there is one. Returns the number of bytes consumed from data.
// If data is empty and fin is true, the expected behavior is to consume the
@@ -64,30 +69,41 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
bool fin,
QuicFrame* frame);
- // Serializes all frames into a single packet. All frames must fit into a
- // single packet.
- PacketPair SerializeAllFrames(const QuicFrames& frames);
+ // Serializes all frames into a single packet. All frames must fit into a
+ // single packet. Also, sets the entropy hash of the serialized packet to a
+ // random bool and returns that value as a member of SerializedPacket.
+ // Never returns an RetransmittableFrames in SerializedPacket
+ SerializedPacket SerializeAllFrames(const QuicFrames& frames);
// Returns true if there are frames pending to be serialized.
bool HasPendingFrames();
// Returns the number of bytes which are free to frames in the current packet.
- size_t BytesFree();
+ size_t BytesFree() const;
// Adds |frame| to the packet creator's list of frames to be serialized.
// Returns false if the frame doesn't fit into the current packet.
- bool AddFrame(const QuicFrame& frame);
+ bool AddSavedFrame(const QuicFrame& frame);
// Serializes all frames which have been added and adds any which should be
- // retransmitted to |retransmittable_frames| if it's not NULL.
- PacketPair SerializePacket(QuicFrames* retransmittable_frames);
-
- // Packetize FEC data.
- PacketPair SerializeFec();
+ // retransmitted to |retransmittable_frames| if it's not NULL. All frames must
+ // fit into a single packet. Sets the entropy hash of the serialized
+ // packet to a random bool and returns that value as a member of
+ // SerializedPacket. Also, sets |serialized_frames| in the SerializedPacket
+ // to the corresponding RetransmittableFrames if any frames are to be
+ // retransmitted.
+ SerializedPacket SerializePacket();
+
+ // Packetize FEC data. All frames must fit into a single packet. Also, sets
+ // the entropy hash of the serialized packet to a random bool and returns
+ // that value as a member of SerializedPacket.
+ SerializedPacket SerializeFec();
// Creates a packet with connection close frame. Caller owns the created
- // packet.
- PacketPair CloseConnection(QuicConnectionCloseFrame* close_frame);
+ // packet. Also, sets the entropy hash of the serialized packet to a random
+ // bool and returns that value as a member of SerializedPacket.
+ SerializedPacket SerializeConnectionClose(
+ QuicConnectionCloseFrame* close_frame);
QuicPacketSequenceNumber sequence_number() const {
return sequence_number_;
@@ -105,17 +121,24 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
static bool ShouldRetransmit(const QuicFrame& frame);
void FillPacketHeader(QuicFecGroupNumber fec_group,
- QuicPacketPrivateFlags flags,
+ bool fec_flag,
+ bool fec_entropy_flag,
QuicPacketHeader* header);
+ // Allows a frame to be added without creating retransmittable frames.
+ // Particularly useful for retransmits using SerializeAllFrames().
+ bool AddFrame(const QuicFrame& frame, bool save_retransmittable_frames);
+
Options options_;
QuicGuid guid_;
QuicFramer* framer_;
+ QuicRandom* random_generator_;
QuicPacketSequenceNumber sequence_number_;
QuicFecGroupNumber fec_group_number_;
scoped_ptr<QuicFecGroup> fec_group_;
size_t packet_size_;
QuicFrames queued_frames_;
+ scoped_ptr<RetransmittableFrames> queued_retransmittable_frames_;
};
} // namespace net
diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc
index 1c202d0..c060da7 100644
--- a/net/quic/quic_packet_creator_test.cc
+++ b/net/quic/quic_packet_creator_test.cc
@@ -6,6 +6,9 @@
#include "base/stl_util.h"
#include "net/quic/crypto/null_encrypter.h"
+#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_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -28,17 +31,15 @@ class QuicPacketCreatorTest : public ::testing::Test {
sequence_number_(0),
guid_(2),
data_("foo"),
- creator_(guid_, &framer_) {
+ creator_(guid_, &framer_, QuicRandom::GetInstance()) {
framer_.set_visitor(&framer_visitor_);
}
~QuicPacketCreatorTest() {
- for (QuicFrames::iterator it = frames_.begin(); it != frames_.end(); ++it) {
- QuicConnection::DeleteEnclosedFrame(&(*it));
- }
}
void ProcessPacket(QuicPacket* packet) {
- scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet));
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.EncryptPacket(sequence_number_, *packet));
framer_.ProcessPacket(*encrypted);
}
@@ -65,7 +66,8 @@ class QuicPacketCreatorTest : public ::testing::Test {
TEST_F(QuicPacketCreatorTest, SerializeFrame) {
frames_.push_back(QuicFrame(new QuicStreamFrame(
0u, false, 0u, StringPiece(""))));
- PacketPair pair = creator_.SerializeAllFrames(frames_);
+ SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
+ delete frames_[0].stream_frame;
{
InSequence s;
@@ -74,8 +76,8 @@ TEST_F(QuicPacketCreatorTest, SerializeFrame) {
EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
}
- ProcessPacket(pair.second);
- delete pair.second;
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
}
TEST_F(QuicPacketCreatorTest, SerializeFrames) {
@@ -84,7 +86,10 @@ TEST_F(QuicPacketCreatorTest, SerializeFrames) {
0u, false, 0u, StringPiece(""))));
frames_.push_back(QuicFrame(new QuicStreamFrame(
0u, true, 0u, StringPiece(""))));
- PacketPair pair = creator_.SerializeAllFrames(frames_);
+ SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
+ delete frames_[0].ack_frame;
+ delete frames_[1].stream_frame;
+ delete frames_[2].stream_frame;
{
InSequence s;
@@ -95,8 +100,8 @@ TEST_F(QuicPacketCreatorTest, SerializeFrames) {
EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
}
- ProcessPacket(pair.second);
- delete pair.second;
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
}
TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
@@ -106,7 +111,8 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
frames_.push_back(QuicFrame(new QuicStreamFrame(
0u, false, 0u, StringPiece(""))));
- PacketPair pair = creator_.SerializeAllFrames(frames_);
+ SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
+ delete frames_[0].stream_frame;
{
InSequence s;
@@ -116,14 +122,14 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
}
- ProcessPacket(pair.second);
- delete pair.second;
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
ASSERT_FALSE(creator_.ShouldSendFec(false));
ASSERT_TRUE(creator_.ShouldSendFec(true));
- pair = creator_.SerializeFec();
- ASSERT_EQ(2u, pair.first);
+ serialized = creator_.SerializeFec();
+ ASSERT_EQ(2u, serialized.sequence_number);
{
InSequence s;
@@ -132,17 +138,17 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
EXPECT_CALL(framer_visitor_, OnFecData(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
}
- ProcessPacket(pair.second);
- delete pair.second;
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, CloseConnection) {
+TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) {
QuicConnectionCloseFrame frame;
frame.error_code = QUIC_NO_ERROR;
frame.ack_frame = QuicAckFrame(0u, 0u);
- PacketPair pair = creator_.CloseConnection(&frame);
- ASSERT_EQ(1u, pair.first);
+ SerializedPacket serialized = creator_.SerializeConnectionClose(&frame);
+ ASSERT_EQ(1u, serialized.sequence_number);
ASSERT_EQ(1u, creator_.sequence_number());
InSequence s;
@@ -152,8 +158,8 @@ TEST_F(QuicPacketCreatorTest, CloseConnection) {
EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
- ProcessPacket(pair.second);
- delete pair.second;
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
}
TEST_F(QuicPacketCreatorTest, CreateStreamFrame) {
@@ -200,40 +206,41 @@ TEST_F(QuicPacketCreatorTest, AddFrameAndSerialize) {
// Add a variety of frame types and then a padding frame.
QuicAckFrame ack_frame;
- EXPECT_TRUE(creator_.AddFrame(QuicFrame(&ack_frame)));
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
EXPECT_TRUE(creator_.HasPendingFrames());
QuicCongestionFeedbackFrame congestion_feedback;
congestion_feedback.type = kFixRate;
- EXPECT_TRUE(creator_.AddFrame(QuicFrame(&congestion_feedback)));
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&congestion_feedback)));
EXPECT_TRUE(creator_.HasPendingFrames());
QuicFrame frame;
size_t consumed = creator_.CreateStreamFrame(1u, "test", 0u, false, &frame);
EXPECT_EQ(4u, consumed);
ASSERT_TRUE(frame.stream_frame);
- EXPECT_TRUE(creator_.AddFrame(frame));
+ EXPECT_TRUE(creator_.AddSavedFrame(frame));
EXPECT_TRUE(creator_.HasPendingFrames());
QuicPaddingFrame padding_frame;
- EXPECT_TRUE(creator_.AddFrame(QuicFrame(&padding_frame)));
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&padding_frame)));
EXPECT_TRUE(creator_.HasPendingFrames());
EXPECT_EQ(0u, creator_.BytesFree());
- EXPECT_FALSE(creator_.AddFrame(QuicFrame(&ack_frame)));
+ EXPECT_FALSE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
// Ensure the packet is successfully created.
- QuicFrames retransmittable_frames;
- PacketPair pair = creator_.SerializePacket(&retransmittable_frames);
- ASSERT_TRUE(pair.second);
- delete pair.second;
- ASSERT_EQ(1u, retransmittable_frames.size());
- EXPECT_EQ(STREAM_FRAME, retransmittable_frames[0].type);
+ SerializedPacket serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ delete serialized.packet;
+ ASSERT_TRUE(serialized.retransmittable_frames);
+ RetransmittableFrames* retransmittable = serialized.retransmittable_frames;
+ ASSERT_EQ(1u, retransmittable->frames().size());
+ EXPECT_EQ(STREAM_FRAME, retransmittable->frames()[0].type);
+ ASSERT_TRUE(retransmittable->frames()[0].stream_frame);
+ delete serialized.retransmittable_frames;
EXPECT_FALSE(creator_.HasPendingFrames());
EXPECT_EQ(max_plaintext_size - kPacketHeaderSize, creator_.BytesFree());
-
- delete frame.stream_frame;
}
} // namespace
diff --git a/net/quic/quic_packet_entropy_manager.cc b/net/quic/quic_packet_entropy_manager.cc
new file mode 100644
index 0000000..35fb198
--- /dev/null
+++ b/net/quic/quic_packet_entropy_manager.cc
@@ -0,0 +1,145 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_packet_entropy_manager.h"
+
+#include "base/logging.h"
+#include "net/base/linked_hash_map.h"
+
+using std::make_pair;
+using std::max;
+
+namespace net {
+
+QuicPacketEntropyManager::QuicPacketEntropyManager()
+ : sent_packets_entropy_hash_(0),
+ received_packets_entropy_hash_(0),
+ largest_received_sequence_number_(0) {}
+
+QuicPacketEntropyManager::~QuicPacketEntropyManager() {}
+
+void QuicPacketEntropyManager::RecordReceivedPacketEntropyHash(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ largest_received_sequence_number_ =
+ max<QuicPacketSequenceNumber>(largest_received_sequence_number_,
+ sequence_number);
+ received_packets_entropy_.insert(make_pair(sequence_number, entropy_hash));
+ received_packets_entropy_hash_ ^= entropy_hash;
+ DVLOG(2) << "setting cumulative received entropy hash to: "
+ << static_cast<int>(received_packets_entropy_hash_)
+ << " updated with sequence number " << sequence_number
+ << " entropy hash: " << static_cast<int>(entropy_hash);
+}
+
+void QuicPacketEntropyManager::RecordSentPacketEntropyHash(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ // TODO(satyamshekhar): Check this logic again when/if we enable packet
+ // reordering.
+ sent_packets_entropy_hash_ ^= entropy_hash;
+ sent_packets_entropy_.insert(
+ make_pair(sequence_number,
+ make_pair(entropy_hash, sent_packets_entropy_hash_)));
+ DVLOG(2) << "setting cumulative sent entropy hash to: "
+ << static_cast<int>(sent_packets_entropy_hash_)
+ << " updated with sequence number " << sequence_number
+ << " entropy hash: " << static_cast<int>(entropy_hash);
+}
+
+QuicPacketEntropyHash QuicPacketEntropyManager::ReceivedEntropyHash(
+ QuicPacketSequenceNumber sequence_number) const {
+ if (sequence_number == largest_received_sequence_number_) {
+ return received_packets_entropy_hash_;
+ }
+
+ ReceivedEntropyMap::const_iterator it =
+ received_packets_entropy_.upper_bound(sequence_number);
+ // When this map is empty we should only query entropy for
+ // |largest_received_sequence_number_|.
+ LOG_IF(WARNING, it != received_packets_entropy_.end())
+ << "largest_received: " << largest_received_sequence_number_
+ << " sequence_number: " << sequence_number;
+
+ // TODO(satyamshekhar): Make this O(1).
+ QuicPacketEntropyHash hash = received_packets_entropy_hash_;
+ for (; it != received_packets_entropy_.end(); ++it) {
+ hash ^= it->second;
+ }
+ return hash;
+}
+
+QuicPacketEntropyHash QuicPacketEntropyManager::SentEntropyHash(
+ QuicPacketSequenceNumber sequence_number) const {
+ SentEntropyMap::const_iterator it =
+ sent_packets_entropy_.find(sequence_number);
+ if (it == sent_packets_entropy_.end()) {
+ // Should only happen when we have not received ack for any packet.
+ DCHECK_EQ(0u, sequence_number);
+ return 0;
+ }
+ return it->second.second;
+}
+
+void QuicPacketEntropyManager::RecalculateReceivedEntropyHash(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ received_packets_entropy_hash_ = entropy_hash;
+ ReceivedEntropyMap::iterator it =
+ received_packets_entropy_.lower_bound(sequence_number);
+ // TODO(satyamshekhar): Make this O(1).
+ for (; it != received_packets_entropy_.end(); ++it) {
+ received_packets_entropy_hash_ ^= it->second;
+ }
+}
+
+bool QuicPacketEntropyManager::IsValidEntropy(
+ QuicPacketSequenceNumber sequence_number,
+ const SequenceNumberSet& missing_packets,
+ QuicPacketEntropyHash entropy_hash) const {
+ SentEntropyMap::const_iterator entropy_it =
+ sent_packets_entropy_.find(sequence_number);
+ if (entropy_it == sent_packets_entropy_.end()) {
+ DCHECK_EQ(0u, sequence_number);
+ // Close connection if something goes wrong.
+ return 0 == sequence_number;
+ }
+ QuicPacketEntropyHash expected_entropy_hash = entropy_it->second.second;
+ for (SequenceNumberSet::const_iterator it = missing_packets.begin();
+ it != missing_packets.end(); ++it) {
+ entropy_it = sent_packets_entropy_.find(*it);
+ DCHECK(entropy_it != sent_packets_entropy_.end());
+ expected_entropy_hash ^= entropy_it->second.first;
+ }
+ DLOG_IF(WARNING, entropy_hash != expected_entropy_hash)
+ << "Invalid entropy hash: " << static_cast<int>(entropy_hash)
+ << " expected entropy hash: " << static_cast<int>(expected_entropy_hash);
+ return entropy_hash == expected_entropy_hash;
+}
+
+void QuicPacketEntropyManager::ClearSentEntropyBefore(
+ QuicPacketSequenceNumber sequence_number) {
+ if (sent_packets_entropy_.empty()) {
+ return;
+ }
+ SentEntropyMap::iterator it = sent_packets_entropy_.begin();
+ while (it->first < sequence_number) {
+ sent_packets_entropy_.erase(it);
+ it = sent_packets_entropy_.begin();
+ DCHECK(it != sent_packets_entropy_.end());
+ }
+ DVLOG(2) << "Cleared entropy before: "
+ << sent_packets_entropy_.begin()->first;
+}
+
+void QuicPacketEntropyManager::ClearReceivedEntropyBefore(
+ QuicPacketSequenceNumber sequence_number) {
+ // This might clear the received_packets_entropy_ map if the current packet
+ // is peer_least_packet_awaiting_ack_ and it doesn't have entropy flag set.
+ received_packets_entropy_.erase(
+ received_packets_entropy_.begin(),
+ received_packets_entropy_.lower_bound(sequence_number));
+}
+
+} // namespace net
diff --git a/net/quic/quic_packet_entropy_manager.h b/net/quic/quic_packet_entropy_manager.h
new file mode 100644
index 0000000..5a6c671
--- /dev/null
+++ b/net/quic/quic_packet_entropy_manager.h
@@ -0,0 +1,102 @@
+// 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.
+//
+// Manages the packet entropy calculation for both sent and received packets
+// for a connection.
+
+#ifndef NET_QUIC_QUIC_PACKET_ENTROPY_MANAGER_H_
+#define NET_QUIC_QUIC_PACKET_ENTROPY_MANAGER_H_
+
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+// Records all sent and received packets by a connection to track the cumulative
+// entropy of both sent and received packets separately. It is used by the
+// connection to validate an ack frame sent by the peer as a preventive measure
+// against the optimistic ack attack. Also, called by the framer when it
+// truncates an ack frame to get the correct entropy value for the ack frame
+// being serialized.
+class NET_EXPORT_PRIVATE QuicPacketEntropyManager :
+ public QuicReceivedEntropyHashCalculatorInterface {
+ public:
+ QuicPacketEntropyManager();
+ virtual ~QuicPacketEntropyManager();
+
+ // Record the received entropy hash against |sequence_number|.
+ void RecordReceivedPacketEntropyHash(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash);
+
+ // Record |entropy_hash| for sent packet corresponding to |sequence_number|.
+ void RecordSentPacketEntropyHash(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash);
+
+ // QuicReceivedEntropyHashCalculatorInterface
+ // Called by QuicFramer, when the outgoing ack gets truncated, to recalculate
+ // the received entropy hash for the truncated ack frame.
+ virtual QuicPacketEntropyHash ReceivedEntropyHash(
+ QuicPacketSequenceNumber sequence_number) const OVERRIDE;
+
+ QuicPacketEntropyHash SentEntropyHash(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Recalculate the received entropy hash since we had some missing packets
+ // which the sender won't retransmit again and has sent us the |entropy_hash|
+ // for packets up to, but not including, |sequence_number|.
+ void RecalculateReceivedEntropyHash(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash);
+
+ // Returns true if |entropy_hash| matches the expected sent entropy hash
+ // up to |sequence_number| removing sequence numbers from |missing_packets|.
+ bool IsValidEntropy(QuicPacketSequenceNumber sequence_number,
+ const SequenceNumberSet& missing_packets,
+ QuicPacketEntropyHash entropy_hash) const;
+
+ // Removes not required entries from |sent_packets_entropy_| before
+ // |sequence_number|.
+ void ClearSentEntropyBefore(QuicPacketSequenceNumber sequence_number);
+
+ // Removes not required entries from |received_packets_entropy_| before
+ // |sequence_number|.
+ void ClearReceivedEntropyBefore(QuicPacketSequenceNumber sequence_number);
+
+ QuicPacketEntropyHash sent_packets_entropy_hash() const {
+ return sent_packets_entropy_hash_;
+ }
+
+ QuicPacketEntropyHash received_packets_entropy_hash() const {
+ return received_packets_entropy_hash_;
+ }
+
+ private:
+ typedef linked_hash_map<QuicPacketSequenceNumber,
+ std::pair<QuicPacketEntropyHash,
+ QuicPacketEntropyHash> > SentEntropyMap;
+ typedef std::map<QuicPacketSequenceNumber,
+ QuicPacketEntropyHash> ReceivedEntropyMap;
+
+ // TODO(satyamshekhar): Can be optimized using an interval set like data
+ // structure.
+ // Set of received sequence numbers that had the received entropy flag set.
+ ReceivedEntropyMap received_packets_entropy_;
+
+ // Linked hash map from sequence numbers to the sent entropy hash up to the
+ // sequence number in the key.
+ SentEntropyMap sent_packets_entropy_;
+
+ // Cumulative hash of entropy of all sent packets.
+ QuicPacketEntropyHash sent_packets_entropy_hash_;
+
+ // Cumulative hash of entropy of all received packets.
+ QuicPacketEntropyHash received_packets_entropy_hash_;
+
+ QuicPacketSequenceNumber largest_received_sequence_number_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_PACKET_ENTROPY_MANAGER_H_
diff --git a/net/quic/quic_packet_entropy_manager_test.cc b/net/quic/quic_packet_entropy_manager_test.cc
new file mode 100644
index 0000000..17f4f1e3
--- /dev/null
+++ b/net/quic/quic_packet_entropy_manager_test.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_packet_entropy_manager.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::make_pair;
+using std::pair;
+using std::vector;
+
+namespace net {
+namespace test {
+namespace {
+
+class QuicPacketEntropyManagerTest : public ::testing::Test {
+ protected:
+ QuicPacketEntropyManager entropy_manager_;
+};
+
+TEST_F(QuicPacketEntropyManagerTest, ReceivedPacketEntropyHash) {
+ vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies;
+ entropies.push_back(make_pair(1, 12));
+ entropies.push_back(make_pair(7, 1));
+ entropies.push_back(make_pair(2, 33));
+ entropies.push_back(make_pair(5, 3));
+ entropies.push_back(make_pair(8, 34));
+
+ for (size_t i = 0; i < entropies.size(); ++i) {
+ entropy_manager_.RecordReceivedPacketEntropyHash(entropies[i].first,
+ entropies[i].second);
+ }
+
+ sort(entropies.begin(), entropies.end());
+
+ QuicPacketEntropyHash hash = 0;
+ size_t index = 0;
+ for (size_t i = 1; i <= (*entropies.rbegin()).first; ++i) {
+ if (entropies[index].first == i) {
+ hash ^= entropies[index].second;
+ ++index;
+ }
+ EXPECT_EQ(hash, entropy_manager_.ReceivedEntropyHash(i));
+ }
+};
+
+TEST_F(QuicPacketEntropyManagerTest, EntropyHashBelowLeastObserved) {
+ EXPECT_EQ(0, entropy_manager_.ReceivedEntropyHash(0));
+ EXPECT_EQ(0, entropy_manager_.ReceivedEntropyHash(9));
+ entropy_manager_.RecordReceivedPacketEntropyHash(4, 5);
+ EXPECT_EQ(0, entropy_manager_.ReceivedEntropyHash(3));
+};
+
+TEST_F(QuicPacketEntropyManagerTest, EntropyHashAboveLargesObserved) {
+ EXPECT_EQ(0, entropy_manager_.ReceivedEntropyHash(0));
+ EXPECT_EQ(0, entropy_manager_.ReceivedEntropyHash(9));
+ entropy_manager_.RecordReceivedPacketEntropyHash(4, 5);
+ EXPECT_EQ(0, entropy_manager_.ReceivedEntropyHash(3));
+};
+
+TEST_F(QuicPacketEntropyManagerTest, RecalculateReceivedEntropyHash) {
+ vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies;
+ entropies.push_back(make_pair(1, 12));
+ entropies.push_back(make_pair(2, 1));
+ entropies.push_back(make_pair(3, 33));
+ entropies.push_back(make_pair(4, 3));
+ entropies.push_back(make_pair(5, 34));
+ entropies.push_back(make_pair(6, 29));
+
+ QuicPacketEntropyHash entropy_hash = 0;
+ for (size_t i = 0; i < entropies.size(); ++i) {
+ entropy_manager_.RecordReceivedPacketEntropyHash(entropies[i].first,
+ entropies[i].second);
+ entropy_hash ^= entropies[i].second;
+ }
+ EXPECT_EQ(entropy_hash, entropy_manager_.ReceivedEntropyHash(6));
+
+ // Now set the entropy hash up to 4 to be 100.
+ entropy_hash ^= 100;
+ for (size_t i = 0; i < 3; ++i) {
+ entropy_hash ^= entropies[i].second;
+ }
+ entropy_manager_.RecalculateReceivedEntropyHash(4, 100);
+ EXPECT_EQ(entropy_hash, entropy_manager_.ReceivedEntropyHash(6));
+}
+
+TEST_F(QuicPacketEntropyManagerTest, SentEntropyHash) {
+ EXPECT_EQ(0, entropy_manager_.SentEntropyHash(0));
+
+ vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies;
+ entropies.push_back(make_pair(1, 12));
+ entropies.push_back(make_pair(2, 1));
+ entropies.push_back(make_pair(3, 33));
+ entropies.push_back(make_pair(4, 3));
+
+ for (size_t i = 0; i < entropies.size(); ++i) {
+ entropy_manager_.RecordSentPacketEntropyHash(entropies[i].first,
+ entropies[i].second);
+ }
+
+ QuicPacketEntropyHash hash = 0;
+ for (size_t i = 0; i < entropies.size(); ++i) {
+ hash ^= entropies[i].second;
+ EXPECT_EQ(hash, entropy_manager_.SentEntropyHash(i + 1));
+ }
+}
+
+TEST_F(QuicPacketEntropyManagerTest, IsValidEntropy) {
+ QuicPacketEntropyHash entropies[10] =
+ {12, 1, 33, 3, 32, 100, 28, 42, 22, 255};
+ for (size_t i = 0; i < 10; ++i) {
+ entropy_manager_.RecordSentPacketEntropyHash(i + 1, entropies[i]);
+ }
+
+ SequenceNumberSet missing_packets;
+ missing_packets.insert(1);
+ missing_packets.insert(4);
+ missing_packets.insert(7);
+ missing_packets.insert(8);
+
+ QuicPacketEntropyHash entropy_hash = 0;
+ for (size_t i = 0; i < 10; ++i) {
+ if (missing_packets.find(i + 1) == missing_packets.end()) {
+ entropy_hash ^= entropies[i];
+ }
+ }
+
+ EXPECT_TRUE(entropy_manager_.IsValidEntropy(10, missing_packets,
+ entropy_hash));
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc
new file mode 100644
index 0000000..fb4bcf2
--- /dev/null
+++ b/net/quic/quic_packet_generator.cc
@@ -0,0 +1,203 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_packet_generator.h"
+
+#include "base/logging.h"
+#include "net/quic/quic_fec_group.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+
+namespace net {
+
+QuicPacketGenerator::QuicPacketGenerator(DelegateInterface* delegate,
+ QuicPacketCreator* creator)
+ : delegate_(delegate),
+ packet_creator_(creator),
+ should_flush_(true),
+ should_send_ack_(false),
+ should_send_feedback_(false) {
+}
+
+QuicPacketGenerator::~QuicPacketGenerator() {
+ for (QuicFrames::iterator it = queued_control_frames_.begin();
+ it != queued_control_frames_.end(); ++it) {
+ switch (it->type) {
+ case PADDING_FRAME:
+ delete it->padding_frame;
+ break;
+ case STREAM_FRAME:
+ delete it->stream_frame;
+ break;
+ case ACK_FRAME:
+ delete it->ack_frame;
+ break;
+ case CONGESTION_FEEDBACK_FRAME:
+ delete it->congestion_feedback_frame;
+ break;
+ case RST_STREAM_FRAME:
+ delete it->rst_stream_frame;
+ break;
+ case CONNECTION_CLOSE_FRAME:
+ delete it->connection_close_frame;
+ break;
+ case GOAWAY_FRAME:
+ delete it->goaway_frame;
+ break;
+ case NUM_FRAME_TYPES:
+ DCHECK(false) << "Cannot delete type: " << it->type;
+ }
+ }
+}
+
+void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback) {
+ should_send_ack_ = true;
+ should_send_feedback_ = also_send_feedback;
+ SendQueuedData();
+}
+
+
+void QuicPacketGenerator::AddControlFrame(const QuicFrame& frame) {
+ queued_control_frames_.push_back(frame);
+ SendQueuedData();
+}
+
+QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin) {
+ SendQueuedData();
+
+ size_t total_bytes_consumed = 0;
+ bool fin_consumed = false;
+
+ // Make sure any queued data gets sent before new data.
+ // SendQueuedData();
+
+ while (delegate_->CanWrite(false)) {
+ // TODO(rch) figure out FEC.
+ // packet_creator_.MaybeStartFEC();
+ QuicFrame frame;
+ size_t bytes_consumed = packet_creator_->CreateStreamFrame(
+ id, data, offset + total_bytes_consumed, fin, &frame);
+ bool success = packet_creator_->AddSavedFrame(frame);
+ DCHECK(success);
+
+ total_bytes_consumed += bytes_consumed;
+ fin_consumed = fin && bytes_consumed == data.size();
+ data.remove_prefix(bytes_consumed);
+ DCHECK(data.empty() || packet_creator_->BytesFree() == 0u);
+
+ // TODO(ianswett): Restore packet reordering.
+ if (should_flush_ || !packet_creator_->HasRoomForStreamFrame()) {
+ SerializeAndSendPacket();
+ }
+
+ if (data.empty()) {
+ // We're done writing the data. Exit the loop.
+ // We don't make this a precondition because we could have 0 bytes of data
+ // if we're simply writing a fin.
+ break;
+ }
+ }
+
+ // Ensure the FEC group is closed at the end of this method unless other
+ // writes are pending.
+ if (should_flush_ && packet_creator_->ShouldSendFec(true)) {
+ SerializedPacket serialized_fec = packet_creator_->SerializeFec();
+ DCHECK(serialized_fec.packet);
+ delegate_->OnSerializedPacket(serialized_fec);
+ }
+
+ DCHECK(!should_flush_ || !packet_creator_->HasPendingFrames());
+ return QuicConsumedData(total_bytes_consumed, fin_consumed);
+}
+
+void QuicPacketGenerator::SendQueuedData() {
+ while (HasPendingData() && delegate_->CanWrite(false)) {
+ if (!AddNextPendingFrame()) {
+ // Packet was full, so serialize and send it.
+ SerializeAndSendPacket();
+ }
+ }
+
+ if (should_flush_) {
+ if (packet_creator_->HasPendingFrames()) {
+ SerializeAndSendPacket();
+ }
+
+ // Ensure the FEC group is closed at the end of this method unless other
+ // writes are pending.
+ if (packet_creator_->ShouldSendFec(true)) {
+ SerializedPacket serialized_fec = packet_creator_->SerializeFec();
+ DCHECK(serialized_fec.packet);
+ delegate_->OnSerializedPacket(serialized_fec);
+ }
+ }
+}
+
+void QuicPacketGenerator::StartBatchOperations() {
+ should_flush_ = false;
+}
+
+void QuicPacketGenerator::FinishBatchOperations() {
+ should_flush_ = true;
+ SendQueuedData();
+}
+
+bool QuicPacketGenerator::HasQueuedData() const {
+ return packet_creator_->HasPendingFrames() || HasPendingData();
+}
+
+bool QuicPacketGenerator::HasPendingData() const {
+ return should_send_ack_ || should_send_feedback_ ||
+ !queued_control_frames_.empty();
+}
+
+bool QuicPacketGenerator::AddNextPendingFrame() {
+ if (should_send_ack_) {
+ pending_ack_frame_.reset(delegate_->CreateAckFrame());
+ if (!packet_creator_->AddSavedFrame(QuicFrame(pending_ack_frame_.get()))) {
+ // packet was full
+ return false;
+ }
+ should_send_ack_ = false;
+ return true;
+ }
+
+ if (should_send_feedback_) {
+ pending_feedback_frame_.reset(delegate_->CreateFeedbackFrame());
+ if (!packet_creator_->AddSavedFrame(QuicFrame(
+ pending_feedback_frame_.get()))) {
+ // packet was full
+ return false;
+ }
+ should_send_feedback_ = false;
+ return true;
+ }
+
+ DCHECK(!queued_control_frames_.empty());
+ if (!packet_creator_->AddSavedFrame(queued_control_frames_.back())) {
+ // packet was full
+ return false;
+ }
+ queued_control_frames_.pop_back();
+ return true;
+}
+
+void QuicPacketGenerator::SerializeAndSendPacket() {
+ packet_creator_->MaybeStartFEC();
+ SerializedPacket serialized_packet = packet_creator_->SerializePacket();
+ DCHECK(serialized_packet.packet);
+ delegate_->OnSerializedPacket(serialized_packet);
+
+ if (packet_creator_->ShouldSendFec(false)) {
+ SerializedPacket serialized_fec = packet_creator_->SerializeFec();
+ DCHECK(serialized_fec.packet);
+ delegate_->OnSerializedPacket(serialized_fec);
+ }
+}
+
+} // namespace net
diff --git a/net/quic/quic_packet_generator.h b/net/quic/quic_packet_generator.h
new file mode 100644
index 0000000..32becc1
--- /dev/null
+++ b/net/quic/quic_packet_generator.h
@@ -0,0 +1,113 @@
+// 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.
+//
+// Responsible for generating packets on behalf of a QuicConnection.
+// Packets are serialized just-in-time. Control frame are queued.
+// Ack and Feedback frames will requested from the Connection
+// just-in-time. When a packet needs to be sent, the Generator
+// wills serialized a packet pass it to QuicConnection::SendOrQueuePacket()
+//
+// The Generator's mode of operation is controlled by two conditions:
+//
+// 1) Is the Delegate writable?
+//
+// 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 send.
+// * AddControlFram will enqueue the control frame.
+// * ConsumeData will do nothing.
+//
+// If the Delegate is writable, then the behavior depends on the second
+// condition:
+//
+// 2) Is the Generator in batch mode?
+//
+// If the Generator is NOT in batch mode, then each call to a write
+// operation will serialize one or more packets. The contents will
+// include any previous queued frames. If an ack should be sent
+// but has not been sent, then the Delegate will be asked to create
+// an Ack frame which will then be included in the packet. When
+// the write call completes, the current packet will be serialized
+// and sent to the Delegate, even if it is not full.
+//
+// If the Generator is in batch mode, then each write operation will
+// add data to the "current" packet. When the current packet becomes
+// full, it will be serialized and sent to the packet. When batch
+// mode is ended via |FinishBatchOperations|, the current packet
+// will be serialzied, even if it is not full.
+//
+// FEC behavior also depends on batch mode. In batch mode, FEC packets
+// will be sent after |max_packets_per_group| have been sent, as well
+// as after batch operations are complete. When not in batch mode,
+// an FEC packet will be sent after each write call completes.
+//
+// TODO(rch): This behavior should probably be tuned. When not in batch
+// mode we, should probably set a timer so that several independent
+// operations can be grouped into the same FEC group.
+//
+// When an FEC packet is generate, it will be send to the Delegate,
+// even if the Delegate has become unwritable after handling the
+// data packet immediately proceeding the FEC packet.
+
+#ifndef NET_QUIC_QUIC_PACKET_GENERATOR_H_
+#define NET_QUIC_QUIC_PACKET_GENERATOR_H_
+
+#include "net/quic/quic_packet_creator.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE QuicPacketGenerator {
+ public:
+ class NET_EXPORT_PRIVATE DelegateInterface {
+ public:
+ virtual ~DelegateInterface() {}
+ virtual bool CanWrite(bool is_retransmission) = 0;
+ virtual QuicAckFrame* CreateAckFrame() = 0;
+ virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() = 0;
+ // Takes ownership of |packet.packet| and |packet.retransmittable_frames|.
+ virtual bool OnSerializedPacket(const SerializedPacket& packet) = 0;
+ };
+
+ QuicPacketGenerator(DelegateInterface* delegate,
+ QuicPacketCreator* creator);
+
+ virtual ~QuicPacketGenerator();
+
+ void SetShouldSendAck(bool also_send_feedback);
+ void AddControlFrame(const QuicFrame& frame);
+ QuicConsumedData ConsumeData(QuicStreamId id,
+ base::StringPiece data,
+ QuicStreamOffset offset,
+ bool fin);
+
+ // Disables flushing.
+ void StartBatchOperations();
+ // Enables flushing and flushes queued data.
+ void FinishBatchOperations();
+
+ bool HasQueuedData() const;
+
+ private:
+ // Must be called when the connection is writable
+ // and not blocked by the congestion manager.
+ void SendQueuedData();
+
+ bool HasPendingData() const;
+ bool AddNextPendingFrame();
+ void SerializeAndSendPacket();
+
+ DelegateInterface* delegate_;
+
+ QuicPacketCreator* packet_creator_;
+ QuicFrames queued_control_frames_;
+ bool should_flush_;
+ bool should_send_ack_;
+ scoped_ptr<QuicAckFrame> pending_ack_frame_;
+ scoped_ptr<QuicCongestionFeedbackFrame> pending_feedback_frame_;
+ bool should_send_feedback_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_PACKET_GENERATOR_H_
diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc
new file mode 100644
index 0000000..85a16e8
--- /dev/null
+++ b/net/quic/quic_packet_generator_test.cc
@@ -0,0 +1,508 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_packet_generator.h"
+
+#include <string>
+
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/null_encrypter.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/simple_quic_framer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using testing::InSequence;
+using testing::Return;
+using testing::SaveArg;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+class MockDelegate : public QuicPacketGenerator::DelegateInterface {
+ public:
+ MockDelegate() {}
+ virtual ~MockDelegate() {}
+
+ MOCK_METHOD1(CanWrite, bool(bool is_retransmission));
+
+ MOCK_METHOD0(CreateAckFrame, QuicAckFrame*());
+ MOCK_METHOD0(CreateFeedbackFrame, QuicCongestionFeedbackFrame*());
+ MOCK_METHOD1(OnSerializedPacket, bool(const SerializedPacket& packet));
+
+ void SetCanWrite(bool can_write) {
+ EXPECT_CALL(*this, CanWrite(false)).WillRepeatedly(Return(can_write));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockDelegate);
+};
+
+// Simple struct for describing the contents of a packet.
+// Useful in conjunction with a SimpleQuicFrame for validating
+// that a packet contains the expected frames.
+struct PacketContents {
+ PacketContents()
+ : num_ack_frames(0),
+ num_connection_close_frames(0),
+ num_feedback_frames(0),
+ num_goaway_frames(0),
+ num_rst_stream_frames(0),
+ num_stream_frames(0),
+ fec_group(0) {
+ }
+
+ size_t num_ack_frames;
+ size_t num_connection_close_frames;
+ size_t num_feedback_frames;
+ size_t num_goaway_frames;
+ size_t num_rst_stream_frames;
+ size_t num_stream_frames;
+
+ QuicFecGroupNumber fec_group;
+};
+
+} // namespace
+
+class QuicPacketGeneratorTest : public ::testing::Test {
+ protected:
+ QuicPacketGeneratorTest()
+ : framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)),
+ creator_(42, &framer_, &random_),
+ generator_(&delegate_, &creator_),
+ packet_(0, NULL, 0, NULL),
+ packet2_(0, NULL, 0, NULL),
+ packet3_(0, NULL, 0, NULL),
+ packet4_(0, NULL, 0, NULL),
+ packet5_(0, NULL, 0, NULL) {
+ }
+
+ ~QuicPacketGeneratorTest() {
+ delete packet_.packet;
+ delete packet_.retransmittable_frames;
+ delete packet2_.packet;
+ delete packet2_.retransmittable_frames;
+ delete packet3_.packet;
+ delete packet3_.retransmittable_frames;
+ delete packet4_.packet;
+ delete packet4_.retransmittable_frames;
+ delete packet5_.packet;
+ delete packet5_.retransmittable_frames;
+ }
+
+ QuicAckFrame* CreateAckFrame() {
+ // TODO(rch): Initialize this so it can be verified later.
+ return new QuicAckFrame(0, 0);
+ }
+
+ QuicCongestionFeedbackFrame* CreateFeedbackFrame() {
+ QuicCongestionFeedbackFrame* frame = new QuicCongestionFeedbackFrame;
+ frame->type = kFixRate;
+ frame->fix_rate.bitrate = QuicBandwidth::FromBytesPerSecond(42);
+ return frame;
+ }
+
+ QuicRstStreamFrame* CreateRstStreamFrame() {
+ return new QuicRstStreamFrame(1, QUIC_NO_ERROR);
+ }
+
+ QuicGoAwayFrame* CreateGoAwayFrame() {
+ return new QuicGoAwayFrame(QUIC_NO_ERROR, 1, "");
+ }
+
+ void CheckPacketContains(const PacketContents& contents,
+ const SerializedPacket& packet) {
+ size_t num_retransmittable_frames = contents.num_connection_close_frames +
+ contents.num_goaway_frames + contents.num_rst_stream_frames +
+ contents.num_stream_frames;
+ size_t num_frames = contents.num_feedback_frames + contents.num_ack_frames +
+ num_retransmittable_frames;
+
+ if (num_retransmittable_frames == 0) {
+ ASSERT_TRUE(packet.retransmittable_frames == NULL);
+ } else {
+ ASSERT_TRUE(packet.retransmittable_frames != NULL);
+ EXPECT_EQ(num_retransmittable_frames,
+ packet.retransmittable_frames->frames().size());
+ }
+
+ ASSERT_TRUE(packet.packet != NULL);
+ ASSERT_TRUE(simple_framer_.ProcessPacket(*packet.packet));
+ EXPECT_EQ(num_frames, simple_framer_.num_frames());
+ EXPECT_EQ(contents.num_ack_frames, simple_framer_.ack_frames().size());
+ EXPECT_EQ(contents.num_connection_close_frames,
+ simple_framer_.connection_close_frames().size());
+ EXPECT_EQ(contents.num_feedback_frames,
+ simple_framer_.feedback_frames().size());
+ EXPECT_EQ(contents.num_goaway_frames,
+ simple_framer_.goaway_frames().size());
+ EXPECT_EQ(contents.num_rst_stream_frames,
+ simple_framer_.rst_stream_frames().size());
+ EXPECT_EQ(contents.num_stream_frames,
+ simple_framer_.stream_frames().size());
+ EXPECT_EQ(contents.fec_group, simple_framer_.header().fec_group);
+ }
+
+ void CheckPacketHasSingleStreamFrame(const SerializedPacket& packet) {
+ ASSERT_TRUE(packet.retransmittable_frames != NULL);
+ EXPECT_EQ(1u, packet.retransmittable_frames->frames().size());
+ ASSERT_TRUE(packet.packet != NULL);
+ ASSERT_TRUE(simple_framer_.ProcessPacket(*packet.packet));
+ EXPECT_EQ(1u, simple_framer_.num_frames());
+ EXPECT_EQ(1u, simple_framer_.stream_frames().size());
+ }
+
+ void CheckPacketIsFec(const SerializedPacket& packet,
+ QuicPacketSequenceNumber fec_group) {
+ ASSERT_TRUE(packet.retransmittable_frames == NULL);
+ ASSERT_TRUE(packet.packet != NULL);
+ ASSERT_TRUE(simple_framer_.ProcessPacket(*packet.packet));
+ EXPECT_TRUE(simple_framer_.header().fec_flag);
+ EXPECT_EQ(fec_group, simple_framer_.fec_data().fec_group);
+ }
+
+ StringPiece CreateData(size_t len) {
+ data_array_.reset(new char[len]);
+ memset(data_array_.get(), '?', len);
+ return StringPiece(data_array_.get(), len);
+ }
+
+ QuicFramer framer_;
+ MockRandom random_;
+ QuicPacketCreator creator_;
+ testing::StrictMock<MockDelegate> delegate_;
+ QuicPacketGenerator generator_;
+ SimpleQuicFramer simple_framer_;
+ SerializedPacket packet_;
+ SerializedPacket packet2_;
+ SerializedPacket packet3_;
+ SerializedPacket packet4_;
+ SerializedPacket packet5_;
+
+ private:
+ scoped_ptr<char[]> data_array_;
+};
+
+TEST_F(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) {
+ delegate_.SetCanWrite(false);
+
+ generator_.SetShouldSendAck(false);
+ EXPECT_TRUE(generator_.HasQueuedData());
+}
+
+TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) {
+ delegate_.SetCanWrite(true);
+ generator_.StartBatchOperations();
+
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+
+ generator_.SetShouldSendAck(false);
+ EXPECT_TRUE(generator_.HasQueuedData());
+}
+
+TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) {
+ delegate_.SetCanWrite(true);
+
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+
+ generator_.SetShouldSendAck(false);
+ EXPECT_FALSE(generator_.HasQueuedData());
+
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest,
+ ShouldSendAckWithFeedback_WritableAndShouldNotFlush) {
+ delegate_.SetCanWrite(true);
+ generator_.StartBatchOperations();
+
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
+ Return(CreateFeedbackFrame()));
+
+ generator_.SetShouldSendAck(true);
+ EXPECT_TRUE(generator_.HasQueuedData());
+}
+
+TEST_F(QuicPacketGeneratorTest,
+ ShouldSendAckWithFeedback_WritableAndShouldFlush) {
+ delegate_.SetCanWrite(true);
+
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
+ Return(CreateFeedbackFrame()));
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+
+ generator_.SetShouldSendAck(true);
+ EXPECT_FALSE(generator_.HasQueuedData());
+
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ contents.num_feedback_frames = 1;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritable) {
+ delegate_.SetCanWrite(false);
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_TRUE(generator_.HasQueuedData());
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldNotFlush) {
+ delegate_.SetCanWrite(true);
+ generator_.StartBatchOperations();
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_TRUE(generator_.HasQueuedData());
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) {
+ delegate_.SetCanWrite(true);
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_FALSE(generator_.HasQueuedData());
+
+ PacketContents contents;
+ contents.num_rst_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) {
+ delegate_.SetCanWrite(false);
+
+ QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true);
+ EXPECT_EQ(0u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedData());
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) {
+ delegate_.SetCanWrite(true);
+ generator_.StartBatchOperations();
+
+ QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedData());
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) {
+ delegate_.SetCanWrite(true);
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedData());
+
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest,
+ ConsumeDataMultipleTimes_WritableAndShouldNotFlush) {
+ delegate_.SetCanWrite(true);
+ generator_.StartBatchOperations();
+
+ generator_.ConsumeData(1, "foo", 2, true);
+ QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false);
+ EXPECT_EQ(4u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedData());
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) {
+ delegate_.SetCanWrite(true);
+ generator_.StartBatchOperations();
+
+ generator_.ConsumeData(1, "foo", 2, true);
+ QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false);
+ EXPECT_EQ(4u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedData());
+
+ // Now both frames will be flushed out.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ generator_.FinishBatchOperations();
+ EXPECT_FALSE(generator_.HasQueuedData());
+
+ PacketContents contents;
+ contents.num_stream_frames = 2;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) {
+ delegate_.SetCanWrite(true);
+
+ // Send FEC every two packets.
+ creator_.options()->max_packets_per_fec_group = 2;
+
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet2_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet3_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet4_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet5_), Return(true)));
+ }
+
+ // 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);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedData());
+
+ CheckPacketHasSingleStreamFrame(packet_);
+ CheckPacketHasSingleStreamFrame(packet2_);
+ CheckPacketIsFec(packet3_, 1);
+
+ CheckPacketHasSingleStreamFrame(packet4_);
+ CheckPacketIsFec(packet5_, 4);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) {
+ delegate_.SetCanWrite(true);
+
+ // Send FEC every six packets.
+ creator_.options()->max_packets_per_fec_group = 6;
+
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet2_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet3_), Return(true)));
+ }
+
+ // 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);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedData());
+
+ CheckPacketHasSingleStreamFrame(packet_);
+ CheckPacketHasSingleStreamFrame(packet2_);
+ CheckPacketIsFec(packet3_, 1);
+}
+
+TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
+ delegate_.SetCanWrite(false);
+
+ generator_.SetShouldSendAck(true);
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_TRUE(generator_.HasQueuedData());
+
+ delegate_.SetCanWrite(true);
+
+ generator_.StartBatchOperations();
+
+ // When the first write operation is invoked, the ack and feedback
+ // frames will be returned.
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
+ Return(CreateFeedbackFrame()));
+
+ // Send some data and a control frame
+ generator_.ConsumeData(3, "quux", 7, false);
+ generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
+
+ // All five frames will be flushed out in a single packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ generator_.FinishBatchOperations();
+ EXPECT_FALSE(generator_.HasQueuedData());
+
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ contents.num_goaway_frames = 1;
+ contents.num_feedback_frames = 1;
+ contents.num_rst_stream_frames = 1;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) {
+ delegate_.SetCanWrite(false);
+
+ generator_.SetShouldSendAck(true);
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_TRUE(generator_.HasQueuedData());
+
+ delegate_.SetCanWrite(true);
+
+ generator_.StartBatchOperations();
+
+ // When the first write operation is invoked, the ack and feedback
+ // frames will be returned.
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
+ Return(CreateFeedbackFrame()));
+
+ {
+ InSequence dummy;
+ // All five frames will be flushed out in a single packet
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet2_), Return(true)));
+ }
+
+ // Send enough data to exceed one packet
+ size_t data_len = kMaxPacketSize + 100;
+ QuicConsumedData consumed =
+ generator_.ConsumeData(3, CreateData(data_len), 0, true);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
+
+ generator_.FinishBatchOperations();
+ EXPECT_FALSE(generator_.HasQueuedData());
+
+ // The first packet should have the queued data and part of the stream data.
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ contents.num_feedback_frames = 1;
+ contents.num_rst_stream_frames = 1;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+
+ // The second should have the remainder of the stream data.
+ PacketContents contents2;
+ contents2.num_goaway_frames = 1;
+ contents2.num_stream_frames = 1;
+ CheckPacketContains(contents2, packet2_);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc
index 14ad48b..42ee620 100644
--- a/net/quic/quic_protocol.cc
+++ b/net/quic/quic_protocol.cc
@@ -9,6 +9,7 @@ using base::StringPiece;
using std::map;
using std::numeric_limits;
using std::ostream;
+using std::string;
namespace net {
@@ -24,43 +25,37 @@ QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id,
data(data) {
}
+ostream& operator<<(ostream& os, const QuicPacketHeader& header) {
+ os << "{ guid: " << header.public_header.guid
+ << ", reset_flag: " << header.public_header.reset_flag
+ << ", version_flag: " << header.public_header.version_flag
+ << ", fec_flag: " << header.fec_flag
+ << ", entropy_flag: " << header.entropy_flag
+ << ", entropy hash: " << static_cast<int>(header.entropy_hash)
+ << ", sequence_number: " << header.packet_sequence_number
+ << ", fec_group: " << header.fec_group<< "}\n";
+ return os;
+}
+
// TODO(ianswett): Initializing largest_observed to 0 should not be necessary.
-ReceivedPacketInfo::ReceivedPacketInfo() : largest_observed(0) {}
+ReceivedPacketInfo::ReceivedPacketInfo()
+ : largest_observed(0) {
+}
ReceivedPacketInfo::~ReceivedPacketInfo() {}
-void ReceivedPacketInfo::RecordReceived(
- QuicPacketSequenceNumber sequence_number) {
- DCHECK(IsAwaitingPacket(sequence_number));
- if (largest_observed < sequence_number) {
- DCHECK_LT(sequence_number - largest_observed,
- numeric_limits<uint16>::max());
- // We've got a new high sequence number. Note any new intermediate missing
- // packets, and update the last_ack data.
- for (QuicPacketSequenceNumber i = largest_observed + 1;
- i < sequence_number; ++i) {
- DVLOG(1) << "missing " << i;
- missing_packets.insert(i);
- }
- largest_observed = sequence_number;
- } else {
- // We've gotten one of the out of order packets - remove it from our
- // "missing packets" list.
- DVLOG(1) << "Removing " << sequence_number << " from missing list";
- missing_packets.erase(sequence_number);
- }
-}
-
-bool ReceivedPacketInfo::IsAwaitingPacket(
- QuicPacketSequenceNumber sequence_number) const {
- return sequence_number > largest_observed ||
- ContainsKey(missing_packets, sequence_number);
+bool IsAwaitingPacket(const ReceivedPacketInfo& received_info,
+ QuicPacketSequenceNumber sequence_number) {
+ return sequence_number > received_info.largest_observed ||
+ ContainsKey(received_info.missing_packets, sequence_number);
}
-void ReceivedPacketInfo::ClearMissingBefore(
- QuicPacketSequenceNumber least_unacked) {
- missing_packets.erase(missing_packets.begin(),
- missing_packets.lower_bound(least_unacked));
+void InsertMissingPacketsBetween(ReceivedPacketInfo* received_info,
+ QuicPacketSequenceNumber lower,
+ QuicPacketSequenceNumber higher) {
+ for (QuicPacketSequenceNumber i = lower; i < higher; ++i) {
+ received_info->missing_packets.insert(i);
+ }
}
SentPacketInfo::SentPacketInfo() {}
@@ -70,24 +65,24 @@ SentPacketInfo::~SentPacketInfo() {}
// Testing convenience method.
QuicAckFrame::QuicAckFrame(QuicPacketSequenceNumber largest_observed,
QuicPacketSequenceNumber least_unacked) {
- for (QuicPacketSequenceNumber seq_num = 1;
- seq_num <= largest_observed; ++seq_num) {
- received_info.RecordReceived(seq_num);
- }
received_info.largest_observed = largest_observed;
+ received_info.entropy_hash = 0;
sent_info.least_unacked = least_unacked;
+ sent_info.entropy_hash = 0;
}
ostream& operator<<(ostream& os, const SentPacketInfo& sent_info) {
- os << "least_unacked: " << sent_info.least_unacked;
+ os << "entropy_hash: " << static_cast<int>(sent_info.entropy_hash);
+ os << " least_unacked: " << sent_info.least_unacked;
return os;
}
ostream& operator<<(ostream& os, const ReceivedPacketInfo& received_info) {
- os << "largest_observed: "
- << received_info.largest_observed
+ os << "entropy_hash: " << static_cast<int>(received_info.entropy_hash)
+ << " largest_observed: " << received_info.largest_observed
<< " missing_packets: [ ";
- for (SequenceSet::const_iterator it = received_info.missing_packets.begin();
+ for (SequenceNumberSet::const_iterator it =
+ received_info.missing_packets.begin();
it != received_info.missing_packets.end(); ++it) {
os << *it << " ";
}
@@ -151,6 +146,15 @@ CongestionFeedbackMessageInterArrival() {}
CongestionFeedbackMessageInterArrival::
~CongestionFeedbackMessageInterArrival() {}
+QuicGoAwayFrame::QuicGoAwayFrame(QuicErrorCode error_code,
+ QuicStreamId last_good_stream_id,
+ const string& reason)
+ : error_code(error_code),
+ last_good_stream_id(last_good_stream_id),
+ reason_phrase(reason) {
+ DCHECK_LE(error_code, numeric_limits<uint8>::max());
+}
+
QuicFecData::QuicFecData() {}
bool QuicFecData::operator==(const QuicFecData& other) const {
@@ -169,6 +173,58 @@ QuicData::~QuicData() {
}
}
+RetransmittableFrames::RetransmittableFrames() {}
+
+RetransmittableFrames::~RetransmittableFrames() {
+ for (QuicFrames::iterator it = frames_.begin(); it != frames_.end(); ++it) {
+ switch (it->type) {
+ case PADDING_FRAME:
+ delete it->padding_frame;
+ break;
+ case STREAM_FRAME:
+ delete it->stream_frame;
+ break;
+ case ACK_FRAME:
+ delete it->ack_frame;
+ break;
+ case CONGESTION_FEEDBACK_FRAME:
+ delete it->congestion_feedback_frame;
+ break;
+ case RST_STREAM_FRAME:
+ delete it->rst_stream_frame;
+ break;
+ case CONNECTION_CLOSE_FRAME:
+ delete it->connection_close_frame;
+ break;
+ case GOAWAY_FRAME:
+ delete it->goaway_frame;
+ break;
+ case NUM_FRAME_TYPES:
+ DCHECK(false) << "Cannot delete type: " << it->type;
+ }
+ }
+ STLDeleteElements(&stream_data_);
+}
+
+const QuicFrame& RetransmittableFrames::AddStreamFrame(
+ QuicStreamFrame* stream_frame) {
+ // Make an owned copy of the StringPiece.
+ string* stream_data = new string(stream_frame->data.data(),
+ stream_frame->data.size());
+ // Ensure the frame's StringPiece points to the owned copy of the data.
+ stream_frame->data = StringPiece(*stream_data);
+ stream_data_.push_back(stream_data);
+ frames_.push_back(QuicFrame(stream_frame));
+ return frames_.back();
+}
+
+const QuicFrame& RetransmittableFrames::AddNonStreamFrame(
+ const QuicFrame& frame) {
+ DCHECK_NE(frame.type, STREAM_FRAME);
+ frames_.push_back(frame);
+ return frames_.back();
+}
+
ostream& operator<<(ostream& os, const QuicEncryptedPacket& s) {
os << s.length() << "-byte data";
return os;
diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h
index 3d5f73e..689167c 100644
--- a/net/quic/quic_protocol.h
+++ b/net/quic/quic_protocol.h
@@ -35,6 +35,7 @@ typedef uint64 QuicStreamOffset;
typedef uint64 QuicPacketSequenceNumber;
typedef QuicPacketSequenceNumber QuicFecGroupNumber;
typedef uint64 QuicPublicResetNonceProof;
+typedef uint8 QuicPacketEntropyHash;
// TODO(rch): Consider Quic specific names for these constants.
// Maximum size in bytes of a QUIC packet.
@@ -105,8 +106,6 @@ const QuicStreamId kCryptoStreamId = 1;
// Value which indicates this packet is not FEC protected.
const uint8 kNoFecOffset = 0xFF;
-typedef std::pair<QuicPacketSequenceNumber, QuicPacket*> PacketPair;
-
const int64 kDefaultTimeoutUs = 600000000; // 10 minutes.
enum QuicFrameType {
@@ -116,20 +115,23 @@ enum QuicFrameType {
CONGESTION_FEEDBACK_FRAME,
RST_STREAM_FRAME,
CONNECTION_CLOSE_FRAME,
+ GOAWAY_FRAME,
NUM_FRAME_TYPES
};
enum QuicPacketPublicFlags {
PACKET_PUBLIC_FLAGS_NONE = 0,
- PACKET_PUBLIC_FLAGS_VERSION = 1,
- PACKET_PUBLIC_FLAGS_RST = 2, // Packet is a public reset packet.
- PACKET_PUBLIC_FLAGS_MAX = 3 // Both bit set.
+ PACKET_PUBLIC_FLAGS_VERSION = 1 << 0,
+ PACKET_PUBLIC_FLAGS_RST = 1 << 1, // Packet is a public reset packet.
+ PACKET_PUBLIC_FLAGS_MAX = (1 << 2) - 1 // All bits set.
};
enum QuicPacketPrivateFlags {
PACKET_PRIVATE_FLAGS_NONE = 0,
- PACKET_PRIVATE_FLAGS_FEC = 1, // Payload is FEC as opposed to frames.
- PACKET_PRIVATE_FLAGS_MAX = PACKET_PRIVATE_FLAGS_FEC
+ PACKET_PRIVATE_FLAGS_FEC = 1 << 0, // Payload is FEC as opposed to frames.
+ PACKET_PRIVATE_FLAGS_ENTROPY = 1 << 1,
+ PACKET_PRIVATE_FLAGS_FEC_ENTROPY = 1 << 2,
+ PACKET_PRIVATE_FLAGS_MAX = (1 << 3) - 1 // All bits set.
};
enum QuicVersion {
@@ -161,6 +163,8 @@ enum QuicErrorCode {
QUIC_INVALID_RST_STREAM_DATA,
// Connection close data is malformed.
QUIC_INVALID_CONNECTION_CLOSE_DATA,
+ // GoAway data is malformed.
+ QUIC_INVALID_GOAWAY_DATA,
// Ack data is malformed.
QUIC_INVALID_ACK_DATA,
// There was an error decrypting.
@@ -171,10 +175,8 @@ enum QuicErrorCode {
QUIC_PACKET_TOO_LARGE,
// Data was sent for a stream which did not exist.
QUIC_PACKET_FOR_NONEXISTENT_STREAM,
- // The client is going away (browser close, etc.)
- QUIC_CLIENT_GOING_AWAY,
- // The server is going away (restart etc.)
- QUIC_SERVER_GOING_AWAY,
+ // The peer is going away. May be a client or server.
+ QUIC_PEER_GOING_AWAY,
// A stream ID was invalid.
QUIC_INVALID_STREAM_ID,
// Too many streams already open.
@@ -210,7 +212,8 @@ struct NET_EXPORT_PRIVATE QuicPacketPublicHeader {
// Universal header. All QuicPacket headers will have a guid and public flags.
// TODO(satyamshekhar): Support versioning as per Protocol Negotiation Doc.
QuicGuid guid;
- QuicPacketPublicFlags flags;
+ bool reset_flag;
+ bool version_flag;
};
// Header for Data or FEC packets.
@@ -218,8 +221,15 @@ struct QuicPacketHeader {
QuicPacketHeader() {}
explicit QuicPacketHeader(const QuicPacketPublicHeader& header)
: public_header(header) {}
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicPacketHeader& s);
+
QuicPacketPublicHeader public_header;
- QuicPacketPrivateFlags private_flags;
+ bool fec_flag;
+ bool fec_entropy_flag;
+ bool entropy_flag;
+ QuicPacketEntropyHash entropy_hash;
QuicPacketSequenceNumber packet_sequence_number;
QuicFecGroupNumber fec_group;
};
@@ -252,7 +262,7 @@ struct NET_EXPORT_PRIVATE QuicStreamFrame {
// TODO(ianswett): Re-evaluate the trade-offs of hash_set vs set when framing
// is finalized.
-typedef std::set<QuicPacketSequenceNumber> SequenceSet;
+typedef std::set<QuicPacketSequenceNumber> SequenceNumberSet;
// TODO(pwestin): Add a way to enforce the max size of this map.
typedef std::map<QuicPacketSequenceNumber, QuicTime> TimeMap;
@@ -262,16 +272,9 @@ struct NET_EXPORT_PRIVATE ReceivedPacketInfo {
NET_EXPORT_PRIVATE friend std::ostream& operator<<(
std::ostream& os, const ReceivedPacketInfo& s);
- // Records a packet receipt.
- void RecordReceived(QuicPacketSequenceNumber sequence_number);
-
- // True if the sequence number is greater than largest_observed or is listed
- // as missing.
- // Always returns false for sequence numbers less than least_unacked.
- bool IsAwaitingPacket(QuicPacketSequenceNumber sequence_number) const;
-
- // Clears all missing packets less than |least_unacked|.
- void ClearMissingBefore(QuicPacketSequenceNumber least_unacked);
+ // Entropy hash of all packets up to largest observed not including missing
+ // packets.
+ QuicPacketEntropyHash entropy_hash;
// The highest packet sequence number we've observed from the peer.
//
@@ -283,16 +286,34 @@ struct NET_EXPORT_PRIVATE ReceivedPacketInfo {
// list.
QuicPacketSequenceNumber largest_observed;
+ // TODO(satyamshekhar): Can be optimized using an interval set like data
+ // structure.
// The set of packets which we're expecting and have not received.
- SequenceSet missing_packets;
+ SequenceNumberSet missing_packets;
};
+// True if the sequence number is greater than largest_observed or is listed
+// as missing.
+// Always returns false for sequence numbers less than least_unacked.
+bool NET_EXPORT_PRIVATE IsAwaitingPacket(
+ const ReceivedPacketInfo& received_info,
+ QuicPacketSequenceNumber sequence_number);
+
+// Inserts missing packets between [lower, higher).
+void NET_EXPORT_PRIVATE InsertMissingPacketsBetween(
+ ReceivedPacketInfo* received_info,
+ QuicPacketSequenceNumber lower,
+ QuicPacketSequenceNumber higher);
+
struct NET_EXPORT_PRIVATE SentPacketInfo {
SentPacketInfo();
~SentPacketInfo();
NET_EXPORT_PRIVATE friend std::ostream& operator<<(
std::ostream& os, const SentPacketInfo& s);
+ // Entropy hash of all packets up to, but not including, the least unacked
+ // packet.
+ QuicPacketEntropyHash entropy_hash;
// The lowest packet we've sent which is unacked, and we expect an ack for.
QuicPacketSequenceNumber least_unacked;
};
@@ -356,14 +377,12 @@ struct NET_EXPORT_PRIVATE QuicCongestionFeedbackFrame {
struct NET_EXPORT_PRIVATE QuicRstStreamFrame {
QuicRstStreamFrame() {}
- QuicRstStreamFrame(QuicStreamId stream_id, uint64 offset,
- QuicErrorCode error_code)
- : stream_id(stream_id), offset(offset), error_code(error_code) {
+ QuicRstStreamFrame(QuicStreamId stream_id, QuicErrorCode error_code)
+ : stream_id(stream_id), error_code(error_code) {
DCHECK_LE(error_code, std::numeric_limits<uint8>::max());
}
QuicStreamId stream_id;
- uint64 offset;
QuicErrorCode error_code;
std::string error_details;
};
@@ -374,6 +393,17 @@ struct NET_EXPORT_PRIVATE QuicConnectionCloseFrame {
QuicAckFrame ack_frame;
};
+struct NET_EXPORT_PRIVATE QuicGoAwayFrame {
+ QuicGoAwayFrame() {}
+ QuicGoAwayFrame(QuicErrorCode error_code,
+ QuicStreamId last_good_stream_id,
+ const std::string& reason);
+
+ QuicErrorCode error_code;
+ QuicStreamId last_good_stream_id;
+ std::string reason_phrase;
+};
+
struct NET_EXPORT_PRIVATE QuicFrame {
QuicFrame() {}
explicit QuicFrame(QuicPaddingFrame* padding_frame)
@@ -400,6 +430,10 @@ struct NET_EXPORT_PRIVATE QuicFrame {
: type(CONNECTION_CLOSE_FRAME),
connection_close_frame(frame) {
}
+ explicit QuicFrame(QuicGoAwayFrame* frame)
+ : type(GOAWAY_FRAME),
+ goaway_frame(frame) {
+ }
QuicFrameType type;
union {
@@ -409,6 +443,7 @@ struct NET_EXPORT_PRIVATE QuicFrame {
QuicCongestionFeedbackFrame* congestion_feedback_frame;
QuicRstStreamFrame* rst_stream_frame;
QuicConnectionCloseFrame* connection_close_frame;
+ QuicGoAwayFrame* goaway_frame;
};
};
@@ -464,8 +499,8 @@ class NET_EXPORT_PRIVATE QuicData {
class NET_EXPORT_PRIVATE QuicPacket : public QuicData {
public:
static QuicPacket* NewDataPacket(char* buffer,
- size_t length,
- bool owns_buffer) {
+ size_t length,
+ bool owns_buffer) {
return new QuicPacket(buffer, length, owns_buffer, false);
}
@@ -530,6 +565,43 @@ class NET_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData {
DISALLOW_COPY_AND_ASSIGN(QuicEncryptedPacket);
};
+class NET_EXPORT_PRIVATE RetransmittableFrames {
+ public:
+ RetransmittableFrames();
+ ~RetransmittableFrames();
+
+ // Allocates a local copy of the referenced StringPiece has QuicStreamFrame
+ // use it.
+ // Takes ownership of |stream_frame|.
+ const QuicFrame& AddStreamFrame(QuicStreamFrame* stream_frame);
+ // Takes ownership of the frame inside |frame|.
+ const QuicFrame& AddNonStreamFrame(const QuicFrame& frame);
+ const QuicFrames& frames() const { return frames_; }
+
+ private:
+ QuicFrames frames_;
+ // Data referenced by the StringPiece of a QuicStreamFrame.
+ std::vector<std::string*> stream_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(RetransmittableFrames);
+};
+
+struct NET_EXPORT_PRIVATE SerializedPacket {
+ SerializedPacket(QuicPacketSequenceNumber sequence_number,
+ QuicPacket* packet,
+ QuicPacketEntropyHash entropy_hash,
+ RetransmittableFrames* retransmittable_frames)
+ : sequence_number(sequence_number),
+ packet(packet),
+ entropy_hash(entropy_hash),
+ retransmittable_frames(retransmittable_frames) { }
+
+ QuicPacketSequenceNumber sequence_number;
+ QuicPacket* packet;
+ QuicPacketEntropyHash entropy_hash;
+ RetransmittableFrames* retransmittable_frames;
+};
+
// A struct for functions which consume data payloads and fins.
// The first member of the pair indicates bytes consumed.
// The second member of the pair indicates if an incoming fin was consumed.
diff --git a/net/quic/quic_protocol_test.cc b/net/quic/quic_protocol_test.cc
index a77582f..80f6f6a 100644
--- a/net/quic/quic_protocol_test.cc
+++ b/net/quic/quic_protocol_test.cc
@@ -11,41 +11,26 @@ namespace net {
namespace test {
namespace {
-TEST(QuicProtocolTest, ReceivedInfo_RecordReceived) {
+TEST(QuicProtocolTest, IsAawaitingPacket) {
ReceivedPacketInfo received_info;
- received_info.RecordReceived(1u);
+ received_info.largest_observed = 10u;
+ EXPECT_TRUE(IsAwaitingPacket(received_info, 11u));
+ EXPECT_FALSE(IsAwaitingPacket(received_info, 1u));
- EXPECT_EQ(1u, received_info.largest_observed);
- EXPECT_EQ(0u, received_info.missing_packets.size());
-
- received_info.RecordReceived(3u);
-
- EXPECT_EQ(3u, received_info.largest_observed);
- EXPECT_EQ(1u, received_info.missing_packets.size());
-
- received_info.RecordReceived(2u);
-
- EXPECT_EQ(3u, received_info.largest_observed);
- EXPECT_EQ(0u, received_info.missing_packets.size());
+ received_info.missing_packets.insert(10);
+ EXPECT_TRUE(IsAwaitingPacket(received_info, 10u));
}
-TEST(QuicProtocolTest, ReceivedInfo_ClearMissingBefore) {
+TEST(QuicProtocolTest, InsertMissingPacketsBetween) {
ReceivedPacketInfo received_info;
- received_info.RecordReceived(1u);
-
- // Clear nothing.
- received_info.ClearMissingBefore(1);
- EXPECT_EQ(0u, received_info.missing_packets.size());
-
- received_info.RecordReceived(3u);
-
- // Clear the first packet.
- received_info.ClearMissingBefore(2);
- EXPECT_EQ(1u, received_info.missing_packets.size());
-
- // Clear the second packet, which has not been received.
- received_info.ClearMissingBefore(3);
- EXPECT_EQ(0u, received_info.missing_packets.size());
+ InsertMissingPacketsBetween(&received_info, 4u, 10u);
+ EXPECT_EQ(6u, received_info.missing_packets.size());
+
+ QuicPacketSequenceNumber i = 4;
+ for (SequenceNumberSet::iterator it = received_info.missing_packets.begin();
+ it != received_info.missing_packets.end(); ++it, ++i) {
+ EXPECT_EQ(i, *it);
+ }
}
} // namespace
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
index ef0efbe3..dfcea32 100644
--- a/net/quic/quic_session.cc
+++ b/net/quic/quic_session.cc
@@ -39,7 +39,12 @@ class VisitorShim : public QuicConnectionVisitorInterface {
session_->PostProcessAfterData();
}
- virtual void OnAck(AckedPackets acked_packets) OVERRIDE {
+ virtual void OnGoAway(const QuicGoAwayFrame& frame) OVERRIDE {
+ session_->OnGoAway(frame);
+ session_->PostProcessAfterData();
+ }
+
+ virtual void OnAck(const SequenceNumberSet& acked_packets) OVERRIDE {
session_->OnAck(acked_packets);
session_->PostProcessAfterData();
}
@@ -65,7 +70,9 @@ QuicSession::QuicSession(QuicConnection* connection, bool is_server)
max_open_streams_(kDefaultMaxStreamsPerConnection),
next_stream_id_(is_server ? 2 : 3),
is_server_(is_server),
- largest_peer_created_stream_id_(0) {
+ largest_peer_created_stream_id_(0),
+ goaway_received_(false),
+ goaway_sent_(false) {
connection->set_visitor(visitor_shim_.get());
}
@@ -109,7 +116,12 @@ void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) {
if (!stream) {
return; // Errors are handled by GetStream.
}
- stream->OnStreamReset(frame.error_code, frame.offset);
+ stream->OnStreamReset(frame.error_code);
+}
+
+void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) {
+ DCHECK(frame.last_good_stream_id < next_stream_id_);
+ goaway_received_ = true;
}
void QuicSession::ConnectionClose(QuicErrorCode error, bool from_peer) {
@@ -150,18 +162,20 @@ QuicConsumedData QuicSession::WriteData(QuicStreamId id,
StringPiece data,
QuicStreamOffset offset,
bool fin) {
- // TODO(wtc): type mismatch -- connection_->SendStreamData() returns a
- // size_t.
return connection_->SendStreamData(id, data, offset, fin);
}
void QuicSession::SendRstStream(QuicStreamId id,
- QuicErrorCode error,
- QuicStreamOffset offset) {
- connection_->SendRstStream(id, error, offset);
+ QuicErrorCode error) {
+ connection_->SendRstStream(id, error);
CloseStream(id);
}
+void QuicSession::SendGoAway(QuicErrorCode error_code, const string& reason) {
+ goaway_sent_ = true;
+ connection_->SendGoAway(error_code, largest_peer_created_stream_id_, reason);
+}
+
void QuicSession::CloseStream(QuicStreamId stream_id) {
DLOG(INFO) << "Closing stream " << stream_id;
@@ -226,6 +240,12 @@ ReliableQuicStream* QuicSession::GetIncomingReliableStream(
return NULL;
}
+ if (goaway_sent_) {
+ // We've already sent a GoAway
+ connection()->SendRstStream(stream_id, QUIC_PEER_GOING_AWAY);
+ return NULL;
+ }
+
implicitly_created_streams_.erase(stream_id);
if (stream_id > largest_peer_created_stream_id_) {
// TODO(rch) add unit test for this
diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h
index 32bffc1..6f07b9a 100644
--- a/net/quic/quic_session.h
+++ b/net/quic/quic_session.h
@@ -40,9 +40,10 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
const QuicPacketHeader& header,
const std::vector<QuicStreamFrame>& frame) OVERRIDE;
virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE;
+ virtual void OnGoAway(const QuicGoAwayFrame& frame) OVERRIDE;
virtual void ConnectionClose(QuicErrorCode error, bool from_peer) OVERRIDE;
// Not needed for HTTP.
- virtual void OnAck(AckedPackets acked_packets) OVERRIDE {}
+ virtual void OnAck(const SequenceNumberSet& acked_packets) OVERRIDE {}
virtual bool OnCanWrite() OVERRIDE;
// Called by streams when they want to write data to the peer.
@@ -56,8 +57,10 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
bool fin);
// Called by streams when they want to close the stream in both directions.
void SendRstStream(QuicStreamId id,
- QuicErrorCode error,
- QuicStreamOffset offset);
+ QuicErrorCode error);
+
+ // Called when the session wants to go away and not accept any new streams.
+ void SendGoAway(QuicErrorCode error_code, const std::string& reason);
// Removes the stream associated with 'stream_id' from the active stream map.
virtual void CloseStream(QuicStreamId stream_id);
@@ -90,6 +93,14 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
void MarkWriteBlocked(QuicStreamId id);
+ bool goaway_received() const {
+ return goaway_received_;
+ }
+
+ bool goaway_sent() const {
+ return goaway_sent_;
+ }
+
protected:
// Creates a new stream, owned by the caller, to handle a peer-initiated
// stream. Returns NULL and does error handling if the stream can not be
@@ -111,15 +122,10 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
ReliableQuicStream* GetIncomingReliableStream(QuicStreamId stream_id);
- size_t get_max_open_streams() const {
- return max_open_streams_;
- }
-
- protected:
// This is called after every call other than OnConnectionClose from the
// QuicConnectionVisitor to allow post-processing once the work has been done.
// In this case, it deletes streams given that it's safe to do so (no other
- // opterations are being done on the streams at this time)
+ // operations are being done on the streams at this time)
virtual void PostProcessAfterData();
base::hash_map<QuicStreamId, ReliableQuicStream*>* streams() {
@@ -129,6 +135,10 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
return &closed_streams_;
}
+ size_t get_max_open_streams() const {
+ return max_open_streams_;
+ }
+
private:
friend class test::QuicSessionPeer;
friend class VisitorShim;
@@ -161,6 +171,11 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
std::list<QuicStreamId> write_blocked_streams_;
QuicStreamId largest_peer_created_stream_id_;
+
+ // Whether a GoAway has been received.
+ bool goaway_received_;
+ // Whether a GoAway has been sent.
+ bool goaway_sent_;
};
} // namespace net
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
index daf904e..b97a021 100644
--- a/net/quic/quic_session_test.cc
+++ b/net/quic/quic_session_test.cc
@@ -198,6 +198,17 @@ TEST_F(QuicSessionTest, OnCanWriteWithClosedStream) {
EXPECT_TRUE(session_.OnCanWrite());
}
+TEST_F(QuicSessionTest, SendGoAway) {
+ // After sending a GoAway, ensure new incoming streams cannot be created and
+ // result in a RST being sent.
+ EXPECT_CALL(*connection_,
+ SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away."));
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+ EXPECT_TRUE(session_.goaway_sent());
+
+ EXPECT_CALL(*connection_, SendRstStream(3u, QUIC_PEER_GOING_AWAY));
+ EXPECT_FALSE(session_.GetIncomingReliableStream(3u));
+}
} // namespace
} // namespace test
} // namespace net
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index f18e3b3..e817021 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -38,14 +38,14 @@ class QuicStreamFactoryTest : public ::testing::Test {
host));
QuicFramer framer(QuicDecrypter::Create(kNULL),
QuicEncrypter::Create(kNULL));
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*chlo));
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(1, *chlo));
}
scoped_ptr<QuicEncryptedPacket> ConstructShlo() {
scoped_ptr<QuicPacket> shlo(ConstructHandshakePacket(0xDEADBEEF, kSHLO));
QuicFramer framer(QuicDecrypter::Create(kNULL),
QuicEncrypter::Create(kNULL));
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*shlo));
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(1, *shlo));
}
scoped_ptr<QuicEncryptedPacket> ConstructRstPacket(
@@ -53,12 +53,15 @@ class QuicStreamFactoryTest : public ::testing::Test {
QuicStreamId stream_id) {
QuicPacketHeader header;
header.public_header.guid = 0xDEADBEEF;
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
header.packet_sequence_number = num;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.entropy_flag = false;
+ header.fec_entropy_flag = false;
+ header.fec_flag = false;
header.fec_group = 0;
- QuicRstStreamFrame rst(stream_id, 0, QUIC_NO_ERROR);
+ QuicRstStreamFrame rst(stream_id, QUIC_NO_ERROR);
return scoped_ptr<QuicEncryptedPacket>(
ConstructPacket(header, QuicFrame(&rst)));
}
@@ -68,13 +71,15 @@ class QuicStreamFactoryTest : public ::testing::Test {
QuicPacketSequenceNumber least_unacked) {
QuicPacketHeader header;
header.public_header.guid = 0xDEADBEEF;
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
header.packet_sequence_number = 2;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.entropy_flag = false;
+ header.fec_entropy_flag = false;
+ header.fec_flag = false;
header.fec_group = 0;
QuicAckFrame ack(largest_received, least_unacked);
-
QuicCongestionFeedbackFrame feedback;
feedback.type = kTCP;
feedback.tcp.accumulated_number_of_lost_packets = 0;
@@ -86,8 +91,9 @@ class QuicStreamFactoryTest : public ::testing::Test {
frames.push_back(QuicFrame(&ack));
frames.push_back(QuicFrame(&feedback));
scoped_ptr<QuicPacket> packet(
- framer.ConstructFrameDataPacket(header, frames));
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*packet));
+ framer.ConstructFrameDataPacket(header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(
+ framer.EncryptPacket(header.packet_sequence_number, *packet));
}
// Returns a newly created packet to send congestion feedback data.
@@ -95,9 +101,12 @@ class QuicStreamFactoryTest : public ::testing::Test {
QuicPacketSequenceNumber sequence_number) {
QuicPacketHeader header;
header.public_header.guid = 0xDEADBEEF;
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
header.packet_sequence_number = sequence_number;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.entropy_flag = false;
+ header.fec_entropy_flag = false;
+ header.fec_flag = false;
header.fec_group = 0;
QuicCongestionFeedbackFrame frame;
@@ -117,8 +126,9 @@ class QuicStreamFactoryTest : public ::testing::Test {
QuicFrames frames;
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer.ConstructFrameDataPacket(header, frames));
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*packet));
+ framer.ConstructFrameDataPacket(header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(
+ framer.EncryptPacket(header.packet_sequence_number, *packet));
}
MockHostResolver host_resolver_;
diff --git a/net/quic/quic_stream_sequencer.cc b/net/quic/quic_stream_sequencer.cc
index f4959ae..ec2a410 100644
--- a/net/quic/quic_stream_sequencer.cc
+++ b/net/quic/quic_stream_sequencer.cc
@@ -44,11 +44,10 @@ bool QuicStreamSequencer::WillAcceptStreamFrame(
size_t data_len = frame.data.size();
DCHECK_LE(data_len, max_frame_memory_);
- QuicStreamOffset byte_offset = frame.offset;
- if (byte_offset < num_bytes_consumed_ ||
- frames_.find(byte_offset) != frames_.end()) {
- return false;
+ if (IsDuplicate(frame)) {
+ return true;
}
+ QuicStreamOffset byte_offset = frame.offset;
if (data_len > max_frame_memory_) {
// We're never going to buffer this frame and we can't pass it up.
// The stream might only consume part of it and we'd need a partial ack.
@@ -72,6 +71,10 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
// OnStreamFrame. Error handling should be done by the caller.
return false;
}
+ if (IsDuplicate(frame)) {
+ // Silently ignore duplicates.
+ return true;
+ }
QuicStreamOffset byte_offset = frame.offset;
const char* data = frame.data.data();
@@ -98,7 +101,6 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
byte_offset += bytes_consumed;
}
}
-
DVLOG(1) << "Buffering packet at offset " << byte_offset;
frames_.insert(make_pair(byte_offset, string(data, data_len)));
return true;
@@ -151,6 +153,15 @@ bool QuicStreamSequencer::IsClosed() const {
return num_bytes_consumed_ >= close_offset_ && half_close_ == false;
}
+bool QuicStreamSequencer::IsDuplicate(const QuicStreamFrame& frame) const {
+ // A frame is duplicate if the frame offset is smaller than our bytes consumed
+ // or we have stored the frame in our map.
+ // TODO(pwestin): Is it possible that a new frame contain more data even if
+ // the offset is the same?
+ return (frame.offset < num_bytes_consumed_ ||
+ frames_.find(frame.offset) != frames_.end());
+}
+
void QuicStreamSequencer::FlushBufferedFrames() {
FrameMap::iterator it = frames_.find(num_bytes_consumed_);
while (it != frames_.end()) {
diff --git a/net/quic/quic_stream_sequencer.h b/net/quic/quic_stream_sequencer.h
index 37da13e..04a47f0 100644
--- a/net/quic/quic_stream_sequencer.h
+++ b/net/quic/quic_stream_sequencer.h
@@ -49,6 +49,8 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer {
bool OnStreamFrame(const QuicStreamFrame& frame);
// Wait until we've seen 'offset' bytes, and then terminate the stream.
+ // TODO(ianswett): Simplify this method by removing half_close, now that
+ // the sequencer is bypassed for stream resets and half_close is always true.
void CloseStreamAtOffset(QuicStreamOffset offset, bool half_close);
// Once data is buffered, it's up to the stream to read it when the stream
@@ -63,6 +65,9 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer {
// Returns true if the sequencer has delivered a full close.
bool IsClosed() const;
+ // Returns true if the sequencer has received this frame before.
+ bool IsDuplicate(const QuicStreamFrame& frame) const;
+
private:
friend class test::QuicStreamSequencerPeer;
diff --git a/net/quic/quic_stream_sequencer_test.cc b/net/quic/quic_stream_sequencer_test.cc
index 7d22c4c..74937ae 100644
--- a/net/quic/quic_stream_sequencer_test.cc
+++ b/net/quic/quic_stream_sequencer_test.cc
@@ -92,9 +92,9 @@ TEST_F(QuicStreamSequencerTest, RejectOldFrame) {
EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
EXPECT_EQ(0u, sequencer_->frames()->size());
EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
- // Nack this - it matches a past sequence number and we should not see it
+ // Ignore this - it matches a past sequence number and we should not see it
// again.
- EXPECT_FALSE(sequencer_->OnFrame(0, "def", 3));
+ EXPECT_TRUE(sequencer_->OnFrame(0, "def", 3));
EXPECT_EQ(0u, sequencer_->frames()->size());
}
@@ -122,7 +122,7 @@ TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) {
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
// Ignore this - it matches a buffered frame.
// Right now there's no checking that the payload is consistent.
- EXPECT_FALSE(sequencer_->OnFrame(0, "def", 3));
+ EXPECT_TRUE(sequencer_->OnFrame(0, "def", 3));
EXPECT_EQ(1u, sequencer_->frames()->size());
}
diff --git a/net/quic/quic_time_test.cc b/net/quic/quic_time_test.cc
index fc7d7e3..a4a6c55 100644
--- a/net/quic/quic_time_test.cc
+++ b/net/quic/quic_time_test.cc
@@ -98,13 +98,13 @@ TEST_F(QuicTimeTest, SubtractDelta) {
TEST_F(QuicTimeTest, MockClock) {
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
- QuicTime now = clock_.Now();
+ QuicTime now = clock_.ApproximateNow();
QuicTime time = QuicTime::FromMicroseconds(1000);
EXPECT_EQ(now, time);
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
- now = clock_.Now();
+ now = clock_.ApproximateNow();
EXPECT_NE(now, time);
diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc
index 0cfac44..0628dee 100644
--- a/net/quic/quic_utils.cc
+++ b/net/quic/quic_utils.cc
@@ -55,13 +55,13 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_INVALID_FEC_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_RST_STREAM_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_CONNECTION_CLOSE_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_GOAWAY_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_ACK_DATA);
RETURN_STRING_LITERAL(QUIC_DECRYPTION_FAILURE);
RETURN_STRING_LITERAL(QUIC_ENCRYPTION_FAILURE);
RETURN_STRING_LITERAL(QUIC_PACKET_TOO_LARGE);
RETURN_STRING_LITERAL(QUIC_PACKET_FOR_NONEXISTENT_STREAM);
- RETURN_STRING_LITERAL(QUIC_CLIENT_GOING_AWAY);
- RETURN_STRING_LITERAL(QUIC_SERVER_GOING_AWAY);
+ RETURN_STRING_LITERAL(QUIC_PEER_GOING_AWAY);
RETURN_STRING_LITERAL(QUIC_CRYPTO_TAGS_OUT_OF_ORDER);
RETURN_STRING_LITERAL(QUIC_CRYPTO_TOO_MANY_ENTRIES);
RETURN_STRING_LITERAL(QUIC_CRYPTO_INVALID_VALUE_LENGTH)
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
index 1de1eb9..79b060e 100644
--- a/net/quic/reliable_quic_stream.cc
+++ b/net/quic/reliable_quic_stream.cc
@@ -14,7 +14,6 @@ ReliableQuicStream::ReliableQuicStream(QuicStreamId id,
QuicSession* session)
: sequencer_(this),
id_(id),
- offset_(0),
session_(session),
visitor_(NULL),
stream_bytes_read_(0),
@@ -48,21 +47,21 @@ bool ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
// We don't want to be reading: blackhole the data.
return true;
}
+ // Note: This count include duplicate data received.
+ stream_bytes_read_ += frame.data.length();
bool accepted = sequencer_.OnStreamFrame(frame);
if (frame.fin) {
- sequencer_.CloseStreamAtOffset(frame.offset + frame.data.size(),
- true);
+ sequencer_.CloseStreamAtOffset(frame.offset + frame.data.size(), true);
}
return accepted;
}
-void ReliableQuicStream::OnStreamReset(QuicErrorCode error,
- QuicStreamOffset offset) {
+void ReliableQuicStream::OnStreamReset(QuicErrorCode error) {
error_ = error;
- sequencer_.CloseStreamAtOffset(offset, false); // Full close.
+ TerminateFromPeer(false); // Full close.
}
void ReliableQuicStream::ConnectionClose(QuicErrorCode error, bool from_peer) {
@@ -84,7 +83,7 @@ void ReliableQuicStream::TerminateFromPeer(bool half_close) {
void ReliableQuicStream::Close(QuicErrorCode error) {
error_ = error;
- session()->SendRstStream(id(), error, offset_);
+ session()->SendRstStream(id(), error);
}
bool ReliableQuicStream::IsHalfClosed() const {
@@ -151,8 +150,7 @@ QuicConsumedData ReliableQuicStream::WriteDataInternal(
}
QuicConsumedData consumed_data =
- session()->WriteData(id(), data, offset_, fin);
- offset_ += consumed_data.bytes_consumed;
+ session()->WriteData(id(), data, stream_bytes_written_, fin);
stream_bytes_written_ += consumed_data.bytes_consumed;
if (consumed_data.bytes_consumed == data.length()) {
if (fin && consumed_data.fin_consumed) {
diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h
index 50ed951..1c35cf0 100644
--- a/net/quic/reliable_quic_stream.h
+++ b/net/quic/reliable_quic_stream.h
@@ -54,9 +54,7 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
virtual void OnClose();
// Called when we get a stream reset from the client.
- // The rst will be passed through the sequencer, which will call
- // TerminateFromPeer when 'offset' bytes have been processed.
- void OnStreamReset(QuicErrorCode error, QuicStreamOffset ofset);
+ void OnStreamReset(QuicErrorCode error);
// Called when we get or send a connection close, and should immediately
// close the stream. This is not passed through the sequencer,
@@ -126,7 +124,6 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
QuicStreamSequencer sequencer_;
QuicStreamId id_;
- QuicStreamOffset offset_;
QuicSession* session_;
// Optional visitor of this stream to be notified when the stream is closed.
Visitor* visitor_;
diff --git a/net/quic/test_tools/mock_clock.cc b/net/quic/test_tools/mock_clock.cc
index b85ae19..d9fb386 100644
--- a/net/quic/test_tools/mock_clock.cc
+++ b/net/quic/test_tools/mock_clock.cc
@@ -21,6 +21,10 @@ QuicTime MockClock::Now() const {
return now_;
}
+QuicTime MockClock::ApproximateNow() const {
+ return now_;
+}
+
QuicTime::Delta MockClock::NowAsDeltaSinceUnixEpoch() const {
return now_.Subtract(QuicTime::Zero());
}
diff --git a/net/quic/test_tools/mock_clock.h b/net/quic/test_tools/mock_clock.h
index e1e84c7..7497aa2 100644
--- a/net/quic/test_tools/mock_clock.h
+++ b/net/quic/test_tools/mock_clock.h
@@ -23,6 +23,8 @@ class MockClock : public QuicClock {
virtual QuicTime Now() const OVERRIDE;
+ virtual QuicTime ApproximateNow() const OVERRIDE;
+
virtual QuicTime::Delta NowAsDeltaSinceUnixEpoch() const OVERRIDE;
base::TimeTicks NowInTicks() const;
diff --git a/net/quic/test_tools/mock_random.cc b/net/quic/test_tools/mock_random.cc
index 3b522ae..f7b8ea0 100644
--- a/net/quic/test_tools/mock_random.cc
+++ b/net/quic/test_tools/mock_random.cc
@@ -14,6 +14,10 @@ uint64 MockRandom::RandUint64() {
return 0xDEADBEEF;
}
+bool MockRandom::RandBool() {
+ return false;
+}
+
void MockRandom::Reseed(const void* additional_entropy, size_t entropy_len) {
}
diff --git a/net/quic/test_tools/mock_random.h b/net/quic/test_tools/mock_random.h
index 780057b..b583182 100644
--- a/net/quic/test_tools/mock_random.h
+++ b/net/quic/test_tools/mock_random.h
@@ -17,6 +17,8 @@ class MockRandom : public QuicRandom {
virtual void RandBytes(void* data, size_t len) OVERRIDE;
// Returns 0xDEADBEEF.
virtual uint64 RandUint64() OVERRIDE;
+ // Returns false.
+ virtual bool RandBool() OVERRIDE;
// Does nothing.
virtual void Reseed(const void* additional_entropy,
size_t entropy_len) OVERRIDE;
diff --git a/net/quic/test_tools/quic_connection_peer.cc b/net/quic/test_tools/quic_connection_peer.cc
index 7befa32..d834d5e 100644
--- a/net/quic/test_tools/quic_connection_peer.cc
+++ b/net/quic/test_tools/quic_connection_peer.cc
@@ -33,7 +33,9 @@ void QuicConnectionPeer::SetSendAlgorithm(
}
// static
-QuicAckFrame* QuicConnectionPeer::GetOutgoingAck(QuicConnection* connection) {
+QuicAckFrame* QuicConnectionPeer::GetOutgoingAck(
+ QuicConnection* connection) {
+ connection->UpdateOutgoingAck();
return &connection->outgoing_ack_;
}
@@ -72,8 +74,33 @@ size_t QuicConnectionPeer::GetRetransmissionCount(
QuicPacketSequenceNumber sequence_number) {
QuicConnection::RetransmissionMap::iterator it =
connection->retransmission_map_.find(sequence_number);
+ DCHECK(connection->retransmission_map_.end() != it);
return it->second.number_retransmissions;
}
+// static
+QuicPacketEntropyHash QuicConnectionPeer::GetSentEntropyHash(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number) {
+ return connection->entropy_manager_.SentEntropyHash(sequence_number);
+}
+
+// static
+bool QuicConnectionPeer::IsValidEntropy(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber largest_observed,
+ const SequenceNumberSet& missing_packets,
+ QuicPacketEntropyHash entropy_hash) {
+ return connection->entropy_manager_.IsValidEntropy(
+ largest_observed, missing_packets, entropy_hash);
+}
+
+// static
+QuicPacketEntropyHash QuicConnectionPeer::ReceivedEntropyHash(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number) {
+ return connection->entropy_manager_.ReceivedEntropyHash(sequence_number);
+}
+
} // namespace test
} // namespace net
diff --git a/net/quic/test_tools/quic_connection_peer.h b/net/quic/test_tools/quic_connection_peer.h
index deb313e0..8c3ff67 100644
--- a/net/quic/test_tools/quic_connection_peer.h
+++ b/net/quic/test_tools/quic_connection_peer.h
@@ -49,6 +49,19 @@ class QuicConnectionPeer {
QuicConnection* connection,
QuicPacketSequenceNumber sequence_number);
+ static QuicPacketEntropyHash GetSentEntropyHash(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number);
+
+ static bool IsValidEntropy(QuicConnection* connection,
+ QuicPacketSequenceNumber largest_observed,
+ const SequenceNumberSet& missing_packets,
+ QuicPacketEntropyHash entropy_hash);
+
+ static QuicPacketEntropyHash ReceivedEntropyHash(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number);
+
private:
DISALLOW_COPY_AND_ASSIGN(QuicConnectionPeer);
};
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index c53f797..a96949c 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -29,25 +29,52 @@ bool NoOpFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) {
return true;
}
-FramerVisitorCapturingAcks::FramerVisitorCapturingAcks() {
+FramerVisitorCapturingFrames::FramerVisitorCapturingFrames() : frame_count_(0) {
}
-FramerVisitorCapturingAcks::~FramerVisitorCapturingAcks() {
+FramerVisitorCapturingFrames::~FramerVisitorCapturingFrames() {
}
-bool FramerVisitorCapturingAcks::OnPacketHeader(
+bool FramerVisitorCapturingFrames::OnPacketHeader(
const QuicPacketHeader& header) {
header_ = header;
+ frame_count_ = 0;
return true;
}
-void FramerVisitorCapturingAcks::OnAckFrame(const QuicAckFrame& frame) {
+void FramerVisitorCapturingFrames::OnStreamFrame(const QuicStreamFrame& frame) {
+ // TODO(ianswett): Own the underlying string, so it will not exist outside
+ // this callback.
+ stream_frames_.push_back(frame);
+ ++frame_count_;
+}
+
+void FramerVisitorCapturingFrames::OnAckFrame(const QuicAckFrame& frame) {
ack_.reset(new QuicAckFrame(frame));
+ ++frame_count_;
}
-void FramerVisitorCapturingAcks::OnCongestionFeedbackFrame(
+void FramerVisitorCapturingFrames::OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) {
feedback_.reset(new QuicCongestionFeedbackFrame(frame));
+ ++frame_count_;
+}
+
+void FramerVisitorCapturingFrames::OnRstStreamFrame(
+ const QuicRstStreamFrame& frame) {
+ rst_.reset(new QuicRstStreamFrame(frame));
+ ++frame_count_;
+}
+
+void FramerVisitorCapturingFrames::OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) {
+ close_.reset(new QuicConnectionCloseFrame(frame));
+ ++frame_count_;
+}
+
+void FramerVisitorCapturingFrames::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ goaway_.reset(new QuicGoAwayFrame(frame));
+ ++frame_count_;
}
FramerVisitorCapturingPublicReset::FramerVisitorCapturingPublicReset() {
@@ -108,7 +135,7 @@ PacketSavingConnection::~PacketSavingConnection() {
bool PacketSavingConnection::SendOrQueuePacket(
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
- bool force) {
+ QuicPacketEntropyHash hash) {
packets_.push_back(packet);
return true;
}
@@ -220,9 +247,13 @@ static QuicPacket* ConstructPacketFromHandshakeMessage(
QuicPacketHeader header;
header.public_header.guid = guid;
- header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
header.packet_sequence_number = 1;
- header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.entropy_flag = false;
+ header.entropy_hash = 0;
+ header.fec_flag = false;
+ header.fec_entropy_flag = false;
header.fec_group = 0;
QuicStreamFrame stream_frame(kCryptoStreamId, false, 0,
@@ -231,7 +262,7 @@ static QuicPacket* ConstructPacketFromHandshakeMessage(
QuicFrame frame(&stream_frame);
QuicFrames frames;
frames.push_back(frame);
- return quic_framer.ConstructFrameDataPacket(header, frames);
+ return quic_framer.ConstructFrameDataPacket(header, frames).packet;
}
QuicPacket* ConstructHandshakePacket(QuicGuid guid, CryptoTag tag) {
@@ -268,5 +299,10 @@ QuicPacket* ConstructServerHelloPacket(QuicGuid guid,
return ConstructPacketFromHandshakeMessage(guid, message);
}
+QuicPacketEntropyHash TestEntropyCalculator::ReceivedEntropyHash(
+ QuicPacketSequenceNumber sequence_number) const {
+ return 1u;
+}
+
} // namespace test
} // namespace net
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index 1a23e48..7724d16 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -7,6 +7,8 @@
#ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
#define NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
+#include <vector>
+
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_framer.h"
@@ -63,6 +65,7 @@ class MockFramerVisitor : public QuicFramerVisitorInterface {
MOCK_METHOD1(OnRstStreamFrame, void(const QuicRstStreamFrame& frame));
MOCK_METHOD1(OnConnectionCloseFrame,
void(const QuicConnectionCloseFrame& frame));
+ MOCK_METHOD1(OnGoAwayFrame, void(const QuicGoAwayFrame& frame));
MOCK_METHOD0(OnPacketComplete, void());
private:
@@ -88,50 +91,68 @@ class NoOpFramerVisitor : public QuicFramerVisitorInterface {
virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE {}
virtual void OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) OVERRIDE {}
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE {}
virtual void OnPacketComplete() OVERRIDE {}
private:
DISALLOW_COPY_AND_ASSIGN(NoOpFramerVisitor);
};
-class FramerVisitorCapturingAcks : public NoOpFramerVisitor {
+class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor {
+ public:
+ FramerVisitorCapturingPublicReset();
+ virtual ~FramerVisitorCapturingPublicReset();
+
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& packet) OVERRIDE;
+
+ const QuicPublicResetPacket public_reset_packet() {
+ return public_reset_packet_;
+ }
+
+ private:
+ QuicPublicResetPacket public_reset_packet_;
+};
+
+class FramerVisitorCapturingFrames : public NoOpFramerVisitor {
public:
- FramerVisitorCapturingAcks();
- virtual ~FramerVisitorCapturingAcks();
+ FramerVisitorCapturingFrames();
+ virtual ~FramerVisitorCapturingFrames();
// NoOpFramerVisitor
+
virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
+ virtual void OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
virtual void OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
virtual void OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) OVERRIDE;
+ virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
+ virtual void OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) OVERRIDE;
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
+ size_t frame_count() { return frame_count_; }
QuicPacketHeader* header() { return &header_; }
-
+ const std::vector<QuicStreamFrame>* stream_frames() {
+ return &stream_frames_;
+ }
QuicAckFrame* ack() { return ack_.get(); }
QuicCongestionFeedbackFrame* feedback() { return feedback_.get(); }
+ QuicRstStreamFrame* rst() { return rst_.get(); }
+ QuicConnectionCloseFrame* close() { return close_.get(); }
+ QuicGoAwayFrame* goaway() { return goaway_.get(); }
private:
+ size_t frame_count_;
QuicPacketHeader header_;
+ std::vector<QuicStreamFrame> stream_frames_;
scoped_ptr<QuicAckFrame> ack_;
scoped_ptr<QuicCongestionFeedbackFrame> feedback_;
+ scoped_ptr<QuicRstStreamFrame> rst_;
+ scoped_ptr<QuicConnectionCloseFrame> close_;
+ scoped_ptr<QuicGoAwayFrame> goaway_;
- DISALLOW_COPY_AND_ASSIGN(FramerVisitorCapturingAcks);
-};
-
-class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor {
- public:
- FramerVisitorCapturingPublicReset();
- virtual ~FramerVisitorCapturingPublicReset();
-
- virtual void OnPublicResetPacket(
- const QuicPublicResetPacket& packet) OVERRIDE;
-
- const QuicPublicResetPacket public_reset_packet() {
- return public_reset_packet_;
- }
-
- private:
- QuicPublicResetPacket public_reset_packet_;
+ DISALLOW_COPY_AND_ASSIGN(FramerVisitorCapturingFrames);
};
class MockConnectionVisitor : public QuicConnectionVisitorInterface {
@@ -144,8 +165,9 @@ class MockConnectionVisitor : public QuicConnectionVisitorInterface {
const QuicPacketHeader& header,
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(AckedPackets acked_packets));
+ MOCK_METHOD1(OnAck, void(const SequenceNumberSet& acked_packets));
MOCK_METHOD0(OnCanWrite, bool());
private:
@@ -188,10 +210,11 @@ class MockConnection : public QuicConnection {
const QuicEncryptedPacket& packet));
MOCK_METHOD1(SendConnectionClose, void(QuicErrorCode error));
- MOCK_METHOD3(SendRstStream, void(QuicStreamId id,
- QuicErrorCode error,
- QuicStreamOffset offset));
-
+ MOCK_METHOD2(SendRstStream, void(QuicStreamId id,
+ QuicErrorCode error));
+ MOCK_METHOD3(SendGoAway, void(QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const string& reason));
MOCK_METHOD0(OnCanWrite, bool());
void ProcessUdpPacketInternal(const IPEndPoint& self_address,
@@ -212,7 +235,7 @@ class PacketSavingConnection : public MockConnection {
virtual bool SendOrQueuePacket(QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
- bool force) OVERRIDE;
+ QuicPacketEntropyHash entropy_hash) OVERRIDE;
std::vector<QuicPacket*> packets_;
@@ -249,19 +272,34 @@ class MockSendAlgorithm : public SendAlgorithmInterface {
MockSendAlgorithm();
virtual ~MockSendAlgorithm();
- MOCK_METHOD2(OnIncomingQuicCongestionFeedbackFrame,
- void(const QuicCongestionFeedbackFrame&, const SentPacketsMap&));
+ MOCK_METHOD4(OnIncomingQuicCongestionFeedbackFrame,
+ void(const QuicCongestionFeedbackFrame&,
+ QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth,
+ const SentPacketsMap&));
MOCK_METHOD3(OnIncomingAck,
void(QuicPacketSequenceNumber, QuicByteCount, QuicTime::Delta));
- MOCK_METHOD1(OnIncomingLoss, void(int number_of_lost_packets));
- MOCK_METHOD3(SentPacket, void(QuicPacketSequenceNumber, QuicByteCount, bool));
- MOCK_METHOD1(TimeUntilSend, QuicTime::Delta(bool));
+ MOCK_METHOD1(OnIncomingLoss, void(QuicTime));
+ MOCK_METHOD4(SentPacket, void(QuicTime sent_time, QuicPacketSequenceNumber,
+ QuicByteCount, bool));
+ MOCK_METHOD2(TimeUntilSend, QuicTime::Delta(QuicTime now, bool));
MOCK_METHOD0(BandwidthEstimate, QuicBandwidth(void));
private:
DISALLOW_COPY_AND_ASSIGN(MockSendAlgorithm);
};
+class TestEntropyCalculator :
+ public QuicReceivedEntropyHashCalculatorInterface {
+ public:
+ TestEntropyCalculator() { }
+ virtual ~TestEntropyCalculator() { }
+
+ virtual QuicPacketEntropyHash ReceivedEntropyHash(
+ QuicPacketSequenceNumber sequence_number) const OVERRIDE;
+
+};
+
} // namespace test
} // namespace net
diff --git a/net/quic/test_tools/reliable_quic_stream_peer.cc b/net/quic/test_tools/reliable_quic_stream_peer.cc
index 12ceca2..5119d03 100644
--- a/net/quic/test_tools/reliable_quic_stream_peer.cc
+++ b/net/quic/test_tools/reliable_quic_stream_peer.cc
@@ -16,9 +16,10 @@ void ReliableQuicStreamPeer::SetWriteSideClosed(bool value,
}
// static
-void ReliableQuicStreamPeer::SetOffset(QuicStreamOffset offset,
- ReliableQuicStream* stream) {
- stream->offset_ = offset;
+void ReliableQuicStreamPeer::SetStreamBytesWritten(
+ QuicStreamOffset stream_bytes_written,
+ ReliableQuicStream* stream) {
+ stream->stream_bytes_written_ = stream_bytes_written;
}
} // namespace test
diff --git a/net/quic/test_tools/reliable_quic_stream_peer.h b/net/quic/test_tools/reliable_quic_stream_peer.h
index aaa8434..da229da 100644
--- a/net/quic/test_tools/reliable_quic_stream_peer.h
+++ b/net/quic/test_tools/reliable_quic_stream_peer.h
@@ -17,7 +17,8 @@ namespace test {
class ReliableQuicStreamPeer {
public:
static void SetWriteSideClosed(bool value, ReliableQuicStream* stream);
- static void SetOffset(QuicStreamOffset offset, ReliableQuicStream* stream);
+ static void SetStreamBytesWritten(QuicStreamOffset stream_bytes_written,
+ ReliableQuicStream* stream);
private:
DISALLOW_COPY_AND_ASSIGN(ReliableQuicStreamPeer);
diff --git a/net/quic/test_tools/simple_quic_framer.cc b/net/quic/test_tools/simple_quic_framer.cc
new file mode 100644
index 0000000..2ffd9ca
--- /dev/null
+++ b/net/quic/test_tools/simple_quic_framer.cc
@@ -0,0 +1,180 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/simple_quic_framer.h"
+
+using base::StringPiece;
+using std::string;
+using std::vector;
+
+namespace net {
+namespace test {
+
+class SimpleFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ SimpleFramerVisitor()
+ : error_(QUIC_NO_ERROR) {
+ }
+
+ virtual void OnError(QuicFramer* framer) {
+ error_ = framer->error();
+ }
+
+ virtual void OnPacket() {}
+ virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) {}
+ virtual void OnRevivedPacket() {}
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) {
+ has_header_ = true;
+ header_ = header;
+ return true;
+ }
+
+ virtual void OnFecProtectedPayload(StringPiece payload) {}
+
+ virtual void OnStreamFrame(const QuicStreamFrame& frame) {
+ // Save a copy of the data so it is valid after the packet is processed.
+ stream_data_.push_back(frame.data.as_string());
+ QuicStreamFrame stream_frame(frame);
+ // Make sure that the stream frame points to this data.
+ stream_frame.data = stream_data_.back();
+ stream_frames_.push_back(stream_frame);
+ }
+
+ virtual void OnAckFrame(const QuicAckFrame& frame) {
+ ack_frames_.push_back(frame);
+ }
+
+ virtual void OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) {
+ feedback_frames_.push_back(frame);
+ }
+
+ virtual void OnFecData(const QuicFecData& fec) {
+ fec_data_ = fec;
+ fec_redundancy_ = fec_data_.redundancy.as_string();
+ fec_data_.redundancy = fec_redundancy_;
+ }
+
+ virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) {
+ rst_stream_frames_.push_back(frame);
+ }
+
+ virtual void OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) {
+ connection_close_frames_.push_back(frame);
+ }
+
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ goaway_frames_.push_back(frame);
+ }
+
+ virtual void OnPacketComplete() {}
+
+ const QuicPacketHeader& header() const { return header_; }
+ const vector<QuicAckFrame>& ack_frames() const { return ack_frames_; }
+ const vector<QuicConnectionCloseFrame>& connection_close_frames() const {
+ return connection_close_frames_;
+ }
+ const vector<QuicCongestionFeedbackFrame>& feedback_frames() const {
+ return feedback_frames_;
+ }
+ const vector<QuicGoAwayFrame>& goaway_frames() const {
+ return goaway_frames_;
+ }
+ const vector<QuicRstStreamFrame>& rst_stream_frames() const {
+ return rst_stream_frames_;
+ }
+ const vector<QuicStreamFrame>& stream_frames() const {
+ return stream_frames_;
+ }
+ const QuicFecData& fec_data() const {
+ return fec_data_;
+ }
+
+ private:
+ QuicErrorCode error_;
+ bool has_header_;
+ QuicPacketHeader header_;
+ QuicFecData fec_data_;
+ string fec_redundancy_;
+ vector<QuicAckFrame> ack_frames_;
+ vector<QuicCongestionFeedbackFrame> feedback_frames_;
+ vector<QuicStreamFrame> stream_frames_;
+ vector<QuicRstStreamFrame> rst_stream_frames_;
+ vector<QuicGoAwayFrame> goaway_frames_;
+ vector<QuicConnectionCloseFrame> connection_close_frames_;
+ vector<string> stream_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleFramerVisitor);
+};
+
+SimpleQuicFramer::SimpleQuicFramer()
+ : framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)),
+ visitor_(NULL) {
+}
+
+SimpleQuicFramer::~SimpleQuicFramer() {
+ delete visitor_;
+}
+
+bool SimpleQuicFramer::ProcessPacket(const QuicPacket& packet) {
+ scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(0, packet));
+ LOG(INFO) << __FUNCTION__ << encrypted.get();
+ LOG(INFO) << __FUNCTION__ << encrypted->length();
+ return ProcessPacket(*encrypted);
+}
+
+bool SimpleQuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
+ delete visitor_;
+ visitor_ = new SimpleFramerVisitor;
+ framer_.set_visitor(visitor_);
+ return framer_.ProcessPacket(packet);
+}
+
+const QuicPacketHeader& SimpleQuicFramer::header() const {
+ return visitor_->header();
+}
+
+const QuicFecData& SimpleQuicFramer::fec_data() const {
+ return visitor_->fec_data();
+}
+
+size_t SimpleQuicFramer::num_frames() const {
+ return ack_frames().size() +
+ stream_frames().size() +
+ feedback_frames().size() +
+ rst_stream_frames().size() +
+ goaway_frames().size() +
+ connection_close_frames().size();
+}
+
+const vector<QuicAckFrame>& SimpleQuicFramer::ack_frames() const {
+ return visitor_->ack_frames();
+}
+
+const vector<QuicStreamFrame>& SimpleQuicFramer::stream_frames() const {
+ return visitor_->stream_frames();
+}
+
+const vector<QuicRstStreamFrame>& SimpleQuicFramer::rst_stream_frames() const {
+ return visitor_->rst_stream_frames();
+}
+
+const vector<QuicCongestionFeedbackFrame>&
+SimpleQuicFramer::feedback_frames() const {
+ return visitor_->feedback_frames();
+}
+
+const vector<QuicGoAwayFrame>&
+SimpleQuicFramer::goaway_frames() const {
+ return visitor_->goaway_frames();
+}
+
+const vector<QuicConnectionCloseFrame>&
+SimpleQuicFramer::connection_close_frames() const {
+ return visitor_->connection_close_frames();
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/simple_quic_framer.h b/net/quic/test_tools/simple_quic_framer.h
new file mode 100644
index 0000000..f794270
--- /dev/null
+++ b/net/quic/test_tools/simple_quic_framer.h
@@ -0,0 +1,55 @@
+// 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.
+
+#ifndef NET_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_
+#define NET_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_
+
+#include <vector>
+
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+struct QuicAckFrame;
+class QuicConnection;
+class QuicConnectionVisitorInterface;
+class QuicPacketCreator;
+class ReceiveAlgorithmInterface;
+class SendAlgorithmInterface;
+
+namespace test {
+
+class SimpleFramerVisitor;
+
+// Peer to make public a number of otherwise private QuicConnection methods.
+class SimpleQuicFramer {
+ public:
+ SimpleQuicFramer();
+ ~SimpleQuicFramer();
+
+ bool ProcessPacket(const QuicEncryptedPacket& packet);
+ bool ProcessPacket(const QuicPacket& packet);
+
+ const QuicPacketHeader& header() const;
+ size_t num_frames() const;
+ const std::vector<QuicAckFrame>& ack_frames() const;
+ const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const;
+ const std::vector<QuicCongestionFeedbackFrame>& feedback_frames() const;
+ const std::vector<QuicGoAwayFrame>& goaway_frames() const;
+ const std::vector<QuicRstStreamFrame>& rst_stream_frames() const;
+ const std::vector<QuicStreamFrame>& stream_frames() const;
+ const QuicFecData& fec_data() const;
+
+ private:
+ QuicFramer framer_;
+ SimpleFramerVisitor* visitor_;
+ DISALLOW_COPY_AND_ASSIGN(SimpleQuicFramer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_