diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-03 07:47:41 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-03 07:47:41 +0000 |
commit | fee17f78c16789e740b050327e67abe76b500884 (patch) | |
tree | e6697c04381916b358fbc6759043ab28b9be56c5 | |
parent | 7016f66d683e581d805c25cbde4db1fea933b255 (diff) | |
download | chromium_src-fee17f78c16789e740b050327e67abe76b500884.zip chromium_src-fee17f78c16789e740b050327e67abe76b500884.tar.gz chromium_src-fee17f78c16789e740b050327e67abe76b500884.tar.bz2 |
Land recent QUIC changes.
Count the number of retransmissions for a packet due to RTO and explicit nacks and implement exponential backoff based on number of retransmissions.
Merge internal change: 41823946
QuicConnection now packetizes acks and congestion control frames even more lazily and packs control frames with up to 1 stream frame.
Merge internal change: 41805161
Remove nitty-gritties of QuicFramer from QuicTimeWaitListManager and remove some static methods from QuicFramer.
Merge internal change: 41795314
Store socket addresses in QuicConnection.h instead of pushing it down to QuicFramer.h and getting it back from there.
Merge internal change: 41768211
Retransmit largest_observed_ packet if its missing.
Merge internal change: 41743584
Split framer packet creation into size determination and packet creation methods in order to ensure the maximum number of frames are fit into a single packet.
Merge internal change: 41702016
Handle packet reordering for queued packets.
Merge internal change: 41701179
Bug fix for retransmission order.
Merge internal change: 41638847
Adding basic dispatcher stats for QUIC
Merge internal change: 41630921
Minor fixes in quic_connection.cc
Merge internal change: 41610652
Use priority queue to maintain the list of sequence numbers to be retransmitted. This will enable us to do an exponential backoff for lost packets.
Merge internal change: 41582526
Adding DISALLOW_COPY_AND_ASSIGN to all congestion control classes that were missing it. And a few nits.
Merge internal change: 41580553
Break circular dependency between quic_protocol.h and quic_bandwidth.h
Merge internal change: 41578776
Fix uninitialized memory access in quic_framer_test.
Merge internal change: 41558816
Cleanup, consolidated quic_send_scheduler with quic_congestion_manager
Merge internal change: 41549823
Set the retransmit alarm only when we successfully write the packet to the socket instead of setting it when we attempt to write.
Merge internal change: 41547192
Introduced QuicByteCount and moved TCP receive_bytes packing to QuicFramer where it belongs.
Merge internal change: 41546840
Fix a bug with how FEC groups were being created in QUIC. Now opening multiple and always closing the group.
Merge internal change: 41538221
Removing QUIC logspam
Merge internal change: 41537896
Exposing ReadPublicHeader and ReadPublicResetPacket static methods which don't take a dependency on reader_(unlike Process..() methods).
Merge internal change: 41535886
Use "test" not "testing" for test namespaces to be consistent with other QUIC code.
Merge internal change: 41534488
Fix a number of minor issues that came up in landing recent chrome changes.
Merge internal change: 41531793
Removed the QuicReceiptMetricsCollector and started using the ReceiveAlgorithmInterface directly in the CongestionManager. The QuicReceiptMetricsCollector did not provide any functionality.
Merge internal change: 41486246
Integrating new quic bandwidth class
Merge internal change: 41473682
R=jar@chromium.org
BUG=
Review URL: https://chromiumcodereview.appspot.com/12145002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@180299 0039d316-1c4b-4281-b951-d872f2087c98
62 files changed, 1694 insertions, 1503 deletions
diff --git a/net/net.gyp b/net/net.gyp index 035ec22..c7bf23b 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -667,10 +667,6 @@ 'quic/congestion_control/paced_sender.h', 'quic/congestion_control/quic_congestion_manager.cc', 'quic/congestion_control/quic_congestion_manager.h', - 'quic/congestion_control/quic_receipt_metrics_collector.cc', - 'quic/congestion_control/quic_receipt_metrics_collector.h', - 'quic/congestion_control/quic_send_scheduler.cc', - 'quic/congestion_control/quic_send_scheduler.h', 'quic/congestion_control/receive_algorithm_interface.cc', 'quic/congestion_control/receive_algorithm_interface.h', 'quic/congestion_control/send_algorithm_interface.cc', @@ -1482,8 +1478,6 @@ 'quic/congestion_control/hybrid_slow_start_test.cc', 'quic/congestion_control/leaky_bucket_test.cc', 'quic/congestion_control/paced_sender_test.cc', - 'quic/congestion_control/quic_receipt_metrics_collector_test.cc', - 'quic/congestion_control/quic_send_scheduler_test.cc', 'quic/congestion_control/tcp_cubic_sender_test.cc', 'quic/congestion_control/tcp_receiver_test.cc', 'quic/crypto/crypto_framer_test.cc', @@ -1516,11 +1510,13 @@ 'quic/quic_framer_test.cc', 'quic/quic_http_stream_test.cc', 'quic/quic_packet_creator_test.cc', + 'quic/quic_protocol_test.cc', 'quic/quic_reliable_client_stream_test.cc', 'quic/quic_session_test.cc', 'quic/quic_stream_factory_test.cc', 'quic/quic_stream_sequencer_test.cc', 'quic/quic_time_test.cc', + 'quic/reliable_quic_stream_test.cc', 'socket/buffered_write_stream_socket_unittest.cc', 'socket/client_socket_pool_base_unittest.cc', 'socket/deterministic_socket_data_unittest.cc', diff --git a/net/quic/congestion_control/cubic.h b/net/quic/congestion_control/cubic.h index 4c013a9..e365bbe 100644 --- a/net/quic/congestion_control/cubic.h +++ b/net/quic/congestion_control/cubic.h @@ -79,6 +79,8 @@ class NET_EXPORT_PRIVATE Cubic { // Last congestion window in packets computed by cubic function. QuicTcpCongestionWindow last_target_congestion_window_; + + DISALLOW_COPY_AND_ASSIGN(Cubic); }; } // namespace net diff --git a/net/quic/congestion_control/fix_rate_receiver.cc b/net/quic/congestion_control/fix_rate_receiver.cc index b2b5f8e..950b49c 100644 --- a/net/quic/congestion_control/fix_rate_receiver.cc +++ b/net/quic/congestion_control/fix_rate_receiver.cc @@ -14,27 +14,22 @@ namespace { namespace net { FixRateReceiver::FixRateReceiver() - : bitrate_in_bytes_per_second_(kInitialBitrate) { + : configured_rate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)) { } bool FixRateReceiver::GenerateCongestionFeedback( QuicCongestionFeedbackFrame* feedback) { feedback->type = kFixRate; - feedback->fix_rate.bitrate_in_bytes_per_second = - bitrate_in_bytes_per_second_; + feedback->fix_rate.bitrate = configured_rate_; return true; } void FixRateReceiver::RecordIncomingPacket( - size_t /*bytes*/, + QuicByteCount /*bytes*/, QuicPacketSequenceNumber /*sequence_number*/, QuicTime /*timestamp*/, bool /*recovered*/) { // Nothing to do for this simple implementation. } -void FixRateReceiver::SetBitrate(int bytes_per_second) { - bitrate_in_bytes_per_second_ = bytes_per_second; -} - } // namespace net diff --git a/net/quic/congestion_control/fix_rate_receiver.h b/net/quic/congestion_control/fix_rate_receiver.h index d550f13..690f138 100644 --- a/net/quic/congestion_control/fix_rate_receiver.h +++ b/net/quic/congestion_control/fix_rate_receiver.h @@ -11,9 +11,14 @@ #include "base/compiler_specific.h" #include "net/base/net_export.h" #include "net/quic/congestion_control/receive_algorithm_interface.h" +#include "net/quic/quic_bandwidth.h" namespace net { +namespace test { +class FixRateReceiverPeer; +} // namespace test + class NET_EXPORT_PRIVATE FixRateReceiver : public ReceiveAlgorithmInterface { public: FixRateReceiver(); @@ -23,17 +28,17 @@ class NET_EXPORT_PRIVATE FixRateReceiver : public ReceiveAlgorithmInterface { QuicCongestionFeedbackFrame* feedback) OVERRIDE; // Implements ReceiveAlgorithmInterface. - virtual void RecordIncomingPacket(size_t bytes, + virtual void RecordIncomingPacket(QuicByteCount bytes, QuicPacketSequenceNumber sequence_number, QuicTime timestamp, bool recovered) OVERRIDE; + private: + friend class test::FixRateReceiverPeer; - void SetBitrate(int bytes_per_second); // Used for testing only. + QuicBandwidth configured_rate_; - private: - int bitrate_in_bytes_per_second_; + DISALLOW_COPY_AND_ASSIGN(FixRateReceiver); }; } // namespace net - #endif // NET_QUIC_CONGESTION_CONTROL_FIX_RATE_RECEIVER_H_ diff --git a/net/quic/congestion_control/fix_rate_sender.cc b/net/quic/congestion_control/fix_rate_sender.cc index 758a6c1..2738526 100644 --- a/net/quic/congestion_control/fix_rate_sender.cc +++ b/net/quic/congestion_control/fix_rate_sender.cc @@ -7,7 +7,6 @@ #include <math.h> #include "base/logging.h" -#include "base/time.h" #include "net/quic/quic_protocol.h" namespace { @@ -18,10 +17,10 @@ namespace { namespace net { FixRateSender::FixRateSender(const QuicClock* clock) - : bitrate_in_bytes_per_s_(kInitialBitrate), - fix_rate_leaky_bucket_(clock, kInitialBitrate), - paced_sender_(clock, kInitialBitrate), - bytes_in_flight_(0) { + : bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)), + fix_rate_leaky_bucket_(clock, bitrate_), + paced_sender_(clock, bitrate_), + data_in_flight_(0) { DLOG(INFO) << "FixRateSender"; } @@ -31,19 +30,18 @@ void FixRateSender::OnIncomingQuicCongestionFeedbackFrame( DCHECK(feedback.type == kFixRate) << "Invalid incoming CongestionFeedbackType:" << feedback.type; if (feedback.type == kFixRate) { - bitrate_in_bytes_per_s_ = - feedback.fix_rate.bitrate_in_bytes_per_second; - fix_rate_leaky_bucket_.SetDrainingRate(bitrate_in_bytes_per_s_); - paced_sender_.UpdateBandwidthEstimate(bitrate_in_bytes_per_s_); + bitrate_ = feedback.fix_rate.bitrate; + fix_rate_leaky_bucket_.SetDrainingRate(bitrate_); + paced_sender_.UpdateBandwidthEstimate(bitrate_); } // Silently ignore invalid messages in release mode. } void FixRateSender::OnIncomingAck( QuicPacketSequenceNumber /*acked_sequence_number*/, - size_t bytes_acked, + QuicByteCount bytes_acked, QuicTime::Delta /*rtt*/) { - bytes_in_flight_ -= bytes_acked; + data_in_flight_ -= bytes_acked; } void FixRateSender::OnIncomingLoss(int /*number_of_lost_packets*/) { @@ -51,23 +49,22 @@ void FixRateSender::OnIncomingLoss(int /*number_of_lost_packets*/) { } void FixRateSender::SentPacket(QuicPacketSequenceNumber /*sequence_number*/, - size_t bytes, + QuicByteCount bytes, bool is_retransmission) { fix_rate_leaky_bucket_.Add(bytes); paced_sender_.SentPacket(bytes); if (!is_retransmission) { - bytes_in_flight_ += bytes; + data_in_flight_ += bytes; } } QuicTime::Delta FixRateSender::TimeUntilSend(bool /*is_retransmission*/) { if (CongestionWindow() > fix_rate_leaky_bucket_.BytesPending()) { - if (CongestionWindow() <= bytes_in_flight_) { + if (CongestionWindow() <= data_in_flight_) { // We need an ack before we send more. return QuicTime::Delta::Infinite(); } - QuicTime::Delta zero_time(QuicTime::Delta::Zero()); - return paced_sender_.TimeUntilSend(zero_time); + return paced_sender_.TimeUntilSend(QuicTime::Delta::Zero()); } QuicTime::Delta time_remaining = fix_rate_leaky_bucket_.TimeRemaining(); if (time_remaining.IsZero()) { @@ -77,24 +74,25 @@ QuicTime::Delta FixRateSender::TimeUntilSend(bool /*is_retransmission*/) { return paced_sender_.TimeUntilSend(time_remaining); } -size_t FixRateSender::CongestionWindow() { - size_t window_size = bitrate_in_bytes_per_s_ * kWindowSizeUs / - base::Time::kMicrosecondsPerSecond; +QuicByteCount FixRateSender::CongestionWindow() { + QuicByteCount window_size_bytes = bitrate_.ToBytesPerPeriod( + QuicTime::Delta::FromMicroseconds(kWindowSizeUs)); // Make sure window size is not less than a packet. - return std::max(kMaxPacketSize, window_size); + return std::max(kMaxPacketSize, window_size_bytes); } -size_t FixRateSender::AvailableCongestionWindow() { - size_t congestion_window = CongestionWindow(); - if (bytes_in_flight_ >= congestion_window) { +QuicByteCount FixRateSender::AvailableCongestionWindow() { + QuicByteCount congestion_window = CongestionWindow(); + if (data_in_flight_ >= congestion_window) { return 0; } - size_t available_congestion_window = congestion_window - bytes_in_flight_; + QuicByteCount available_congestion_window = congestion_window - + data_in_flight_; return paced_sender_.AvailableWindow(available_congestion_window); } -int FixRateSender::BandwidthEstimate() { - return bitrate_in_bytes_per_s_; +QuicBandwidth FixRateSender::BandwidthEstimate() { + return bitrate_; } } // namespace net diff --git a/net/quic/congestion_control/fix_rate_sender.h b/net/quic/congestion_control/fix_rate_sender.h index 1c91fd0..4e864b7 100644 --- a/net/quic/congestion_control/fix_rate_sender.h +++ b/net/quic/congestion_control/fix_rate_sender.h @@ -18,6 +18,10 @@ namespace net { +namespace test { +class FixRateSenderPeer; +} // namespace test + class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { public: explicit FixRateSender(const QuicClock* clock); @@ -27,24 +31,28 @@ class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { const QuicCongestionFeedbackFrame& feedback, const SentPacketsMap& sent_packets) OVERRIDE; virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, - size_t acked_bytes, + QuicByteCount acked_bytes, QuicTime::Delta rtt) OVERRIDE; virtual void OnIncomingLoss(int number_of_lost_packets) OVERRIDE; virtual void SentPacket(QuicPacketSequenceNumber equence_number, - size_t bytes, + QuicByteCount bytes, bool is_retransmission) OVERRIDE; virtual QuicTime::Delta TimeUntilSend(bool is_retransmission) OVERRIDE; - virtual size_t AvailableCongestionWindow() OVERRIDE; - virtual int BandwidthEstimate() OVERRIDE; + virtual QuicBandwidth BandwidthEstimate() OVERRIDE; // End implementation of SendAlgorithmInterface. private: - size_t CongestionWindow(); + friend class test::FixRateSenderPeer; + + QuicByteCount AvailableCongestionWindow(); + QuicByteCount CongestionWindow(); - uint32 bitrate_in_bytes_per_s_; + QuicBandwidth bitrate_; LeakyBucket fix_rate_leaky_bucket_; PacedSender paced_sender_; - size_t bytes_in_flight_; + QuicByteCount data_in_flight_; + + DISALLOW_COPY_AND_ASSIGN(FixRateSender); }; } // namespace net diff --git a/net/quic/congestion_control/fix_rate_test.cc b/net/quic/congestion_control/fix_rate_test.cc index 33a739f..2c0770a 100644 --- a/net/quic/congestion_control/fix_rate_test.cc +++ b/net/quic/congestion_control/fix_rate_test.cc @@ -15,44 +15,62 @@ 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() + : FixRateReceiver() { + } + void SetBitrate(QuicBandwidth fix_rate) { + FixRateReceiver::configured_rate_ = fix_rate; + } +}; + class FixRateTest : public ::testing::Test { protected: FixRateTest() - : rtt_(QuicTime::Delta::FromMilliseconds(30)) { - } - void SetUp() { - sender_.reset(new FixRateSender(&clock_)); - receiver_.reset(new FixRateReceiver()); + : rtt_(QuicTime::Delta::FromMilliseconds(30)), + sender_(new FixRateSenderPeer(&clock_)), + receiver_(new FixRateReceiverPeer()) { // Make sure clock does not start at 0. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2)); } const QuicTime::Delta rtt_; MockClock clock_; SendAlgorithmInterface::SentPacketsMap not_used_; - scoped_ptr<FixRateSender> sender_; - scoped_ptr<FixRateReceiver> receiver_; + scoped_ptr<FixRateSenderPeer> sender_; + scoped_ptr<FixRateReceiverPeer> receiver_; }; TEST_F(FixRateTest, ReceiverAPI) { QuicCongestionFeedbackFrame feedback; QuicTime timestamp(QuicTime::Zero()); - receiver_->SetBitrate(300000); // Bytes per second. + receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(300)); receiver_->RecordIncomingPacket(1, 1, timestamp, false); ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); EXPECT_EQ(kFixRate, feedback.type); - EXPECT_EQ(300000u, feedback.fix_rate.bitrate_in_bytes_per_second); + EXPECT_EQ(300000u, feedback.fix_rate.bitrate.ToBytesPerSecond()); } TEST_F(FixRateTest, SenderAPI) { QuicCongestionFeedbackFrame feedback; feedback.type = kFixRate; - feedback.fix_rate.bitrate_in_bytes_per_second = 300000; + feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(300); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_); - EXPECT_EQ(300000, sender_->BandwidthEstimate()); + EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond()); EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); - EXPECT_EQ(kMaxPacketSize * 2, sender_->AvailableCongestionWindow()); + EXPECT_EQ(kMaxPacketSize * 2, + sender_->AvailableCongestionWindow()); sender_->SentPacket(1, kMaxPacketSize, false); - EXPECT_EQ(3000u - kMaxPacketSize, sender_->AvailableCongestionWindow()); + EXPECT_EQ(3000u - kMaxPacketSize, + sender_->AvailableCongestionWindow()); EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); sender_->SentPacket(2, kMaxPacketSize, false); sender_->SentPacket(3, 600, false); @@ -69,18 +87,19 @@ TEST_F(FixRateTest, SenderAPI) { } TEST_F(FixRateTest, FixRatePacing) { - const int64 packet_size = 1200; - const int64 bit_rate = 240000; + const QuicByteCount packet_size = 1200; + const QuicBandwidth bitrate = QuicBandwidth::FromKBytesPerSecond(240); const int64 num_packets = 200; QuicCongestionFeedbackFrame feedback; - receiver_->SetBitrate(240000); // Bytes per second. + receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(240)); ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_); QuicTime acc_advance_time(QuicTime::Zero()); QuicPacketSequenceNumber sequence_number = 0; - for (int i = 0; i < num_packets; i += 2) { + for (int64 i = 0; i < num_packets; i += 2) { EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); - EXPECT_EQ(kMaxPacketSize * 2, sender_->AvailableCongestionWindow()); + 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); @@ -90,8 +109,8 @@ TEST_F(FixRateTest, FixRatePacing) { sender_->OnIncomingAck(sequence_number - 2, packet_size, rtt_); acc_advance_time = acc_advance_time.Add(advance_time); } - EXPECT_EQ(num_packets * packet_size * 1000000 / bit_rate, - acc_advance_time.ToMicroseconds()); + EXPECT_EQ(num_packets * packet_size * 1000000 / bitrate.ToBytesPerSecond(), + static_cast<uint64>(acc_advance_time.ToMicroseconds())); } } // namespace test diff --git a/net/quic/congestion_control/hybrid_slow_start.h b/net/quic/congestion_control/hybrid_slow_start.h index cb3345c..cee9c73 100644 --- a/net/quic/congestion_control/hybrid_slow_start.h +++ b/net/quic/congestion_control/hybrid_slow_start.h @@ -56,6 +56,8 @@ class NET_EXPORT_PRIVATE HybridSlowStart { QuicTime last_time_; // Last time when the ACK spacing was close. uint8 sample_count_; // Number of samples to decide current RTT. QuicTime::Delta current_rtt_; // The minimum rtt of current round. + + DISALLOW_COPY_AND_ASSIGN(HybridSlowStart); }; } // namespace net diff --git a/net/quic/congestion_control/leaky_bucket.cc b/net/quic/congestion_control/leaky_bucket.cc index 60ec7a6..0012ae8 100644 --- a/net/quic/congestion_control/leaky_bucket.cc +++ b/net/quic/congestion_control/leaky_bucket.cc @@ -8,19 +8,19 @@ namespace net { -LeakyBucket::LeakyBucket(const QuicClock* clock, int bytes_per_second) +LeakyBucket::LeakyBucket(const QuicClock* clock, QuicBandwidth draining_rate) : clock_(clock), bytes_(0), time_last_updated_(QuicTime::Zero()), - draining_rate_bytes_per_s_(bytes_per_second) { + draining_rate_(draining_rate) { } -void LeakyBucket::SetDrainingRate(int bytes_per_second) { +void LeakyBucket::SetDrainingRate(QuicBandwidth draining_rate) { Update(); - draining_rate_bytes_per_s_ = bytes_per_second; + draining_rate_ = draining_rate; } -void LeakyBucket::Add(size_t bytes) { +void LeakyBucket::Add(QuicByteCount bytes) { Update(); bytes_ += bytes; } @@ -29,25 +29,24 @@ QuicTime::Delta LeakyBucket::TimeRemaining() { Update(); return QuicTime::Delta::FromMicroseconds( (bytes_ * base::Time::kMicrosecondsPerSecond) / - draining_rate_bytes_per_s_); + draining_rate_.ToBytesPerSecond()); } -size_t LeakyBucket::BytesPending() { +QuicByteCount LeakyBucket::BytesPending() { Update(); return bytes_; } void LeakyBucket::Update() { - QuicTime::Delta elapsed_time = clock_->Now().Subtract(time_last_updated_); - size_t bytes_cleared = - (elapsed_time.ToMicroseconds() * draining_rate_bytes_per_s_) / - base::Time::kMicrosecondsPerSecond; + QuicTime now = clock_->Now(); + QuicTime::Delta elapsed_time = now.Subtract(time_last_updated_); + QuicByteCount bytes_cleared = draining_rate_.ToBytesPerPeriod(elapsed_time); if (bytes_cleared >= bytes_) { bytes_ = 0; } else { bytes_ -= bytes_cleared; } - time_last_updated_ = clock_->Now(); + time_last_updated_ = now; } } // namespace net diff --git a/net/quic/congestion_control/leaky_bucket.h b/net/quic/congestion_control/leaky_bucket.h index 0ea8bc7..be138982 100644 --- a/net/quic/congestion_control/leaky_bucket.h +++ b/net/quic/congestion_control/leaky_bucket.h @@ -12,7 +12,9 @@ #include "base/basictypes.h" #include "net/base/net_export.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" namespace net { @@ -20,27 +22,29 @@ namespace net { class NET_EXPORT_PRIVATE LeakyBucket { public: // clock is not owned by this class. - LeakyBucket(const QuicClock* clock, int bytes_per_second); + LeakyBucket(const QuicClock* clock, QuicBandwidth draining_rate); // Set the rate at which the bytes leave the buffer. - void SetDrainingRate(int bytes_per_second); + void SetDrainingRate(QuicBandwidth draining_rate); // Add data to the buffer. - void Add(size_t bytes); + void Add(QuicByteCount bytes); // Time until the buffer is empty in us. QuicTime::Delta TimeRemaining(); // Number of bytes in the buffer. - size_t BytesPending(); + QuicByteCount BytesPending(); private: void Update(); const QuicClock* clock_; - size_t bytes_; + QuicByteCount bytes_; QuicTime time_last_updated_; - int draining_rate_bytes_per_s_; + QuicBandwidth draining_rate_; + + DISALLOW_COPY_AND_ASSIGN(LeakyBucket); }; } // namespace net diff --git a/net/quic/congestion_control/leaky_bucket_test.cc b/net/quic/congestion_control/leaky_bucket_test.cc index f0e6ec0..a510200 100644 --- a/net/quic/congestion_control/leaky_bucket_test.cc +++ b/net/quic/congestion_control/leaky_bucket_test.cc @@ -14,15 +14,15 @@ namespace test { class LeakyBucketTest : public ::testing::Test { protected: void SetUp() { - leaky_bucket_.reset(new LeakyBucket(&clock_, 0)); + leaky_bucket_.reset(new LeakyBucket(&clock_, QuicBandwidth::Zero())); } MockClock clock_; scoped_ptr<LeakyBucket> leaky_bucket_; }; TEST_F(LeakyBucketTest, Basic) { - int bytes_per_second = 200000; - leaky_bucket_->SetDrainingRate(bytes_per_second); + QuicBandwidth draining_rate = QuicBandwidth::FromBytesPerSecond(200000); + leaky_bucket_->SetDrainingRate(draining_rate); leaky_bucket_->Add(2000); EXPECT_EQ(2000u, leaky_bucket_->BytesPending()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), @@ -54,8 +54,8 @@ TEST_F(LeakyBucketTest, Basic) { } TEST_F(LeakyBucketTest, ChangeDrainRate) { - int bytes_per_second = 200000; - leaky_bucket_->SetDrainingRate(bytes_per_second); + QuicBandwidth draining_rate = QuicBandwidth::FromBytesPerSecond(200000); + leaky_bucket_->SetDrainingRate(draining_rate); leaky_bucket_->Add(2000); EXPECT_EQ(2000u, leaky_bucket_->BytesPending()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), @@ -64,8 +64,8 @@ TEST_F(LeakyBucketTest, ChangeDrainRate) { EXPECT_EQ(1000u, leaky_bucket_->BytesPending()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(5), leaky_bucket_->TimeRemaining()); - bytes_per_second = 100000; // Cut drain rate in half. - leaky_bucket_->SetDrainingRate(bytes_per_second); + draining_rate = draining_rate.Scale(0.5f); // Cut drain rate in half. + leaky_bucket_->SetDrainingRate(draining_rate); EXPECT_EQ(1000u, leaky_bucket_->BytesPending()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), leaky_bucket_->TimeRemaining()); diff --git a/net/quic/congestion_control/paced_sender.cc b/net/quic/congestion_control/paced_sender.cc index 38419d0..f44aa33 100644 --- a/net/quic/congestion_control/paced_sender.cc +++ b/net/quic/congestion_control/paced_sender.cc @@ -4,28 +4,27 @@ #include "net/quic/congestion_control/paced_sender.h" -#include "base/time.h" #include "net/quic/quic_protocol.h" namespace net { // To prevent too aggressive pacing we allow the following packet burst size. -const size_t kMinPacketBurstSize = 2; +const int64 kMinPacketBurstSize = 2; // Max estimated time between calls to TimeUntilSend and // AvailableCongestionWindow. const int64 kMaxSchedulingDelayUs = 2000; -PacedSender::PacedSender(const QuicClock* clock, int bytes_per_s) - : leaky_bucket_(clock, bytes_per_s), - pace_in_bytes_per_s_(bytes_per_s) { +PacedSender::PacedSender(const QuicClock* clock, QuicBandwidth estimate) + : leaky_bucket_(clock, estimate), + pace_(estimate) { } -void PacedSender::UpdateBandwidthEstimate(int bytes_per_s) { - leaky_bucket_.SetDrainingRate(bytes_per_s); - pace_in_bytes_per_s_ = bytes_per_s; +void PacedSender::UpdateBandwidthEstimate(QuicBandwidth estimate) { + leaky_bucket_.SetDrainingRate(estimate); + pace_ = estimate; } -void PacedSender::SentPacket(size_t bytes) { +void PacedSender::SentPacket(QuicByteCount bytes) { leaky_bucket_.Add(bytes); } @@ -34,9 +33,9 @@ QuicTime::Delta PacedSender::TimeUntilSend(QuicTime::Delta time_until_send) { return time_until_send; } // Pace the data. - size_t pacing_window = kMaxSchedulingDelayUs * pace_in_bytes_per_s_ / - base::Time::kMicrosecondsPerSecond; - size_t min_window_size = kMinPacketBurstSize * kMaxPacketSize; + QuicByteCount pacing_window = pace_.ToBytesPerPeriod( + QuicTime::Delta::FromMicroseconds(kMaxSchedulingDelayUs)); + QuicByteCount min_window_size = kMinPacketBurstSize * kMaxPacketSize; pacing_window = std::max(pacing_window, min_window_size); if (pacing_window > leaky_bucket_.BytesPending()) { @@ -46,10 +45,11 @@ QuicTime::Delta PacedSender::TimeUntilSend(QuicTime::Delta time_until_send) { return leaky_bucket_.TimeRemaining(); } -size_t PacedSender::AvailableWindow(size_t available_congestion_window) { - size_t accuracy_window = (kMaxSchedulingDelayUs * pace_in_bytes_per_s_) / - base::Time::kMicrosecondsPerSecond; - size_t min_burst_window = kMinPacketBurstSize * kMaxPacketSize; +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; @@ -61,7 +61,7 @@ size_t PacedSender::AvailableWindow(size_t available_congestion_window) { // 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); - size_t bytes_pending = leaky_bucket_.BytesPending(); + QuicByteCount bytes_pending = leaky_bucket_.BytesPending(); if (bytes_pending > available_congestion_window) { return 0; } diff --git a/net/quic/congestion_control/paced_sender.h b/net/quic/congestion_control/paced_sender.h index 1b6f81d..af3adaf 100644 --- a/net/quic/congestion_control/paced_sender.h +++ b/net/quic/congestion_control/paced_sender.h @@ -10,6 +10,7 @@ #include "base/basictypes.h" #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" @@ -17,13 +18,13 @@ namespace net { class NET_EXPORT_PRIVATE PacedSender { public: - PacedSender(const QuicClock* clock, int bandwidth_estimate_bytes_per_s); + PacedSender(const QuicClock* clock, QuicBandwidth bandwidth_estimate); // The estimated bandidth from the congestion algorithm changed. - void UpdateBandwidthEstimate(int bytes_per_s); + void UpdateBandwidthEstimate(QuicBandwidth bandwidth_estimate); // A packet of size bytes was sent. - void SentPacket(size_t bytes); + void SentPacket(QuicByteCount bytes); // Return time until we can send based on the pacing. QuicTime::Delta TimeUntilSend(QuicTime::Delta time_until_send); @@ -31,12 +32,14 @@ class NET_EXPORT_PRIVATE PacedSender { // 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. - size_t AvailableWindow(size_t available_congestion_window); + QuicByteCount AvailableWindow(QuicByteCount available_congestion_window); private: // Helper object to track the rate data can leave the buffer for pacing. LeakyBucket leaky_bucket_; - int pace_in_bytes_per_s_; + QuicBandwidth pace_; + + DISALLOW_COPY_AND_ASSIGN(PacedSender); }; } // namespace net diff --git a/net/quic/congestion_control/paced_sender_test.cc b/net/quic/congestion_control/paced_sender_test.cc index e93261b..d2432aa 100644 --- a/net/quic/congestion_control/paced_sender_test.cc +++ b/net/quic/congestion_control/paced_sender_test.cc @@ -13,13 +13,14 @@ namespace net { namespace test { -const int kHundredKBytesPerS = 100000; +const int kHundredKBytesPerS = 100; class PacedSenderTest : public ::testing::Test { protected: PacedSenderTest() : zero_time_(QuicTime::Delta::Zero()), - paced_sender_(new PacedSender(&clock_, kHundredKBytesPerS)) { + paced_sender_(new PacedSender(&clock_, + QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS))) { } const QuicTime::Delta zero_time_; @@ -28,7 +29,8 @@ class PacedSenderTest : public ::testing::Test { }; TEST_F(PacedSenderTest, Basic) { - paced_sender_->UpdateBandwidthEstimate(kHundredKBytesPerS * 10); + paced_sender_->UpdateBandwidthEstimate( + QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS * 10)); EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero()); EXPECT_EQ(kMaxPacketSize * 2, paced_sender_->AvailableWindow(kMaxPacketSize * 4)); @@ -40,12 +42,13 @@ TEST_F(PacedSenderTest, Basic) { EXPECT_EQ(0u, paced_sender_->AvailableWindow(kMaxPacketSize * 4)); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24)); EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero()); - EXPECT_EQ(kMaxPacketSize * 2, + EXPECT_EQ(kMaxPacketSize * 2u, paced_sender_->AvailableWindow(kMaxPacketSize * 4)); } TEST_F(PacedSenderTest, LowRate) { - paced_sender_->UpdateBandwidthEstimate(kHundredKBytesPerS); + paced_sender_->UpdateBandwidthEstimate( + QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS)); EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero()); size_t window = paced_sender_->AvailableWindow(kMaxPacketSize * 4); EXPECT_EQ(kMaxPacketSize * 2, window); @@ -62,10 +65,11 @@ TEST_F(PacedSenderTest, LowRate) { } TEST_F(PacedSenderTest, HighRate) { - int bandwidth_estimate = kHundredKBytesPerS * 100; + QuicBandwidth bandwidth_estimate = QuicBandwidth::FromKBytesPerSecond( + kHundredKBytesPerS * 100); paced_sender_->UpdateBandwidthEstimate(bandwidth_estimate); EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero()); - EXPECT_EQ(bandwidth_estimate / 500u, + EXPECT_EQ(static_cast<uint64>(bandwidth_estimate.ToBytesPerSecond() / 500u), paced_sender_->AvailableWindow(kMaxPacketSize * 100)); for (int i = 0; i < 16; ++i) { paced_sender_->SentPacket(kMaxPacketSize); @@ -76,7 +80,7 @@ TEST_F(PacedSenderTest, HighRate) { EXPECT_EQ(2040, paced_sender_->TimeUntilSend(zero_time_).ToMicroseconds()); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(20400)); EXPECT_TRUE(paced_sender_->TimeUntilSend(zero_time_).IsZero()); - EXPECT_EQ(bandwidth_estimate / 500u, + EXPECT_EQ(static_cast<uint64>(bandwidth_estimate.ToBytesPerSecond() / 500u), paced_sender_->AvailableWindow(kMaxPacketSize * 100)); } diff --git a/net/quic/congestion_control/quic_congestion_manager.cc b/net/quic/congestion_control/quic_congestion_manager.cc index 8e300bb..238b5c7 100644 --- a/net/quic/congestion_control/quic_congestion_manager.cc +++ b/net/quic/congestion_control/quic_congestion_manager.cc @@ -4,51 +4,175 @@ #include "net/quic/congestion_control/quic_congestion_manager.h" -#include "net/quic/congestion_control/quic_receipt_metrics_collector.h" -#include "net/quic/congestion_control/quic_send_scheduler.h" +#include <algorithm> +#include <map> + +#include "base/stl_util.h" +#include "net/quic/congestion_control/receive_algorithm_interface.h" +#include "net/quic/congestion_control/send_algorithm_interface.h" + +namespace { +const int kBitrateSmoothingPeriodMs = 3000; +const int kMinBitrateSmoothingPeriodMs = 500; +const int kHistoryPeriodMs = 5000; + +const int kDefaultRetransmissionTimeMs = 500; +const size_t kMaxRetransmissions = 15; + +COMPILE_ASSERT(kHistoryPeriodMs >= kBitrateSmoothingPeriodMs, + history_must_be_longer_or_equal_to_the_smoothing_period); +} // namespace + +using std::map; +using std::min; namespace net { QuicCongestionManager::QuicCongestionManager( const QuicClock* clock, CongestionFeedbackType type) - : collector_(new QuicReceiptMetricsCollector(clock, type)), - scheduler_(new QuicSendScheduler(clock, type)) { + : clock_(clock), + receive_algorithm_(ReceiveAlgorithmInterface::Create(clock, type)), + send_algorithm_(SendAlgorithmInterface::Create(clock, type)) { } QuicCongestionManager::~QuicCongestionManager() { + STLDeleteValues(&packet_history_map_); } void QuicCongestionManager::SentPacket(QuicPacketSequenceNumber sequence_number, - size_t bytes, + QuicByteCount bytes, bool is_retransmission) { - scheduler_->SentPacket(sequence_number, bytes, is_retransmission); + DCHECK(!ContainsKey(pending_packets_, sequence_number)); + send_algorithm_->SentPacket(sequence_number, bytes, is_retransmission); + + packet_history_map_[sequence_number] = + new class SendAlgorithmInterface::SentPacket(bytes, clock_->Now()); + pending_packets_[sequence_number] = bytes; + CleanupPacketHistory(); + DLOG(INFO) << "Sent sequence number:" << sequence_number; } void QuicCongestionManager::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& frame) { - scheduler_->OnIncomingQuicCongestionFeedbackFrame(frame); + send_algorithm_->OnIncomingQuicCongestionFeedbackFrame(frame, + packet_history_map_); } void QuicCongestionManager::OnIncomingAckFrame(const QuicAckFrame& frame) { - scheduler_->OnIncomingAckFrame(frame); + // 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()); + } + // We want to. + // * Get all packets lower(including) than largest_observed + // from pending_packets_. + // * Remove all missing packets. + // * Send each ACK in the list to send_algorithm_. + PendingPacketsMap::iterator it, it_upper; + it = pending_packets_.begin(); + it_upper = pending_packets_.upper_bound(frame.received_info.largest_observed); + + while (it != it_upper) { + QuicPacketSequenceNumber sequence_number = it->first; + if (!frame.received_info.IsAwaitingPacket(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 { + ++it; + } + } } QuicTime::Delta QuicCongestionManager::TimeUntilSend(bool is_retransmission) { - return scheduler_->TimeUntilSend(is_retransmission); + return send_algorithm_->TimeUntilSend(is_retransmission); } bool QuicCongestionManager::GenerateCongestionFeedback( QuicCongestionFeedbackFrame* feedback) { - return collector_->GenerateCongestionFeedback(feedback); + return receive_algorithm_->GenerateCongestionFeedback(feedback); } void QuicCongestionManager::RecordIncomingPacket( - size_t bytes, + QuicByteCount bytes, QuicPacketSequenceNumber sequence_number, QuicTime timestamp, bool revived) { - collector_->RecordIncomingPacket(bytes, sequence_number, timestamp, revived); + receive_algorithm_->RecordIncomingPacket(bytes, sequence_number, timestamp, + revived); +} + +// static +const QuicTime::Delta QuicCongestionManager::DefaultRetransmissionTime() { + return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); +} + +// static +const QuicTime::Delta QuicCongestionManager::GetRetransmissionDelay( + size_t number_retransmissions) { + return QuicTime::Delta::FromMilliseconds( + kDefaultRetransmissionTimeMs * + (1 << min<size_t>(number_retransmissions, kMaxRetransmissions))); +} + +QuicBandwidth QuicCongestionManager::SentBandwidth() 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. + SendAlgorithmInterface::SentPacketsMap::const_reverse_iterator history_rit = + packet_history_map_.rbegin(); + + 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()); + if (diff > kBitrateSmoothingPeriod) { + break; + } + sum_bytes_sent += history_rit->second->BytesSent(); + max_diff = diff; + } + if (max_diff < kMinBitrateSmoothingPeriod) { + // No estimate. + return QuicBandwidth::Zero(); + } + return QuicBandwidth::FromBytesAndTimeDelta(sum_bytes_sent, max_diff); +} + +QuicBandwidth QuicCongestionManager::BandwidthEstimate() { + return send_algorithm_->BandwidthEstimate(); +} + +void QuicCongestionManager::CleanupPacketHistory() { + const QuicTime::Delta kHistoryPeriod = + QuicTime::Delta::FromMilliseconds(kHistoryPeriodMs); + QuicTime Now = clock_->Now(); + + 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) { + 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(); + } } } // namespace net diff --git a/net/quic/congestion_control/quic_congestion_manager.h b/net/quic/congestion_control/quic_congestion_manager.h index 9402213..09a9c9c 100644 --- a/net/quic/congestion_control/quic_congestion_manager.h +++ b/net/quic/congestion_control/quic_congestion_manager.h @@ -3,8 +3,8 @@ // found in the LICENSE file. // // This is the interface from the QuicConnection into the QUIC -// congestion control code. It wraps the QuicSendScheduler and -// QuicReceiptMetricsCollector and provides a single interface +// congestion control code. It wraps the SendAlgorithmInterface and +// ReceiveAlgorithmInterface and provides a single interface // for consumers. #ifndef NET_QUIC_CONGESTION_CONTROL_QUIC_CONGESTION_MANAGER_H_ @@ -12,17 +12,19 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +#include "net/quic/congestion_control/send_algorithm_interface.h" +#include "net/quic/quic_bandwidth.h" #include "net/quic/quic_protocol.h" namespace net { namespace test { class QuicConnectionPeer; +class QuicCongestionManagerPeer; } // namespace test class QuicClock; -class QuicReceiptMetricsCollector; -class QuicSendScheduler; +class ReceiveAlgorithmInterface; class QuicCongestionManager { public: @@ -40,7 +42,7 @@ class QuicCongestionManager { // 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, - size_t bytes, + QuicByteCount bytes, bool is_retransmission); // Calculate the time until we can send the next packet to the wire. @@ -63,16 +65,34 @@ class QuicCongestionManager { // timestamp: the arrival time of the packet. // revived: true if the packet was lost and then recovered with help of a // FEC packet. - virtual void RecordIncomingPacket(size_t bytes, + virtual void RecordIncomingPacket(QuicByteCount bytes, QuicPacketSequenceNumber sequence_number, QuicTime timestamp, bool revived); + const QuicTime::Delta DefaultRetransmissionTime(); + + const QuicTime::Delta GetRetransmissionDelay( + size_t number_retransmissions); + private: friend class test::QuicConnectionPeer; - - scoped_ptr<QuicReceiptMetricsCollector> collector_; - scoped_ptr<QuicSendScheduler> scheduler_; + 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; + // TODO(pwestin): Currently only used for testing. How do we surface this? + QuicBandwidth BandwidthEstimate(); + void CleanupPacketHistory(); + + const QuicClock* clock_; + scoped_ptr<ReceiveAlgorithmInterface> receive_algorithm_; + scoped_ptr<SendAlgorithmInterface> send_algorithm_; + SendAlgorithmInterface::SentPacketsMap packet_history_map_; + PendingPacketsMap pending_packets_; + + DISALLOW_COPY_AND_ASSIGN(QuicCongestionManager); }; } // namespace net diff --git a/net/quic/congestion_control/quic_receipt_metrics_collector.cc b/net/quic/congestion_control/quic_receipt_metrics_collector.cc deleted file mode 100644 index 7f3c076..0000000 --- a/net/quic/congestion_control/quic_receipt_metrics_collector.cc +++ /dev/null @@ -1,34 +0,0 @@ -// 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/congestion_control/quic_receipt_metrics_collector.h" - -#include "net/quic/congestion_control/receive_algorithm_interface.h" - -namespace net { - -QuicReceiptMetricsCollector::QuicReceiptMetricsCollector( - const QuicClock* clock, - CongestionFeedbackType type) - : receive_algorithm_(ReceiveAlgorithmInterface::Create(clock, type)) { -} - -QuicReceiptMetricsCollector::~QuicReceiptMetricsCollector() { -} - -bool QuicReceiptMetricsCollector::GenerateCongestionFeedback( - QuicCongestionFeedbackFrame* feedback) { - return receive_algorithm_->GenerateCongestionFeedback(feedback); -} - -void QuicReceiptMetricsCollector::RecordIncomingPacket( - size_t bytes, - QuicPacketSequenceNumber sequence_number, - QuicTime timestamp, - bool revived) { - receive_algorithm_->RecordIncomingPacket(bytes, sequence_number, timestamp, - revived); -} - -} // namespace net diff --git a/net/quic/congestion_control/quic_receipt_metrics_collector.h b/net/quic/congestion_control/quic_receipt_metrics_collector.h deleted file mode 100644 index d985cff..0000000 --- a/net/quic/congestion_control/quic_receipt_metrics_collector.h +++ /dev/null @@ -1,57 +0,0 @@ -// 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. -// -// This is the base class for QUIC receive side congestion control. -// This class will provide the QuicCongestionFeedbackFrame objects for outgoing -// packets if needed. -// The acctual receive side algorithm is implemented via the -// ReceiveAlgorithmInterface. - -#ifndef NET_QUIC_CONGESTION_CONTROL_QUIC_RECEIPT_METRICS_COLLECTOR_H_ -#define NET_QUIC_CONGESTION_CONTROL_QUIC_RECEIPT_METRICS_COLLECTOR_H_ - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "net/base/net_export.h" -#include "net/quic/congestion_control/receive_algorithm_interface.h" -#include "net/quic/quic_clock.h" -#include "net/quic/quic_protocol.h" - -namespace net { - -class NET_EXPORT_PRIVATE QuicReceiptMetricsCollector { - public: - QuicReceiptMetricsCollector(const QuicClock* clock, - CongestionFeedbackType congestion_type); - - virtual ~QuicReceiptMetricsCollector(); - - // Should be called before sending an ACK packet, to decide if we need - // to attach a QuicCongestionFeedbackFrame block. - // Returns false if no QuicCongestionFeedbackFrame block is needed. - // Otherwise fills in feedback and returns true. - virtual bool GenerateCongestionFeedback( - QuicCongestionFeedbackFrame* feedback); - - // Should be called for each incoming packet. - // bytes: the packet size in bytes including IP headers. - // sequence_number: the unique sequence number from the QUIC packet header. - // timestamp: the arrival time of the packet. - // revived: true if the packet was lost and then recovered with help of a - // FEC packet. - virtual void RecordIncomingPacket(size_t bytes, - QuicPacketSequenceNumber sequence_number, - QuicTime timestamp, - bool revived); - - // TODO(pwestin) Keep track of the number of FEC recovered packets. - // Needed by all congestion control algorithms. - - private: - scoped_ptr<ReceiveAlgorithmInterface> receive_algorithm_; -}; - -} // namespace net - -#endif // NET_QUIC_CONGESTION_CONTROL_QUIC_RECEIPT_METRICS_COLLECTOR_H_ diff --git a/net/quic/congestion_control/quic_receipt_metrics_collector_test.cc b/net/quic/congestion_control/quic_receipt_metrics_collector_test.cc deleted file mode 100644 index 891fcba..0000000 --- a/net/quic/congestion_control/quic_receipt_metrics_collector_test.cc +++ /dev/null @@ -1,34 +0,0 @@ -// 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 "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "net/quic/congestion_control/quic_receipt_metrics_collector.h" -#include "net/quic/test_tools/mock_clock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { -namespace test { - -class QuicReceiptMetricsCollectorTest : public ::testing::Test { - protected: - void SetUpCongestionType(CongestionFeedbackType congestion_type) { - receiver_.reset(new QuicReceiptMetricsCollector(&clock_, congestion_type)); - } - - MockClock clock_; - scoped_ptr<QuicReceiptMetricsCollector> receiver_; -}; - -TEST_F(QuicReceiptMetricsCollectorTest, FixedRateReceiverAPI) { - SetUpCongestionType(kFixRate); - QuicCongestionFeedbackFrame feedback; - QuicTime timestamp(QuicTime::Zero()); - receiver_->RecordIncomingPacket(1, 1, timestamp, false); - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - EXPECT_EQ(kFixRate, feedback.type); -} - -} // namespace test -} // namespace net diff --git a/net/quic/congestion_control/quic_send_scheduler.cc b/net/quic/congestion_control/quic_send_scheduler.cc deleted file mode 100644 index 8b9acf5..0000000 --- a/net/quic/congestion_control/quic_send_scheduler.cc +++ /dev/null @@ -1,176 +0,0 @@ -// 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/congestion_control/quic_send_scheduler.h" - -#include <algorithm> -#include <cmath> -#include <map> - -#include "base/stl_util.h" -#include "base/time.h" -#include "net/quic/congestion_control/send_algorithm_interface.h" -//#include "util/gtl/map-util.h" - -namespace { -const int kBitrateSmoothingPeriodMs = 3000; -const int kMinBitrateSmoothingPeriodMs = 500; -const int kHistoryPeriodMs = 5000; - -COMPILE_ASSERT(kHistoryPeriodMs >= kBitrateSmoothingPeriodMs, - history_must_be_longer_or_equal_to_the_smoothing_period); -} - -using std::map; -using std::max; - -namespace net { - -const int64 kNumMicrosPerSecond = base::Time::kMicrosecondsPerSecond; - -QuicSendScheduler::QuicSendScheduler( - const QuicClock* clock, - CongestionFeedbackType type) - : clock_(clock), - current_estimated_bandwidth_(0), - max_estimated_bandwidth_(-1), - last_sent_packet_(QuicTime::FromMicroseconds(0)), - send_algorithm_(SendAlgorithmInterface::Create(clock, type)) { -} - -QuicSendScheduler::~QuicSendScheduler() { - STLDeleteValues(&packet_history_map_); -} - -void QuicSendScheduler::SentPacket(QuicPacketSequenceNumber sequence_number, - size_t bytes, - bool is_retransmission) { - DCHECK(!ContainsKey(pending_packets_, sequence_number)); - send_algorithm_->SentPacket(sequence_number, bytes, is_retransmission); - - packet_history_map_[sequence_number] = - new class SendAlgorithmInterface::SentPacket(bytes, clock_->Now()); - pending_packets_[sequence_number] = bytes; - CleanupPacketHistory(); - DLOG(INFO) << "Sent sequence number:" << sequence_number; -} - -void QuicSendScheduler::OnIncomingQuicCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& congestion_feedback_frame) { - send_algorithm_->OnIncomingQuicCongestionFeedbackFrame( - congestion_feedback_frame, packet_history_map_); -} - -void QuicSendScheduler::CleanupPacketHistory() { - const QuicTime::Delta kHistoryPeriod = - QuicTime::Delta::FromMilliseconds(kHistoryPeriodMs); - QuicTime Now = clock_->Now(); - - 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) { - 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(); - } -} - -void QuicSendScheduler::OnIncomingAckFrame(const QuicAckFrame& ack_frame) { - // 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(ack_frame.received_info.largest_observed); - if (history_it != packet_history_map_.end()) { - rtt = clock_->Now().Subtract(history_it->second->SendTimestamp()); - } - // We want to. - // * Get all packets lower(including) than largest_observed - // from pending_packets_. - // * Remove all missing packets. - // * Send each ACK in the list to send_algorithm_. - PendingPacketsMap::iterator it, it_upper; - it = pending_packets_.begin(); - it_upper = pending_packets_.upper_bound( - ack_frame.received_info.largest_observed); - - while (it != it_upper) { - QuicPacketSequenceNumber sequence_number = it->first; - if (!ack_frame.received_info.IsAwaitingPacket(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 { - ++it; - } - } -} - -QuicTime::Delta QuicSendScheduler::TimeUntilSend(bool is_retransmission) { - return send_algorithm_->TimeUntilSend(is_retransmission); -} - -size_t QuicSendScheduler::AvailableCongestionWindow() { - return send_algorithm_->AvailableCongestionWindow(); -} - -int QuicSendScheduler::BandwidthEstimate() { - int bandwidth_estimate = send_algorithm_->BandwidthEstimate(); - if (bandwidth_estimate == kNoValidEstimate) { - // If we don't have a valid estimate use the send rate. - return SentBandwidth(); - } - return bandwidth_estimate; -} - -// TODO(pwestin): add a timer to make max_estimated_bandwidth_ accurate. -int QuicSendScheduler::SentBandwidth() { - const QuicTime::Delta kBitrateSmoothingPeriod = - QuicTime::Delta::FromMilliseconds(kBitrateSmoothingPeriodMs); - const QuicTime::Delta kMinBitrateSmoothingPeriod = - QuicTime::Delta::FromMilliseconds(kMinBitrateSmoothingPeriodMs); - - QuicTime Now = clock_->Now(); - size_t sum_bytes_sent = 0; - - // Sum packet from new until they are kBitrateSmoothingPeriod old. - SendAlgorithmInterface::SentPacketsMap::reverse_iterator history_rit = - packet_history_map_.rbegin(); - - 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()); - if (diff > kBitrateSmoothingPeriod) { - break; - } - sum_bytes_sent += history_rit->second->BytesSent(); - max_diff = diff; - } - if (max_diff < kMinBitrateSmoothingPeriod) { - // No estimate. - return 0; - } - current_estimated_bandwidth_ = sum_bytes_sent * kNumMicrosPerSecond / - max_diff.ToMicroseconds(); - max_estimated_bandwidth_ = max(max_estimated_bandwidth_, - current_estimated_bandwidth_); - return current_estimated_bandwidth_; -} - -int QuicSendScheduler::PeakSustainedBandwidth() { - // To make sure that we get the latest estimate we call SentBandwidth. - SentBandwidth(); - return max_estimated_bandwidth_; -} - -} // namespace net diff --git a/net/quic/congestion_control/quic_send_scheduler.h b/net/quic/congestion_control/quic_send_scheduler.h deleted file mode 100644 index 53793bd..0000000 --- a/net/quic/congestion_control/quic_send_scheduler.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// The is the base class for QUIC send side congestion control. -// It decides when we can send a QUIC packet to the wire. -// This class handles the basic bookkeeping of sent bitrate and packet loss. -// The actual send side algorithm is implemented via the -// SendAlgorithmInterface. - -#ifndef NET_QUIC_CONGESTION_CONTROL_QUIC_SEND_SCHEDULER_H_ -#define NET_QUIC_CONGESTION_CONTROL_QUIC_SEND_SCHEDULER_H_ - -#include <map> - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "net/base/net_export.h" -#include "net/quic/congestion_control/quic_receipt_metrics_collector.h" -#include "net/quic/congestion_control/send_algorithm_interface.h" -#include "net/quic/quic_clock.h" -#include "net/quic/quic_time.h" - -namespace net { - -class NET_EXPORT_PRIVATE QuicSendScheduler { - public: - // Enable pacing to prevent a large congestion window to be sent all at once, - // when pacing is enabled a large congestion window will be sent in multiple - // bursts of packet(s) instead of one big burst that might introduce packet - // loss. - QuicSendScheduler(const QuicClock* clock, - CongestionFeedbackType congestion_type); - virtual ~QuicSendScheduler(); - - // Called when we have received an ack frame from peer. - virtual void OnIncomingAckFrame(const QuicAckFrame& frame); - - // Called when a congestion feedback frame is received from peer. - virtual void OnIncomingQuicCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& frame); - - // 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, - size_t bytes, - bool is_retransmission); - - // Calculate the time until we can send the next packet to the wire. - // Note 1: When kUnknownWaitTime is returned, there is no need to poll - // 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); - - // Returns the current available congestion window in bytes, the number of - // bytes that can be sent now. - // Note: due to pacing this function might return a smaller value than the - // real available congestion window. This way we hold off the sender to avoid - // queuing in the lower layers in the stack. - size_t AvailableCongestionWindow(); - - int BandwidthEstimate(); // Current estimate, in bytes per second. - - int SentBandwidth(); // Current smooth acctually sent, in bytes per second. - - int PeakSustainedBandwidth(); // In bytes per second. - - private: - typedef std::map<QuicPacketSequenceNumber, size_t> PendingPacketsMap; - - void CleanupPacketHistory(); - - const QuicClock* clock_; - int64 current_estimated_bandwidth_; - int64 max_estimated_bandwidth_; - QuicTime last_sent_packet_; - scoped_ptr<SendAlgorithmInterface> send_algorithm_; - SendAlgorithmInterface::SentPacketsMap packet_history_map_; - PendingPacketsMap pending_packets_; -}; - -} // namespace net - -#endif // NET_QUIC_CONGESTION_CONTROL_QUIC_SEND_SCHEDULER_H_ diff --git a/net/quic/congestion_control/quic_send_scheduler_test.cc b/net/quic/congestion_control/quic_send_scheduler_test.cc deleted file mode 100644 index 9347b2c..0000000 --- a/net/quic/congestion_control/quic_send_scheduler_test.cc +++ /dev/null @@ -1,197 +0,0 @@ -// 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 "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "net/quic/congestion_control/quic_receipt_metrics_collector.h" -#include "net/quic/congestion_control/quic_send_scheduler.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 QuicSendSchedulerTest : public ::testing::Test { - protected: - void SetUpCongestionType(CongestionFeedbackType congestion_type) { - sender_.reset(new QuicSendScheduler(&clock_, congestion_type)); - } - MockClock clock_; - scoped_ptr<QuicSendScheduler> sender_; -}; - -TEST_F(QuicSendSchedulerTest, FixedRateSenderAPI) { - SetUpCongestionType(kFixRate); - QuicCongestionFeedbackFrame congestion_feedback; - congestion_feedback.type = kFixRate; - congestion_feedback.fix_rate.bitrate_in_bytes_per_second = 30000; - sender_->OnIncomingQuicCongestionFeedbackFrame(congestion_feedback); - EXPECT_EQ(-1, sender_->PeakSustainedBandwidth()); - EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); - EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow()); - sender_->SentPacket(1, kMaxPacketSize, false); - EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40), - sender_->TimeUntilSend(false)); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(35)); - EXPECT_EQ(QuicTime::Delta::Infinite(), - sender_->TimeUntilSend(false)); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); - EXPECT_EQ(QuicTime::Delta::Infinite(), - sender_->TimeUntilSend(false)); -} - -TEST_F(QuicSendSchedulerTest, FixedRatePacing) { - SetUpCongestionType(kFixRate); - QuicAckFrame ack; - ack.received_info.largest_observed = 0; - sender_->OnIncomingAckFrame(ack); - - QuicCongestionFeedbackFrame feedback; - feedback.type = kFixRate; - feedback.fix_rate.bitrate_in_bytes_per_second = 100000; - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback); - - QuicTime acc_advance_time(QuicTime::Zero()); - for (int i = 1; i <= 100; ++i) { - EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); - EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow()); - sender_->SentPacket(i, kMaxPacketSize, false); - QuicTime::Delta advance_time = sender_->TimeUntilSend(false); - clock_.AdvanceTime(advance_time); - acc_advance_time = acc_advance_time.Add(advance_time); - // Ack the packet we sent. - ack.received_info.RecordReceived(i); - sender_->OnIncomingAckFrame(ack); - } - EXPECT_EQ(QuicTime::FromMilliseconds(1200), acc_advance_time); -} - -TEST_F(QuicSendSchedulerTest, AvailableCongestionWindow) { - SetUpCongestionType(kFixRate); - QuicAckFrame ack; - sender_->OnIncomingAckFrame(ack); - - QuicCongestionFeedbackFrame feedback; - feedback.type = kFixRate; - feedback.fix_rate.bitrate_in_bytes_per_second = 100000; - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback); - - EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); - EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow()); - const int32 num_packets = 12; - for (int i = 1; i <= num_packets; i++) { - EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); - sender_->SentPacket(i, 100, false); - EXPECT_EQ(kMaxPacketSize - (i * 100), sender_->AvailableCongestionWindow()); - } - // Ack the packets we sent. - for (int i = 1; i <= num_packets; i++) { - ack.received_info.RecordReceived(i); - } - sender_->OnIncomingAckFrame(ack); - EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow()); -} - -TEST_F(QuicSendSchedulerTest, FixedRateBandwidth) { - SetUpCongestionType(kFixRate); - QuicAckFrame ack; - sender_->OnIncomingAckFrame(ack); - - QuicCongestionFeedbackFrame feedback; - feedback.type = kFixRate; - feedback.fix_rate.bitrate_in_bytes_per_second = 100000; - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback); - - for (int i = 1; i <= 100; ++i) { - QuicTime::Delta advance_time = sender_->TimeUntilSend(false); - clock_.AdvanceTime(advance_time); - EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); - EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow()); - sender_->SentPacket(i, 1000, false); - // Ack the packet we sent. - ack.received_info.RecordReceived(i); - sender_->OnIncomingAckFrame(ack); - } - EXPECT_EQ(100000, sender_->BandwidthEstimate()); - EXPECT_NEAR(100000, sender_->PeakSustainedBandwidth(), 4000); - EXPECT_NEAR(100000, sender_->SentBandwidth(), 4000); -} - -TEST_F(QuicSendSchedulerTest, BandwidthWith3SecondGap) { - SetUpCongestionType(kFixRate); - QuicAckFrame ack; - sender_->OnIncomingAckFrame(ack); - - QuicCongestionFeedbackFrame feedback; - feedback.type = kFixRate; - feedback.fix_rate.bitrate_in_bytes_per_second = 100000; - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback); - - for (int i = 1; i <= 100; ++i) { - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); - EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); - EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow()); - sender_->SentPacket(i, 1000, false); - // Ack the packet we sent. - ack.received_info.RecordReceived(i); - sender_->OnIncomingAckFrame(ack); - } - EXPECT_EQ(100000, sender_->BandwidthEstimate()); - EXPECT_NEAR(100000, sender_->PeakSustainedBandwidth(), 2000); - EXPECT_NEAR(100000, sender_->SentBandwidth(), 2000); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000)); - EXPECT_NEAR(50000, sender_->SentBandwidth(), 1000); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2100)); - EXPECT_NEAR(100000, sender_->BandwidthEstimate(), 2000); - EXPECT_NEAR(100000, sender_->PeakSustainedBandwidth(), 2000); - EXPECT_EQ(0, sender_->SentBandwidth()); - for (int i = 1; i <= 150; ++i) { - EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); - EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow()); - sender_->SentPacket(i + 100, 1000, false); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); - // Ack the packet we sent. - ack.received_info.RecordReceived(i + 100); - sender_->OnIncomingAckFrame(ack); - } - EXPECT_EQ(100000, sender_->BandwidthEstimate()); - EXPECT_NEAR(100000, sender_->PeakSustainedBandwidth(), 2000); - EXPECT_NEAR(100000, sender_->SentBandwidth(), 2000); -} - -TEST_F(QuicSendSchedulerTest, Pacing) { - SetUpCongestionType(kFixRate); - QuicAckFrame ack; - ack.received_info.largest_observed = 0; - sender_->OnIncomingAckFrame(ack); - - QuicCongestionFeedbackFrame feedback; - feedback.type = kFixRate; - // Test a high bitrate (8Mbit/s) to trigger pacing. - feedback.fix_rate.bitrate_in_bytes_per_second = 1000000; - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback); - - QuicTime acc_advance_time(QuicTime::Zero()); - for (int i = 1; i <= 100;) { - EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); - EXPECT_EQ(kMaxPacketSize * 2, sender_->AvailableCongestionWindow()); - sender_->SentPacket(i++, kMaxPacketSize, false); - EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); - sender_->SentPacket(i++, kMaxPacketSize, false); - QuicTime::Delta advance_time = sender_->TimeUntilSend(false); - clock_.AdvanceTime(advance_time); - acc_advance_time = acc_advance_time.Add(advance_time); - // Ack the packets we sent. - ack.received_info.RecordReceived(i - 2); - sender_->OnIncomingAckFrame(ack); - ack.received_info.RecordReceived(i - 1); - sender_->OnIncomingAckFrame(ack); - } - EXPECT_EQ(QuicTime::FromMilliseconds(120), acc_advance_time); -} - -} // namespace test -} // namespace net diff --git a/net/quic/congestion_control/receive_algorithm_interface.h b/net/quic/congestion_control/receive_algorithm_interface.h index 461ec23..e2bae4b 100644 --- a/net/quic/congestion_control/receive_algorithm_interface.h +++ b/net/quic/congestion_control/receive_algorithm_interface.h @@ -33,7 +33,7 @@ class NET_EXPORT_PRIVATE ReceiveAlgorithmInterface { // timestamp: is the sent timestamp from the QUIC packet header. // revived: is set if the packet is lost and then recovered with help of FEC // (Forward Error Correction) packet(s). - virtual void RecordIncomingPacket(size_t bytes, + virtual void RecordIncomingPacket(QuicByteCount bytes, QuicPacketSequenceNumber sequence_number, QuicTime timestamp, bool revived) = 0; diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h index 8f83615..efcc172 100644 --- a/net/quic/congestion_control/send_algorithm_interface.h +++ b/net/quic/congestion_control/send_algorithm_interface.h @@ -9,27 +9,26 @@ #include "base/basictypes.h" #include "net/base/net_export.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" namespace net { -const int kNoValidEstimate = -1; - class NET_EXPORT_PRIVATE SendAlgorithmInterface { public: class SentPacket { public: - SentPacket(size_t bytes, QuicTime timestamp) + SentPacket(QuicByteCount bytes, QuicTime timestamp) : bytes_sent_(bytes), send_timestamp_(timestamp) { } - size_t BytesSent() { return bytes_sent_; } + QuicByteCount BytesSent() { return bytes_sent_; } QuicTime& SendTimestamp() { return send_timestamp_; } private: - size_t bytes_sent_; + QuicByteCount bytes_sent_; QuicTime send_timestamp_; }; @@ -47,7 +46,7 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface { // Called for each received ACK, with sequence number from remote peer. virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, - size_t acked_bytes, + QuicByteCount acked_bytes, QuicTime::Delta rtt) = 0; virtual void OnIncomingLoss(int number_of_lost_packets) = 0; @@ -55,7 +54,7 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface { // 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, - size_t bytes, + QuicByteCount bytes, bool is_retransmission) = 0; // Calculate the time until we can send the next packet. @@ -63,12 +62,9 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface { // of the congestion window. virtual QuicTime::Delta TimeUntilSend(bool is_retransmission) = 0; - // The current available congestion window in bytes. - virtual size_t AvailableCongestionWindow() = 0; - // What's the current estimated bandwidth in bytes per second. - // Returns KNoValidEstimate when it does not have an estimate. - virtual int BandwidthEstimate() = 0; + // Returns 0 when it does not have an estimate. + virtual QuicBandwidth BandwidthEstimate() = 0; }; } // namespace net diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc index 8087283..f309c77 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/net/quic/congestion_control/tcp_cubic_sender.cc @@ -4,15 +4,13 @@ #include "net/quic/congestion_control/tcp_cubic_sender.h" -#include "net/quic/congestion_control/quic_send_scheduler.h" - namespace { // Constants based on TCP defaults. -const size_t kHybridStartLowWindow = 16; -const int kMaxSegmentSize = net::kMaxPacketSize; -const int kDefaultReceiveWindow = 64000; -const size_t kInitialCongestionWindow = 10; -const size_t kMaxCongestionWindow = 10000; +const int64 kHybridStartLowWindow = 16; +const net::QuicByteCount kMaxSegmentSize = net::kMaxPacketSize; +const net::QuicByteCount kDefaultReceiveWindow = 64000; +const int64 kInitialCongestionWindow = 10; +const int64 kMaxCongestionWindow = 10000; const int kMaxBurstLength = 3; }; @@ -23,7 +21,7 @@ TcpCubicSender::TcpCubicSender(const QuicClock* clock, bool reno) cubic_(clock), reno_(reno), congestion_window_count_(0), - receiver_congestion_window_in_bytes_(kDefaultReceiveWindow), + receiver_congestion_window_(kDefaultReceiveWindow), last_received_accumulated_number_of_lost_packets_(0), bytes_in_flight_(0), update_end_sequence_number_(true), @@ -47,12 +45,11 @@ void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame( OnIncomingLoss(recovered_lost_packets); } } - receiver_congestion_window_in_bytes_ = - feedback.tcp.receive_window << 4; + receiver_congestion_window_ = feedback.tcp.receive_window; } void TcpCubicSender::OnIncomingAck( - QuicPacketSequenceNumber acked_sequence_number, size_t acked_bytes, + QuicPacketSequenceNumber acked_sequence_number, QuicByteCount acked_bytes, QuicTime::Delta rtt) { bytes_in_flight_ -= acked_bytes; CongestionAvoidance(acked_sequence_number); @@ -82,7 +79,7 @@ void TcpCubicSender::OnIncomingLoss(int /*number_of_lost_packets*/) { } void TcpCubicSender::SentPacket(QuicPacketSequenceNumber sequence_number, - size_t bytes, + QuicByteCount bytes, bool is_retransmission) { if (!is_retransmission) { bytes_in_flight_ += bytes; @@ -107,24 +104,24 @@ QuicTime::Delta TcpCubicSender::TimeUntilSend(bool is_retransmission) { return QuicTime::Delta::Zero(); } -size_t TcpCubicSender::AvailableCongestionWindow() { +QuicByteCount TcpCubicSender::AvailableCongestionWindow() { if (bytes_in_flight_ > CongestionWindow()) { return 0; } return CongestionWindow() - bytes_in_flight_; } -size_t TcpCubicSender::CongestionWindow() { +QuicByteCount TcpCubicSender::CongestionWindow() { // What's the current congestion window in bytes. - return std::min(receiver_congestion_window_in_bytes_, - static_cast<int>(congestion_window_ * kMaxSegmentSize)); + return std::min(receiver_congestion_window_, + congestion_window_ * kMaxSegmentSize); } -int TcpCubicSender::BandwidthEstimate() { +QuicBandwidth TcpCubicSender::BandwidthEstimate() { // TODO(pwestin): make a long term estimate, based on RTT and loss rate? or // instantaneous estimate? // Throughput ~= (1/RTT)*sqrt(3/2p) - return kNoValidEstimate; + return QuicBandwidth::Zero(); } void TcpCubicSender::Reset() { @@ -133,12 +130,13 @@ void TcpCubicSender::Reset() { } bool TcpCubicSender::IsCwndLimited() const { - const size_t congestion_window_bytes = congestion_window_ * kMaxSegmentSize; + const QuicByteCount congestion_window_bytes = congestion_window_ * + kMaxSegmentSize; if (bytes_in_flight_ >= congestion_window_bytes) { return true; } - const size_t tcp_max_burst = kMaxBurstLength * kMaxSegmentSize; - const size_t left = congestion_window_bytes - bytes_in_flight_; + const QuicByteCount tcp_max_burst = kMaxBurstLength * kMaxSegmentSize; + const QuicByteCount left = congestion_window_bytes - bytes_in_flight_; return left <= tcp_max_burst; } diff --git a/net/quic/congestion_control/tcp_cubic_sender.h b/net/quic/congestion_control/tcp_cubic_sender.h index 54efedd..c91642c 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.h +++ b/net/quic/congestion_control/tcp_cubic_sender.h @@ -14,11 +14,17 @@ #include "net/quic/congestion_control/cubic.h" #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" namespace net { +namespace test { +class TcpCubicSenderPeer; +} // namespace test + class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { public: // Reno option provided for testing. @@ -29,21 +35,21 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { const QuicCongestionFeedbackFrame& feedback, const SentPacketsMap& sent_packets) OVERRIDE; virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, - size_t acked_bytes, + QuicByteCount acked_bytes, QuicTime::Delta rtt) OVERRIDE; virtual void OnIncomingLoss(int number_of_lost_packets) OVERRIDE; virtual void SentPacket(QuicPacketSequenceNumber sequence_number, - size_t bytes, + QuicByteCount bytes, bool is_retransmission) OVERRIDE; virtual QuicTime::Delta TimeUntilSend(bool is_retransmission) OVERRIDE; - virtual size_t AvailableCongestionWindow() OVERRIDE; - virtual int BandwidthEstimate() OVERRIDE; + virtual QuicBandwidth BandwidthEstimate() OVERRIDE; // End implementation of SendAlgorithmInterface. - // Visible for testing. - size_t CongestionWindow(); - private: + friend class test::TcpCubicSenderPeer; + + QuicByteCount AvailableCongestionWindow(); + QuicByteCount CongestionWindow(); void Reset(); void AckAccounting(QuicTime::Delta rtt); void CongestionAvoidance(QuicPacketSequenceNumber ack); @@ -57,16 +63,16 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { const bool reno_; // ACK counter for the Reno implementation. - size_t congestion_window_count_; + int64 congestion_window_count_; // Receiver side advertised window. - int receiver_congestion_window_in_bytes_; + QuicByteCount receiver_congestion_window_; // Receiver side advertised packet loss. int last_received_accumulated_number_of_lost_packets_; // Bytes in flight, aka bytes on the wire. - size_t bytes_in_flight_; + QuicByteCount bytes_in_flight_; // We need to keep track of the end sequence number of each RTT "burst". bool update_end_sequence_number_; @@ -80,6 +86,8 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { // Min RTT during this session. QuicTime::Delta delay_min_; + + DISALLOW_COPY_AND_ASSIGN(TcpCubicSender); }; } // namespace net diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc index 1ce7d7a..5e3edc4 100644 --- a/net/quic/congestion_control/tcp_cubic_sender_test.cc +++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -12,26 +12,32 @@ namespace net { namespace test { -const uint32 kDefaultWindowTCP = 10 * net::kMaxPacketSize; -const size_t kNoNBytesInFlight = 0; +const uint32 kDefaultWindowTCP = 10 * kMaxPacketSize; +const QuicByteCount kNoNBytesInFlight = 0; -class QuicTcpCubicSenderTest : public ::testing::Test { +class TcpCubicSenderPeer : public TcpCubicSender { + public: + explicit TcpCubicSenderPeer(const QuicClock* clock, bool reno) + : TcpCubicSender(clock, reno) { + } + using TcpCubicSender::AvailableCongestionWindow; + using TcpCubicSender::CongestionWindow; +}; + +class TcpCubicSenderTest : public ::testing::Test { protected: - QuicTcpCubicSenderTest() + TcpCubicSenderTest() : rtt_(QuicTime::Delta::FromMilliseconds(60)), - one_ms_(QuicTime::Delta::FromMilliseconds(1)) { - } - void SetUp() { - bool reno = true; - sender_.reset(new TcpCubicSender(&clock_, reno)); - receiver_.reset(new TcpReceiver()); - sequence_number_ = 1; - acked_sequence_number_ = 0; + one_ms_(QuicTime::Delta::FromMilliseconds(1)), + sender_(new TcpCubicSenderPeer(&clock_, true)), + receiver_(new TcpReceiver()), + sequence_number_(1), + acked_sequence_number_(0) { } void SendAvailableCongestionWindow() { - size_t bytes_to_send = sender_->AvailableCongestionWindow(); + QuicByteCount bytes_to_send = sender_->AvailableCongestionWindow(); while (bytes_to_send > 0) { - size_t bytes_in_packet = std::min(kMaxPacketSize, bytes_to_send); + QuicByteCount bytes_in_packet = std::min(kMaxPacketSize, bytes_to_send); sender_->SentPacket(sequence_number_++, bytes_in_packet, false); bytes_to_send -= bytes_in_packet; if (bytes_to_send > 0) { @@ -52,13 +58,13 @@ class QuicTcpCubicSenderTest : public ::testing::Test { const QuicTime::Delta one_ms_; MockClock clock_; SendAlgorithmInterface::SentPacketsMap not_used_; - scoped_ptr<TcpCubicSender> sender_; + scoped_ptr<TcpCubicSenderPeer> sender_; scoped_ptr<TcpReceiver> receiver_; QuicPacketSequenceNumber sequence_number_; QuicPacketSequenceNumber acked_sequence_number_; }; -TEST_F(QuicTcpCubicSenderTest, SimpleSender) { +TEST_F(TcpCubicSenderTest, SimpleSender) { QuicCongestionFeedbackFrame feedback; // At startup make sure we are at the default. EXPECT_EQ(kDefaultWindowTCP, @@ -77,7 +83,7 @@ TEST_F(QuicTcpCubicSenderTest, SimpleSender) { EXPECT_TRUE(sender_->TimeUntilSend(true).IsZero()); } -TEST_F(QuicTcpCubicSenderTest, ExponentialSlowStart) { +TEST_F(TcpCubicSenderTest, ExponentialSlowStart) { const int kNumberOfAck = 20; QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. @@ -93,12 +99,12 @@ TEST_F(QuicTcpCubicSenderTest, ExponentialSlowStart) { SendAvailableCongestionWindow(); AckNPackets(2); } - size_t bytes_to_send = sender_->CongestionWindow(); + QuicByteCount bytes_to_send = sender_->CongestionWindow(); EXPECT_EQ(kDefaultWindowTCP + kMaxPacketSize * 2 * kNumberOfAck, bytes_to_send); } -TEST_F(QuicTcpCubicSenderTest, SlowStartAckTrain) { +TEST_F(TcpCubicSenderTest, SlowStartAckTrain) { // Make sure that we fall out of slow start when we send ACK train longer // than half the RTT, in this test case 30ms, which is more than 30 calls to // Ack2Packets in one round. @@ -119,7 +125,7 @@ TEST_F(QuicTcpCubicSenderTest, SlowStartAckTrain) { SendAvailableCongestionWindow(); AckNPackets(2); } - size_t expected_congestion_window = kDefaultWindowTCP + + QuicByteCount expected_congestion_window = kDefaultWindowTCP + (kMaxPacketSize * 2 * kNumberOfAck); EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); // We should now have fallen out of slow start. @@ -142,7 +148,7 @@ TEST_F(QuicTcpCubicSenderTest, SlowStartAckTrain) { EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); } -TEST_F(QuicTcpCubicSenderTest, SlowStartPacketLoss) { +TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) { // Make sure that we fall out of slow start when we encounter a packet loss. const int kNumberOfAck = 10; QuicCongestionFeedbackFrame feedback; @@ -160,7 +166,7 @@ TEST_F(QuicTcpCubicSenderTest, SlowStartPacketLoss) { AckNPackets(2); } SendAvailableCongestionWindow(); - size_t expected_congestion_window = kDefaultWindowTCP + + QuicByteCount expected_congestion_window = kDefaultWindowTCP + (kMaxPacketSize * 2 * kNumberOfAck); EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); diff --git a/net/quic/congestion_control/tcp_receiver.cc b/net/quic/congestion_control/tcp_receiver.cc index 3401b21..465e40f 100644 --- a/net/quic/congestion_control/tcp_receiver.cc +++ b/net/quic/congestion_control/tcp_receiver.cc @@ -8,11 +8,11 @@ namespace net { // Originally 64K bytes for TCP, setting it to 256K to support higher bitrates. -const size_t kReceiveWindowTCP = 256000; +const QuicByteCount kReceiveWindowTCP = 256000; TcpReceiver::TcpReceiver() : accumulated_number_of_recoverd_lost_packets_(0), - receive_window_in_bytes_(kReceiveWindowTCP) { + receive_window_(kReceiveWindowTCP) { } bool TcpReceiver::GenerateCongestionFeedback( @@ -20,11 +20,11 @@ bool TcpReceiver::GenerateCongestionFeedback( feedback->type = kTCP; feedback->tcp.accumulated_number_of_lost_packets = accumulated_number_of_recoverd_lost_packets_; - feedback->tcp.receive_window = receive_window_in_bytes_ >> 4; + feedback->tcp.receive_window = receive_window_; return true; } -void TcpReceiver::RecordIncomingPacket(size_t bytes, +void TcpReceiver::RecordIncomingPacket(QuicByteCount bytes, QuicPacketSequenceNumber sequence_number, QuicTime timestamp, bool revived) { diff --git a/net/quic/congestion_control/tcp_receiver.h b/net/quic/congestion_control/tcp_receiver.h index 48d781f..695ffbb 100644 --- a/net/quic/congestion_control/tcp_receiver.h +++ b/net/quic/congestion_control/tcp_receiver.h @@ -24,7 +24,7 @@ class NET_EXPORT_PRIVATE TcpReceiver : public ReceiveAlgorithmInterface { virtual bool GenerateCongestionFeedback( QuicCongestionFeedbackFrame* feedback) OVERRIDE; - virtual void RecordIncomingPacket(size_t bytes, + virtual void RecordIncomingPacket(QuicByteCount bytes, QuicPacketSequenceNumber sequence_number, QuicTime timestamp, bool revived) OVERRIDE; @@ -32,9 +32,10 @@ class NET_EXPORT_PRIVATE TcpReceiver : public ReceiveAlgorithmInterface { private: // We need to keep track of FEC recovered packets. int accumulated_number_of_recoverd_lost_packets_; - uint32 receive_window_in_bytes_; + QuicByteCount receive_window_; + + DISALLOW_COPY_AND_ASSIGN(TcpReceiver); }; } // namespace net - #endif // NET_QUIC_CONGESTION_CONTROL_TCP_RECEIVER_H_ diff --git a/net/quic/congestion_control/tcp_receiver_test.cc b/net/quic/congestion_control/tcp_receiver_test.cc index 1d4a2bb..5bb12f4 100644 --- a/net/quic/congestion_control/tcp_receiver_test.cc +++ b/net/quic/congestion_control/tcp_receiver_test.cc @@ -25,12 +25,12 @@ TEST_F(QuicTcpReceiverTest, SimpleReceiver) { receiver_->RecordIncomingPacket(1, 1, timestamp, false); ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); EXPECT_EQ(kTCP, feedback.type); - EXPECT_EQ(256000, feedback.tcp.receive_window << 4); + EXPECT_EQ(256000u, feedback.tcp.receive_window); EXPECT_EQ(0, feedback.tcp.accumulated_number_of_lost_packets); receiver_->RecordIncomingPacket(1, 2, timestamp, true); ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); EXPECT_EQ(kTCP, feedback.type); - EXPECT_EQ(256000, feedback.tcp.receive_window << 4); + EXPECT_EQ(256000u, feedback.tcp.receive_window); EXPECT_EQ(1, feedback.tcp.accumulated_number_of_lost_packets); } diff --git a/net/quic/quic_bandwidth.cc b/net/quic/quic_bandwidth.cc index c270d01..33cb67e 100644 --- a/net/quic/quic_bandwidth.cc +++ b/net/quic/quic_bandwidth.cc @@ -41,10 +41,11 @@ QuicBandwidth QuicBandwidth::FromKBytesPerSecond(int64 k_bytes_per_second) { } // static -QuicBandwidth QuicBandwidth::FromBytesAndTimeDelta(int64 bytes, +QuicBandwidth QuicBandwidth::FromBytesAndTimeDelta(QuicByteCount bytes, QuicTime::Delta delta) { DCHECK_LT(bytes, - kQuicInfiniteBandwidth / (8 * base::Time::kMicrosecondsPerSecond)); + static_cast<uint64>(kQuicInfiniteBandwidth / + (8 * base::Time::kMicrosecondsPerSecond))); int64 bytes_per_second = (bytes * base::Time::kMicrosecondsPerSecond) / delta.ToMicroseconds(); return QuicBandwidth(bytes_per_second * 8); @@ -71,6 +72,17 @@ int64 QuicBandwidth::ToKBytesPerSecond() const { return bits_per_second_ / 8000; } +QuicByteCount QuicBandwidth::ToBytesPerPeriod( + QuicTime::Delta time_period) const { + return ToBytesPerSecond() * time_period.ToMicroseconds() / + base::Time::kMicrosecondsPerSecond; +} + +int64 QuicBandwidth::ToKBytesPerPeriod(QuicTime::Delta time_period) const { + return ToKBytesPerSecond() * time_period.ToMicroseconds() / + base::Time::kMicrosecondsPerSecond; +} + bool QuicBandwidth::IsZero() const { return (bits_per_second_ == 0); } @@ -83,4 +95,8 @@ QuicBandwidth QuicBandwidth::Subtract(const QuicBandwidth& delta) const { return QuicBandwidth(bits_per_second_ - delta.bits_per_second_); } +QuicBandwidth QuicBandwidth::Scale(float scale_factor) const { + return QuicBandwidth(bits_per_second_ * scale_factor); +} + } // namespace net diff --git a/net/quic/quic_bandwidth.h b/net/quic/quic_bandwidth.h index 522bd4c..71c4b2d 100644 --- a/net/quic/quic_bandwidth.h +++ b/net/quic/quic_bandwidth.h @@ -12,7 +12,10 @@ namespace net { +typedef uint64 QuicByteCount; + class NET_EXPORT_PRIVATE QuicBandwidth { + public: // Creates a new QuicBandwidth with an internal value of 0. static QuicBandwidth Zero(); @@ -30,7 +33,7 @@ class NET_EXPORT_PRIVATE QuicBandwidth { static QuicBandwidth FromKBytesPerSecond(int64 k_bytes_per_second); // Create a new QuicBandwidth based on the bytes per the elapsed delta. - static QuicBandwidth FromBytesAndTimeDelta(int64 bytes, + static QuicBandwidth FromBytesAndTimeDelta(QuicByteCount bytes, QuicTime::Delta delta); int64 ToBitsPerSecond() const; @@ -41,12 +44,18 @@ class NET_EXPORT_PRIVATE QuicBandwidth { int64 ToKBytesPerSecond() const; + QuicByteCount ToBytesPerPeriod(QuicTime::Delta time_period) const; + + int64 ToKBytesPerPeriod(QuicTime::Delta time_period) const; + bool IsZero() const; QuicBandwidth Add(const QuicBandwidth& delta) const; QuicBandwidth Subtract(const QuicBandwidth& delta) const; + QuicBandwidth Scale(float scale_factor) const; + private: explicit QuicBandwidth(int64 bits_per_second); int64 bits_per_second_; diff --git a/net/quic/quic_bandwidth_test.cc b/net/quic/quic_bandwidth_test.cc index aa089e6..1090f0b 100644 --- a/net/quic/quic_bandwidth_test.cc +++ b/net/quic/quic_bandwidth_test.cc @@ -6,7 +6,7 @@ #include "testing/gtest/include/gtest/gtest.h" namespace net { -namespace testing { +namespace test { class QuicBandwidthTest : public ::testing::Test { }; @@ -57,5 +57,26 @@ TEST_F(QuicBandwidthTest, TimeDelta) { 1000, QuicTime::Delta::FromMilliseconds(100))); } -} // namespace testing +TEST_F(QuicBandwidthTest, Scale) { + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(500), + QuicBandwidth::FromKBytesPerSecond(1000).Scale(0.5f)); + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(750), + QuicBandwidth::FromKBytesPerSecond(1000).Scale(0.75f)); + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1250), + QuicBandwidth::FromKBytesPerSecond(1000).Scale(1.25f)); +} + + +TEST_F(QuicBandwidthTest, BytesPerPeriod) { + EXPECT_EQ(2000u, QuicBandwidth::FromKBytesPerSecond(2000).ToBytesPerPeriod( + QuicTime::Delta::FromMilliseconds(1))); + EXPECT_EQ(2u, QuicBandwidth::FromKBytesPerSecond(2000).ToKBytesPerPeriod( + QuicTime::Delta::FromMilliseconds(1))); + EXPECT_EQ(200000u, QuicBandwidth::FromKBytesPerSecond(2000).ToBytesPerPeriod( + QuicTime::Delta::FromMilliseconds(100))); + EXPECT_EQ(200u, QuicBandwidth::FromKBytesPerSecond(2000).ToKBytesPerPeriod( + QuicTime::Delta::FromMilliseconds(100))); +} + +} // namespace test } // namespace net diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 77d15f1..c0d2194 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -9,8 +9,6 @@ #include "base/logging.h" #include "base/stl_util.h" #include "net/base/net_errors.h" -#include "net/quic/congestion_control/quic_receipt_metrics_collector.h" -#include "net/quic/congestion_control/quic_send_scheduler.h" #include "net/quic/quic_utils.h" using base::hash_map; @@ -46,7 +44,7 @@ const int kMaxRetransmissionsPerAck = 10; // delivery. // TODO(ianswett): Change to match TCP's rule of retransmitting once an ack // at least 3 sequence numbers larger arrives. -const int kNumberOfNacksBeforeRetransmission = 3; +const size_t kNumberOfNacksBeforeRetransmission = 3; // The maxiumum number of packets we'd like to queue. We may end up queueing // more in the case of many control frames. @@ -57,9 +55,11 @@ const int kMaxPacketsToSerializeAtOnce = 6; // eventually cede. 10 is arbitrary. const int kMaxPacketsPerRetransmissionAlarm = 10; -// Named constants for SendPacket options. +// Named constant for WriteQueuedData(). +const bool kFlush = true; +// Named constant for WritePacket(), SendOrQueuePacket(). const bool kForce = true; -const bool kShouldRetransmit = true; +// Named constant for CanWrite(). const bool kIsRetransmission = true; bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) { @@ -70,14 +70,12 @@ bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) { } // namespace QuicConnection::UnackedPacket::UnackedPacket(QuicFrames unacked_frames) - : frames(unacked_frames), - number_nacks(0) { + : frames(unacked_frames) { } QuicConnection::UnackedPacket::UnackedPacket(QuicFrames unacked_frames, std::string data) : frames(unacked_frames), - number_nacks(0), data(data) { } @@ -166,27 +164,21 @@ void QuicConnection::DeleteEnclosedFrame(QuicFrame* frame) { void QuicConnection::DeleteEnclosedFrames(UnackedPacket* unacked) { for (QuicFrames::iterator it = unacked->frames.begin(); it != unacked->frames.end(); ++it) { - DCHECK(ShouldRetransmit(*it)); DeleteEnclosedFrame(&(*it)); } } -bool QuicConnection::ShouldRetransmit(const QuicFrame& frame) { - return frame.type != ACK_FRAME && frame.type != CONGESTION_FEEDBACK_FRAME; -} - void QuicConnection::OnError(QuicFramer* framer) { SendConnectionClose(framer->error()); } -void QuicConnection::OnPacket(const IPEndPoint& self_address, - const IPEndPoint& peer_address) { +void QuicConnection::OnPacket() { time_of_last_packet_ = clock_->Now(); DVLOG(1) << "last packet: " << time_of_last_packet_.ToMicroseconds(); // TODO(alyssar, rch) handle migration! - self_address_ = self_address; - peer_address_ = peer_address; + self_address_ = last_self_address_; + peer_address_ = last_peer_address_; } void QuicConnection::OnPublicResetPacket( @@ -340,9 +332,11 @@ void QuicConnection::UpdatePacketInformationReceivedByPeer( // incoming_ack shows they've been seen by the peer. UnackedPacketMap::iterator it = unacked_packets_.begin(); while (it != unacked_packets_.end()) { - if (!incoming_ack.received_info.IsAwaitingPacket(it->first)) { + QuicPacketSequenceNumber sequence_number = it->first; + UnackedPacket* unacked = it->second; + if (!incoming_ack.received_info.IsAwaitingPacket(sequence_number)) { // Packet was acked, so remove it from our unacked packet list. - DVLOG(1) << "Got an ack for " << it->first; + 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 @@ -352,46 +346,46 @@ void QuicConnection::UpdatePacketInformationReceivedByPeer( // attempted). for (QueuedPacketList::iterator q = queued_packets_.begin(); q != queued_packets_.end(); ++q) { - if (q->sequence_number == it->first) { + if (q->sequence_number == sequence_number) { queued_packets_.erase(q); break; } } - acked_packets.insert(it->first); - DeleteEnclosedFrames(it->second); - delete it->second; + acked_packets.insert(sequence_number); + DeleteEnclosedFrames(unacked); + delete unacked; UnackedPacketMap::iterator it_tmp = it; ++it; unacked_packets_.erase(it_tmp); + retransmission_map_.erase(sequence_number); } else { // This is a packet which we planned on retransmitting and has not been // seen at the time of this ack being sent out. See if it's our new // lowest unacked packet. - DVLOG(1) << "still missing " << it->first; - if (it->first < lowest_unacked) { - lowest_unacked = it->first; + 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. - QuicPacketSequenceNumber retransmission_number = 0; - if (it->first < peer_largest_observed_packet_) { + if (sequence_number <= peer_largest_observed_packet_) { // The peer got packets after this sequence number. This is an explicit // nack. - ++(it->second->number_nacks); - if (it->second->number_nacks >= kNumberOfNacksBeforeRetransmission && + RetransmissionMap::iterator retransmission_it = + retransmission_map_.find(sequence_number); + ++(retransmission_it->second.number_nacks); + if (retransmission_it->second.number_nacks >= + kNumberOfNacksBeforeRetransmission && retransmitted_packets < kMaxRetransmissionsPerAck) { - retransmission_number = it->first; + ++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); } } - - ++it; - if (retransmission_number > 0) { - ++retransmitted_packets; - DVLOG(1) << "Trying to retransmit packet " << retransmission_number - << " as it has been nacked 3 or more times."; - MaybeRetransmitPacket(retransmission_number); - } } } if (acked_packets.size() > 0) { @@ -461,10 +455,9 @@ void QuicConnection::OnPacketComplete() { 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_); + congestion_manager_.RecordIncomingPacket( + last_size_, last_header_.packet_sequence_number, + clock_->Now(), last_packet_revived_); } else { DLOG(INFO) << "Got revived packet with " << last_stream_frames_.size() << " frames."; @@ -486,53 +479,55 @@ void QuicConnection::MaybeSendAckInResponseToPacket() { } else if (!last_stream_frames_.empty()) { // TODO(alyssar) this case should really be "if the packet contained any // non-ack frame", rather than "if the packet contained a stream frame" - helper_->SetAckAlarm(DefaultRetransmissionTime()); + helper_->SetAckAlarm(congestion_manager_.DefaultRetransmissionTime()); } send_ack_in_response_to_packet_ = !send_ack_in_response_to_packet_; } -QuicConsumedData QuicConnection::SendStreamData( - QuicStreamId id, +QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, StringPiece data, QuicStreamOffset offset, bool fin) { size_t total_bytes_consumed = 0; bool fin_consumed = false; - packet_creator_.MaybeStartFEC(); while (queued_packets_.empty()) { - QuicFrames frames; - size_t bytes_consumed = - packet_creator_.CreateStreamFrame(id, data, offset, fin, &frames); + 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); - PacketPair pair = packet_creator_.SerializeAllFrames(frames); + // 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. - SendPacket(pair.first, pair.second, kShouldRetransmit, !kForce, - !kIsRetransmission); - UnackedPacket* unacked = new UnackedPacket( - frames, frames[0].stream_frame->data.as_string()); - // Ensure the string piece points to the owned copy of the data. - unacked->frames[0].stream_frame->data = StringPiece(unacked->data); - unacked_packets_.insert(make_pair(pair.first, unacked)); + SendOrQueueCurrentPacket(); - if (packet_creator_.ShouldSendFec(data.size() == 0)) { + if (packet_creator_.ShouldSendFec(false)) { PacketPair fec_pair = packet_creator_.SerializeFec(); - // Never resend FEC packets. - SendPacket(fec_pair.first, fec_pair.second, !kShouldRetransmit, !kForce, - !kIsRetransmission); + // Never retransmit FEC packets. + SendOrQueuePacket(fec_pair.first, fec_pair.second, !kForce); } - if (data.size() == 0) { - // We're done writing the data. Exit the loop. - // We don't make this a precondition beacuse we could have 0 bytes of data + 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); } @@ -543,8 +538,8 @@ void QuicConnection::SendRstStream(QuicStreamId id, new QuicRstStreamFrame(id, offset, error))); // Try to write immediately if possible. - if (CanWrite(false)) { - WriteData(); + if (CanWrite(!kIsRetransmission)) { + WriteQueuedData(kFlush); } } @@ -553,15 +548,21 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, const QuicEncryptedPacket& packet) { last_packet_revived_ = false; last_size_ = packet.length(); - framer_.ProcessPacket(self_address, peer_address, packet); - + last_self_address_ = self_address; + last_peer_address_ = peer_address; + framer_.ProcessPacket(packet); MaybeProcessRevivedPacket(); } bool QuicConnection::OnCanWrite() { write_blocked_ = false; - WriteData(); + WriteQueuedData(!kFlush); + + // 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. @@ -580,56 +581,70 @@ 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::WriteData() { - DCHECK_EQ(false, write_blocked_); - // Serialize the ack and congestion frames before draining the pending queue. - if (should_send_ack_) { - queued_control_frames_.push_back(QuicFrame(&outgoing_ack_)); - } - if (should_send_congestion_feedback_) { - queued_control_frames_.push_back(QuicFrame(&outgoing_congestion_feedback_)); - } - while (!queued_control_frames_.empty()) { - size_t num_serialized; - PacketPair pair = packet_creator_.SerializeFrames( - queued_control_frames_, &num_serialized); - // If any serialized frames need to be retransmitted, add them to - // unacked_packets. - QuicFrames unacked_frames; - for (QuicFrames::const_iterator iter = queued_control_frames_.begin(); - iter != queued_control_frames_.begin() + num_serialized; ++iter) { - if (ShouldRetransmit(*iter)) { - unacked_frames.push_back(*iter); - } - } - if (!unacked_frames.empty()) { - unacked_packets_.insert(make_pair(pair.first, - new UnackedPacket(unacked_frames))); - } - queued_packets_.push_back(QueuedPacket( - pair.first, pair.second, !unacked_frames.empty(), false)); - queued_control_frames_.erase( - queued_control_frames_.begin(), - queued_control_frames_.begin() + num_serialized); - } - should_send_ack_ = false; - should_send_congestion_feedback_ = false; +bool QuicConnection::WriteQueuedData(bool flush) { + 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() && - !queued_packets_.empty()) { + packet_iterator != queued_packets_.end()) { // Ensure that from one iteration of this loop to the next we // succeeded in sending a packet so we don't infinitely loop. // TODO(rch): clean up and close the connection if we really hit this. DCHECK_LT(queued_packets_.size(), num_queued_packets); num_queued_packets = queued_packets_.size(); - QueuedPacket p = queued_packets_.front(); - queued_packets_.pop_front(); - SendPacket(p.sequence_number, p.packet, p.should_retransmit, !kForce, - p.is_retransmission); + if (WritePacket(packet_iterator->sequence_number, + packet_iterator->packet, !kForce)) { + packet_iterator = queued_packets_.erase(packet_iterator); + } else { + // TODO(ianswett): Why not break or return false here? + ++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_; @@ -643,44 +658,61 @@ void QuicConnection::RecordPacketReceived(const QuicPacketHeader& header) { bool QuicConnection::MaybeRetransmitPacketForRTO( QuicPacketSequenceNumber sequence_number) { + DCHECK_EQ(ContainsKey(unacked_packets_, sequence_number), + ContainsKey(retransmission_map_, sequence_number)); + + if (!ContainsKey(unacked_packets_, sequence_number)) { + DVLOG(2) << "alarm fired for " << sequence_number + << " but it has been acked or already retransmitted with " + << " different sequence number."; + // So no extra delay is added for this packet. + return true; + } + // If the packet hasn't been acked and we're getting truncated acks, ignore // any RTO for packets larger than the peer's largest observed packet; it may // have been received by the peer and just wasn't acked due to the ack frame // running out of space. if (received_truncated_ack_ && - sequence_number > peer_largest_observed_packet_ && - ContainsKey(unacked_packets_, sequence_number)) { + sequence_number > peer_largest_observed_packet_) { return false; } else { - MaybeRetransmitPacket(sequence_number); + RetransmitPacket(sequence_number); return true; } } -void QuicConnection::MaybeRetransmitPacket( +void QuicConnection::RetransmitPacket( QuicPacketSequenceNumber sequence_number) { - UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number); - - if (it != unacked_packets_.end()) { - UnackedPacket* unacked = it->second; - // TODO(ianswett): Never change the sequence number of the connect packet. - unacked_packets_.erase(it); - // 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); - 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); - SendPacket(packetpair.first, packetpair.second, kShouldRetransmit, - !kForce, kIsRetransmission); - } else { - DVLOG(2) << "alarm fired for " << sequence_number - << " but it has been acked"; - } + UnackedPacketMap::iterator unacked_it = + unacked_packets_.find(sequence_number); + RetransmissionMap::iterator retransmission_it = + retransmission_map_.find(sequence_number); + // There should always be an entry corresponding to |sequence_number| in + // both |retransmission_map_| and |unacked_packets_|. Retransmissions due to + // RTO for sequence numbers that are already acked or retransmitted are + // ignored by MaybeRetransmitPacketForRTO. + DCHECK(unacked_it != unacked_packets_.end()); + DCHECK(retransmission_it != retransmission_map_.end()); + UnackedPacket* 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); + retransmission_info.number_retransmissions = + retransmission_it->second.number_retransmissions + 1; + retransmission_map_.insert(make_pair(packetpair.first, 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); } bool QuicConnection::CanWrite(bool is_retransmission) { @@ -692,81 +724,91 @@ bool QuicConnection::CanWrite(bool is_retransmission) { QuicTime::Delta delay = congestion_manager_.TimeUntilSend(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() seperately. + // TODO(pwestin): we need to handle delay.IsInfinite() separately. helper_->SetSendAlarm(delay); return false; } return true; } -bool QuicConnection::SendPacket(QuicPacketSequenceNumber sequence_number, - QuicPacket* packet, - bool should_retransmit, - bool force, - bool is_retransmission) { +bool QuicConnection::IsRetransmission( + QuicPacketSequenceNumber sequence_number) { + RetransmissionMap::iterator it = retransmission_map_.find(sequence_number); + return it != retransmission_map_.end() && + it->second.number_retransmissions > 0; +} + +void QuicConnection::MaybeSetupRetransmission( + QuicPacketSequenceNumber sequence_number) { + RetransmissionMap::iterator it = retransmission_map_.find(sequence_number); + if (it == retransmission_map_.end()) { + DVLOG(1) << "Will not retransmit packet " << sequence_number; + return; + } + + RetransmissionInfo retransmission_info = it->second; + QuicTime::Delta retransmission_delay = + congestion_manager_.GetRetransmissionDelay( + retransmission_info.number_retransmissions); + retransmission_info.scheduled_time = clock_->Now().Add(retransmission_delay); + retransmission_timeouts_.push(retransmission_info); + + // Do not set the retransmisson alarm if we're already handling the + // retransmission alarm because the retransmission alarm will be reset when + // OnRetransmissionTimeout completes. + 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; + } +} + +bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + bool forced) { if (!connected_) { DLOG(INFO) << "Dropping packet to be sent since connection is disconnected."; delete packet; - return false; + // Returning true because we deleted the packet and the caller shouldn't + // delete it again. + return true; } - // If this packet is being forced, don't bother checking to see if we should - // write, just write. - if (!force) { - // If we can't write, then simply queue the packet. - if (!CanWrite(is_retransmission)) { - queued_packets_.push_back( - QueuedPacket(sequence_number, packet, should_retransmit, - is_retransmission)); - return false; - } - } - if (should_retransmit) { - // Do not set the retransmisson alarm if we're already handling the - // retransmission alarm because the retransmission alarm will be reset when - // OnRetransmissionTimeout completes. - if (!handling_retransmission_timeout_) { - helper_->SetRetransmissionAlarm(DefaultRetransmissionTime()); - } - retransmission_timeouts_.push_back( - make_pair(sequence_number, clock_->Now().Add( - DefaultRetransmissionTime()))); - - // 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; - } + bool is_retransmission = IsRetransmission(sequence_number); + // If we are not forced and we can't write, then simply return false; + if (!forced && !CanWrite(is_retransmission)) { + return false; } scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet)); - int error; DLOG(INFO) << "Sending packet : " << (packet->is_fec_packet() ? "FEC " : - (should_retransmit ? "data bearing " : " ack only ")) + (ContainsKey(retransmission_map_, sequence_number) ? + "data bearing " : " ack only ")) << "packet " << sequence_number; DCHECK(encrypted->length() <= kMaxPacketSize) << "Packet " << sequence_number << " will not be read; too large: " - << packet->length() << " " << encrypted->length() << " " << outgoing_ack_; + << packet->length() << " " << encrypted->length() << " " + << outgoing_ack_; + int error; int rv = helper_->WritePacketToWire(*encrypted, &error); - if (rv == -1) { - if (error == ERR_IO_PENDING) { - write_blocked_ = true; - - // TODO(rch): uncomment when we get non-blocking (and non-retrying) - // UDP sockets. - /* - queued_packets_.push_front( - QueuedPacket(sequence_number, packet, should_retransmit, - is_retransmission)); - */ - return false; - } - // TODO(wtc): is it correct to fall through to return true? + if (rv == -1 && error == ERR_IO_PENDING) { + write_blocked_ = true; + return false; } + // 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 + // and not when it goes to the pending queue, otherwise we will end up adding + // 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(); @@ -777,6 +819,41 @@ bool QuicConnection::SendPacket(QuicPacketSequenceNumber sequence_number, 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)); + // All unacked packets might be retransmitted. + retransmission_map_.insert(make_pair(pair.first, + RetransmissionInfo(pair.first))); + } + SendOrQueuePacket(pair.first, pair.second, !kForce); +} + +bool QuicConnection::SendOrQueuePacket(QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + bool force) { + if (!WritePacket(sequence_number, packet, force)) { + queued_packets_.push_back(QueuedPacket(sequence_number, packet)); + return false; + } + return true; +} + bool QuicConnection::ShouldSimulateLostPacket() { // TODO(rch): enable this return false; @@ -805,8 +882,8 @@ void QuicConnection::SendAck() { should_send_congestion_feedback_ = true; } // Try to write immediately if possible. - if (CanWrite(false)) { - WriteData(); + if (CanWrite(!kIsRetransmission)) { + WriteQueuedData(kFlush); } } @@ -820,23 +897,22 @@ QuicTime QuicConnection::OnRetransmissionTimeout() { // want to set it to the RTO of B when we return from this function. handling_retransmission_timeout_ = true; - for (int i = 0; i < kMaxPacketsPerRetransmissionAlarm; ++i) { - if (retransmission_timeouts_.empty() || - retransmission_timeouts_.front().second > clock_->Now()) { + for (int i = 0; i < kMaxPacketsPerRetransmissionAlarm && + !retransmission_timeouts_.empty(); ++i) { + RetransmissionInfo retransmission_info = retransmission_timeouts_.top(); + DCHECK(retransmission_info.scheduled_time.IsInitialized()); + if (retransmission_info.scheduled_time > clock_->Now()) { break; } - if (!MaybeRetransmitPacketForRTO(retransmission_timeouts_.front().first)) { + retransmission_timeouts_.pop(); + if (!MaybeRetransmitPacketForRTO(retransmission_info.sequence_number)) { DLOG(INFO) << "MaybeRetransmitPacketForRTO failed: " - << "adding an extra delay."; - // This implicitly delays the RTO for all subsequent packets, since - // MaybeRetransmitPacketForRTO will return false for all packets with - // a larger sequence number anyway. - retransmission_timeouts_.front().second = - retransmission_timeouts_.front().second.Add( - DefaultRetransmissionTime()); - break; + << "adding an extra delay for " + << retransmission_info.sequence_number; + retransmission_info.scheduled_time = clock_->Now().Add( + congestion_manager_.DefaultRetransmissionTime()); + retransmission_timeouts_.push(retransmission_info); } - retransmission_timeouts_.pop_front(); } handling_retransmission_timeout_ = false; @@ -847,7 +923,7 @@ QuicTime QuicConnection::OnRetransmissionTimeout() { // We have packets remaining. Return the absolute RTO of the oldest packet // on the list. - return retransmission_timeouts_.front().second; + return retransmission_timeouts_.top().scheduled_time; } void QuicConnection::MaybeProcessRevivedPacket() { @@ -896,9 +972,9 @@ void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error, frame.ack_frame = outgoing_ack_; PacketPair packetpair = packet_creator_.CloseConnection(&frame); - // There's no point in retransmitting this: we're closing the connection. - SendPacket(packetpair.first, packetpair.second, !kShouldRetransmit, kForce, - !kIsRetransmission); + // There's no point in retransmitting/queueing this: we're closing the + // connection. + WritePacket(packetpair.first, packetpair.second, kForce); CloseConnection(error, false); } diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index b436f9c..b28727e 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -17,6 +17,7 @@ #define NET_QUIC_QUIC_CONNECTION_H_ #include <list> +#include <queue> #include <set> #include <vector> @@ -148,7 +149,6 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface, base::StringPiece data, QuicStreamOffset offset, bool fin); - // Send a stream reset frame to the peer. virtual void SendRstStream(QuicStreamId id, QuicErrorCode error, @@ -175,8 +175,7 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface, // From QuicFramerVisitorInterface virtual void OnError(QuicFramer* framer) OVERRIDE; - virtual void OnPacket(const IPEndPoint& self_address, - const IPEndPoint& peer_address) OVERRIDE; + virtual void OnPacket() OVERRIDE; virtual void OnPublicResetPacket( const QuicPublicResetPacket& packet) OVERRIDE; virtual void OnRevivedPacket() OVERRIDE; @@ -212,7 +211,7 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface, // Called to retransmit a packet, in the case a packet was sufficiently // nacked by the peer, or not acked within the time out window. - void MaybeRetransmitPacket(QuicPacketSequenceNumber sequence_number); + void RetransmitPacket(QuicPacketSequenceNumber sequence_number); QuicPacketCreator::Options* options() { return packet_creator_.options(); } @@ -242,18 +241,31 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface, QuicTime OnRetransmissionTimeout(); protected: - // Send a packet to the peer. If should_retransmit is true, this packet - // contains data, and contents will be retransmitted with a new sequence - // number if we don't get an ack. If force is true, then the packet will - // be sent immediately and the send scheduler will not be consulted. If - // is_retransmission is true, this packet is being retransmitted with a new - // sequence number. Always takes ownership of packet. + // Serializes then sends or queues the packet currently open. + void SendOrQueueCurrentPacket(); + + // 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. // TODO(wtc): none of the callers check the return value. - virtual bool SendPacket(QuicPacketSequenceNumber number, - QuicPacket* packet, - bool should_retransmit, - bool force, - bool is_retransmission); + virtual bool SendOrQueuePacket(QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + bool force); + + // Writes the given packet to socket with the help of helper. Returns true on + // successful write, false otherwise. However, behavior is undefined if + // connection is not established or broken. In any circumstances, a return + // value of true implies that |packet| has been deleted and should not be + // accessed. If |sequence_number| is present in |retransmission_map_| it also + // sets up retransmission of the given packet in case of successful write. If + // |force| is true, then the packet will be sent immediately and the send + // scheduler will not be consulted. + bool WritePacket(QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + bool force); // Make sure an ack we got from our peer is sane. bool ValidateAckFrame(const QuicAckFrame& incoming_ack); @@ -280,22 +292,18 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface, private: friend class test::QuicConnectionPeer; + // Packets which have not been written to the wire. + // Owns the QuicPacket* packet. struct QueuedPacket { QueuedPacket(QuicPacketSequenceNumber sequence_number, - QuicPacket* packet, - bool should_retransmit, - bool is_retransmission) + QuicPacket* packet) : sequence_number(sequence_number), - packet(packet), - should_retransmit(should_retransmit), - is_retransmission(is_retransmission) { + packet(packet) { } QuicPacketSequenceNumber sequence_number; QuicPacket* packet; - bool should_retransmit; - bool is_retransmission; }; struct UnackedPacket { @@ -304,32 +312,58 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface, ~UnackedPacket(); QuicFrames frames; - uint8 number_nacks; // Data referenced by the StringPiece of a QuicStreamFrame. std::string data; }; + struct RetransmissionInfo { + explicit RetransmissionInfo(QuicPacketSequenceNumber sequence_number) + : sequence_number(sequence_number), + scheduled_time(QuicTime::Zero()), + number_nacks(0), + number_retransmissions(0) { + } + + QuicPacketSequenceNumber sequence_number; + QuicTime scheduled_time; + size_t number_nacks; + size_t number_retransmissions; + }; + + class RetransmissionInfoComparator { + public: + bool operator()(const RetransmissionInfo& lhs, + const RetransmissionInfo& rhs) const { + DCHECK(lhs.scheduled_time.IsInitialized() && + rhs.scheduled_time.IsInitialized()); + return lhs.scheduled_time > rhs.scheduled_time; + } + }; + typedef std::list<QueuedPacket> QueuedPacketList; typedef base::hash_map<QuicPacketSequenceNumber, UnackedPacket*> UnackedPacketMap; typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap; - typedef std::list<std::pair<QuicPacketSequenceNumber, QuicTime> > + typedef base::hash_map<QuicPacketSequenceNumber, + RetransmissionInfo> RetransmissionMap; + typedef std::priority_queue<RetransmissionInfo, + std::vector<RetransmissionInfo>, + RetransmissionInfoComparator> RetransmissionTimeouts; - // The amount of time we wait before retransmitting a packet. - static const QuicTime::Delta DefaultRetransmissionTime() { - return QuicTime::Delta::FromMilliseconds(500); - } - static void DeleteEnclosedFrames(UnackedPacket* unacked); - static bool ShouldRetransmit(const QuicFrame& frame); // Checks if a packet can be written now, and sets the timer if necessary. bool CanWrite(bool is_retransmission); + 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. - bool WriteData(); + // 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); // If a packet can be revived from the current FEC group, then // revive and process the packet. @@ -340,9 +374,6 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface, // Get the FEC group associate with the last processed packet. QuicFecGroup* GetFecGroup(); - // Fills the ack frame with the appropriate latched information. - void FillAckFrame(QuicAckFrame* ack); - // Closes any FEC groups protecting packets before |sequence_number|. void CloseFecGroupsBefore(QuicPacketSequenceNumber sequence_number); @@ -352,8 +383,14 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface, QuicRandom* random_generator_; const QuicGuid guid_; + // Address on the last successfully processed packet received from the + // client. IPEndPoint self_address_; IPEndPoint peer_address_; + // Address on the last(currently being processed) packet received. Not + // verified/authenticated. + IPEndPoint last_self_address_; + IPEndPoint last_peer_address_; bool last_packet_revived_; // True if the last packet was revived from FEC. size_t last_size_; // Size of the last received packet. @@ -374,13 +411,17 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface, // to this map, which contains owning pointers to the contained frames. UnackedPacketMap unacked_packets_; - // List of packets that we might need to retransmission, and the time at - // which we should retransmission them. This is currently a FIFO queue which - // means we will never fire an RTO for packet 2 if we are waiting to fire - // the RTO for packet 1. This logic is likely suboptimal but it will - // change when it moves to the SendScheduler so it is fine for now. + // Heap of packets that we might need to retransmit, and the time at + // which we should retransmit them. Every time a packet is sent it is added + // to this heap which is O(log(number of pending packets to be retransmitted)) + // which might be costly. This should be optimized to O(1) by maintaining a + // priority queue of lists of packets to be retransmitted, where list x + // contains all packets that have been retransmitted x times. RetransmissionTimeouts retransmission_timeouts_; + // Map from sequence number to the retransmission info. + RetransmissionMap retransmission_map_; + // True while OnRetransmissionTimeout is running to prevent // SetRetransmissionAlarm from being called erroneously. bool handling_retransmission_timeout_; diff --git a/net/quic/quic_connection_helper.cc b/net/quic/quic_connection_helper.cc index 18fd69d..3af51ac 100644 --- a/net/quic/quic_connection_helper.cc +++ b/net/quic/quic_connection_helper.cc @@ -9,8 +9,6 @@ #include "base/task_runner.h" #include "base/time.h" #include "net/base/io_buffer.h" -#include "net/quic/congestion_control/quic_receipt_metrics_collector.h" -#include "net/quic/congestion_control/quic_send_scheduler.h" #include "net/quic/quic_utils.h" namespace net { diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc index 7bd6ff8..f64f867 100644 --- a/net/quic/quic_connection_helper_test.cc +++ b/net/quic/quic_connection_helper_test.cc @@ -34,8 +34,8 @@ class TestConnection : public QuicConnection { QuicConnectionPeer::SendAck(this); } - void SetScheduler(QuicSendScheduler* scheduler) { - QuicConnectionPeer::SetScheduler(this, scheduler); + void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) { + QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm); } }; @@ -99,12 +99,12 @@ class QuicConnectionHelperTest : public ::testing::Test { runner_ = new TestTaskRunner(&clock_); helper_.reset(new QuicConnectionHelper(runner_.get(), &clock_, &random_generator_, socket)); - scheduler_ = new testing::StrictMock<MockScheduler>(); - EXPECT_CALL(*scheduler_, TimeUntilSend(_)). + send_algorithm_ = new testing::StrictMock<MockSendAlgorithm>(); + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_)). WillRepeatedly(testing::Return(QuicTime::Delta::Zero())); connection_.reset(new TestConnection(guid_, IPEndPoint(), helper_.get())); connection_->set_visitor(&visitor_); - connection_->SetScheduler(scheduler_); + connection_->SetSendAlgorithm(send_algorithm_); } // Returns a newly created packet to send kData on stream 1. @@ -125,7 +125,7 @@ class QuicConnectionHelperTest : public ::testing::Test { QuicCongestionFeedbackFrame feedback; feedback.type = kTCP; feedback.tcp.accumulated_number_of_lost_packets = 0; - feedback.tcp.receive_window = 16000; + feedback.tcp.receive_window = 16000 << 4; QuicFrames frames; frames.push_back(QuicFrame(&ack)); @@ -150,7 +150,7 @@ class QuicConnectionHelperTest : public ::testing::Test { return ConstructPacket(header_, QuicFrame(&close)); } - testing::StrictMock<MockScheduler>* scheduler_; + testing::StrictMock<MockSendAlgorithm>* send_algorithm_; scoped_refptr<TestTaskRunner> runner_; scoped_ptr<QuicConnectionHelper> helper_; scoped_array<MockWrite> mock_writes_; @@ -225,7 +225,7 @@ TEST_F(QuicConnectionHelperTest, SetAckAlarm) { EXPECT_EQ(base::TimeDelta::FromMicroseconds(delta.ToMicroseconds()), runner_->GetPostedTasks()[1].delay); - EXPECT_CALL(*scheduler_, SentPacket(1, _, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(1, _, false)); runner_->RunNextTask(); EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.Now()); } @@ -269,7 +269,7 @@ TEST_F(QuicConnectionHelperTest, ResetAckAlarm) { // Verify that the ack alarm task has been re-posted. ASSERT_EQ(2u, runner_->GetPostedTasks().size()); - EXPECT_CALL(*scheduler_, SentPacket(1, _, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(1, _, false)); runner_->RunNextTask(); EXPECT_EQ(QuicTime::Zero().Add(delta2), clock_.Now()); } @@ -283,10 +283,10 @@ TEST_F(QuicConnectionHelperTest, TestRetransmission) { QuicTime::Delta::FromMilliseconds(500); QuicTime start = clock_.Now(); - EXPECT_CALL(*scheduler_, SentPacket(1, _, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(1, _, false)); // Send a packet. connection_->SendStreamData(1, kData, 0, false); - EXPECT_CALL(*scheduler_, 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(); @@ -304,7 +304,7 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) { EXPECT_EQ(base::TimeDelta::FromMicroseconds(kDefaultTimeoutUs), runner_->GetPostedTasks().front().delay); - EXPECT_CALL(*scheduler_, 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)); @@ -348,7 +348,7 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { // 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(*scheduler_, SentPacket(1, _, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(1, _, false)); // Send an ack so we don't set the retransmission alarm. connection_->SendAck(); @@ -362,7 +362,7 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { // This time, we should time out. EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); - EXPECT_CALL(*scheduler_, SentPacket(2, _, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(2, _, false)); runner_->RunNextTask(); EXPECT_EQ(kDefaultTimeoutUs + 5000, clock_.Now().ToMicroseconds()); EXPECT_FALSE(connection_->connected()); @@ -374,16 +374,16 @@ TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) { Initialize(); // Test that if we send a packet with a delay, it ends up queued. - EXPECT_CALL(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::FromMicroseconds(1))); connection_->SendStreamData(1, kData, 0, false); - EXPECT_CALL(*scheduler_, SentPacket(1, _, false)); + 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(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::Zero())); EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true)); runner_->RunNextTask(); diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index 2a91326..1dbde23 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -5,8 +5,8 @@ #include "net/quic/quic_connection.h" #include "net/base/net_errors.h" -#include "net/quic/congestion_control/quic_receipt_metrics_collector.h" -#include "net/quic/congestion_control/quic_send_scheduler.h" +#include "net/quic/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" @@ -23,8 +23,10 @@ using testing::_; using testing::AnyNumber; using testing::Between; using testing::ContainerEq; +using testing::InSequence; using testing::Return; using testing::StrictMock; +using testing::SaveArg; namespace net { namespace test { @@ -33,11 +35,10 @@ namespace { const char data1[] = "foo"; const char data2[] = "bar"; -class TestCollector : public QuicReceiptMetricsCollector { +class TestReceiveAlgorithm : public ReceiveAlgorithmInterface { public: - explicit TestCollector(QuicCongestionFeedbackFrame* feedback) - : QuicReceiptMetricsCollector(&clock_, kFixRate), - feedback_(feedback) { + explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback) + : feedback_(feedback) { } bool GenerateCongestionFeedback( @@ -50,13 +51,12 @@ class TestCollector : public QuicReceiptMetricsCollector { } MOCK_METHOD4(RecordIncomingPacket, - void(size_t, QuicPacketSequenceNumber, QuicTime, bool)); + void(QuicByteCount, QuicPacketSequenceNumber, QuicTime, bool)); private: - MockClock clock_; QuicCongestionFeedbackFrame* feedback_; - DISALLOW_COPY_AND_ASSIGN(TestCollector); + DISALLOW_COPY_AND_ASSIGN(TestReceiveAlgorithm); }; class TestConnectionHelper : public QuicConnectionHelperInterface { @@ -87,7 +87,7 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { QuicEncrypter::Create(kNULL)); FramerVisitorCapturingAcks visitor; framer.set_visitor(&visitor); - EXPECT_TRUE(framer.ProcessPacket(IPEndPoint(), IPEndPoint(), packet)); + EXPECT_TRUE(framer.ProcessPacket(packet)); header_ = *visitor.header(); if (visitor.ack()) { ack_.reset(new QuicAckFrame(*visitor.ack())); @@ -166,22 +166,15 @@ class TestConnection : public QuicConnection { QuicConnectionPeer::SendAck(this); } - void SetCollector(QuicReceiptMetricsCollector* collector) { - QuicConnectionPeer::SetCollector(this, collector); + void SetReceiveAlgorithm(TestReceiveAlgorithm* receive_algorithm) { + QuicConnectionPeer::SetReceiveAlgorithm(this, receive_algorithm); } - void SetScheduler(QuicSendScheduler* scheduler) { - QuicConnectionPeer::SetScheduler(this, scheduler); + void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) { + QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm); } - bool SendPacket(QuicPacketSequenceNumber sequence_number, - QuicPacket* packet, - bool should_retransmit, - bool force, - bool is_retransmission) { - return QuicConnection::SendPacket( - sequence_number, packet, should_retransmit, force, is_retransmission); - } + using QuicConnection::SendOrQueuePacket; private: DISALLOW_COPY_AND_ASSIGN(TestConnection); @@ -193,21 +186,21 @@ class QuicConnectionTest : public ::testing::Test { : guid_(42), framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)), creator_(guid_, &framer_), - scheduler_(new StrictMock<MockScheduler>), + send_algorithm_(new StrictMock<MockSendAlgorithm>), helper_(new TestConnectionHelper(&clock_, &random_generator_)), connection_(guid_, IPEndPoint(), helper_.get()), frame1_(1, false, 0, data1), frame2_(1, false, 3, data2), accept_packet_(true) { connection_.set_visitor(&visitor_); - connection_.SetScheduler(scheduler_); + connection_.SetSendAlgorithm(send_algorithm_); // Simplify tests by not sending feedback unless specifically configured. SetFeedback(NULL); - EXPECT_CALL(*scheduler_, TimeUntilSend(_)).WillRepeatedly(Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_)).WillRepeatedly(Return( QuicTime::Delta::Zero())); - EXPECT_CALL(*collector_, + EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)).Times(AnyNumber()); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(AnyNumber()); } QuicAckFrame* outgoing_ack() { @@ -299,30 +292,25 @@ class QuicConnectionTest : public ::testing::Test { void SendStreamDataToPeer(QuicStreamId id, StringPiece data, QuicStreamOffset offset, bool fin, QuicPacketSequenceNumber* last_packet) { - EXPECT_CALL(*scheduler_, 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(*scheduler_, SentPacket(_, _, _)).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(AnyNumber()); } void SendAckPacketToPeer() { - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(1); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(1); connection_.SendAck(); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(AnyNumber()); } - void ProcessAckPacket(QuicAckFrame* frame, bool expect_success = true) { - if (expect_success) { - EXPECT_CALL(*scheduler_, OnIncomingAckFrame(_)); - } + void ProcessAckPacket(QuicAckFrame* frame) { QuicFrames frames; frames.push_back(QuicFrame(frame)); - size_t num_serialized; - - PacketPair pair = creator_.SerializeFrames(frames, &num_serialized); + PacketPair pair = creator_.SerializeAllFrames(frames); scoped_ptr<QuicPacket> packet(pair.second); scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); @@ -368,16 +356,16 @@ class QuicConnectionTest : public ::testing::Test { } void SetFeedback(QuicCongestionFeedbackFrame* feedback) { - collector_ = new TestCollector(feedback); - connection_.SetCollector(collector_); + receive_algorithm_ = new TestReceiveAlgorithm(feedback); + connection_.SetReceiveAlgorithm(receive_algorithm_); } QuicGuid guid_; QuicFramer framer_; QuicPacketCreator creator_; - MockScheduler* scheduler_; - TestCollector* collector_; + MockSendAlgorithm* send_algorithm_; + TestReceiveAlgorithm* receive_algorithm_; MockClock clock_; MockRandom random_generator_; scoped_ptr<TestConnectionHelper> helper_; @@ -489,19 +477,20 @@ TEST_F(QuicConnectionTest, RejectPacketTooFarOut) { TEST_F(QuicConnectionTest, TruncatedAck) { EXPECT_CALL(visitor_, OnAck(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); for (int i = 0; i < 200; ++i) { SendStreamDataToPeer(1, "foo", i * 3, false, NULL); } QuicAckFrame frame(0, 1); frame.received_info.RecordReceived(193); - ProcessAckPacket(&frame, true); + ProcessAckPacket(&frame); EXPECT_TRUE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); frame.received_info.missing_packets.erase(192); - ProcessAckPacket(&frame, true); + ProcessAckPacket(&frame); EXPECT_FALSE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); } @@ -520,20 +509,21 @@ TEST_F(QuicConnectionTest, LeastUnackedLower) { creator_.set_sequence_number(1); QuicAckFrame frame2(0, 1); // The scheduler will not process out of order acks. - ProcessAckPacket(&frame2, false); + ProcessAckPacket(&frame2); // Now claim it's one, but set the ordering so it was sent "after" the first // one. This should cause a connection error. EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)); creator_.set_sequence_number(7); - ProcessAckPacket(&frame2, false); + ProcessAckPacket(&frame2); } TEST_F(QuicConnectionTest, LargestObservedLower) { SendStreamDataToPeer(1, "foo", 0, false, NULL); SendStreamDataToPeer(1, "bar", 3, false, NULL); SendStreamDataToPeer(1, "eep", 6, false, NULL); + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); // Start out saying the largest observed is 2. QuicAckFrame frame(2, 0); @@ -543,16 +533,16 @@ TEST_F(QuicConnectionTest, LargestObservedLower) { // Now change it to 1, and it should cause a connection error. QuicAckFrame frame2(1, 0); EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); - ProcessAckPacket(&frame2, false); + ProcessAckPacket(&frame2); } TEST_F(QuicConnectionTest, LeastUnackedGreaterThanPacketSequenceNumber) { EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*scheduler_, 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); - ProcessAckPacket(&frame, false); + ProcessAckPacket(&frame); } TEST_F(QuicConnectionTest, @@ -562,18 +552,18 @@ TEST_F(QuicConnectionTest, SendStreamDataToPeer(1, "eep", 6, false, NULL); EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)); QuicAckFrame frame(0, 1); frame.received_info.missing_packets.insert(3); - ProcessAckPacket(&frame, false); + ProcessAckPacket(&frame); } TEST_F(QuicConnectionTest, AckUnsentData) { // Ack a packet which has not been sent. EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)); QuicAckFrame frame(1, 0); - ProcessAckPacket(&frame, false); + ProcessAckPacket(&frame); } TEST_F(QuicConnectionTest, AckAll) { @@ -585,6 +575,7 @@ TEST_F(QuicConnectionTest, AckAll) { } TEST_F(QuicConnectionTest, BasicSending) { + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(6); QuicPacketSequenceNumber last_packet; SendStreamDataToPeer(1, "foo", 0, false, &last_packet); // Packet 1 EXPECT_EQ(1u, last_packet); @@ -635,7 +626,39 @@ TEST_F(QuicConnectionTest, BasicSending) { EXPECT_EQ(9u, last_ack()->sent_info.least_unacked); } +TEST_F(QuicConnectionTest, FECSending) { + // Limit to one byte per packet. + size_t ciphertext_size = NullEncrypter().GetCiphertextSize(1); + connection_.options()->max_packet_length = + ciphertext_size + QuicUtils::StreamFramePacketOverhead(1); + // And send FEC every two packets. + 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 the FEC group to be closed after SendStreamData. + EXPECT_FALSE(creator_.ShouldSendFec(true)); +} + +TEST_F(QuicConnectionTest, FECQueueing) { + // Limit to one byte per packet. + size_t ciphertext_size = NullEncrypter().GetCiphertextSize(1); + connection_.options()->max_packet_length = + ciphertext_size + QuicUtils::StreamFramePacketOverhead(1); + // And send FEC every two packets. + connection_.options()->max_packets_per_fec_group = 2; + + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + helper_->set_blocked(true); + connection_.SendStreamData(1, "food", 0, false); + 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, RetransmitOnNack) { + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); QuicPacketSequenceNumber last_packet; SendStreamDataToPeer(1, "foo", 0, false, &last_packet); // Packet 1 SendStreamDataToPeer(1, "foos", 3, false, &last_packet); // Packet 2 @@ -664,11 +687,29 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) { ProcessAckPacket(&nack_two); // The third nack should trigger a retransimission. - EXPECT_CALL(*scheduler_, SentPacket(_, 37, true)).Times(1); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 37, true)).Times(1); ProcessAckPacket(&nack_two); } +TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) { + 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); + QuicAckFrame frame(1, largest_observed); + frame.received_info.missing_packets.insert(largest_observed); + ProcessAckPacket(&frame); + // Second udp packet will force an ack frame. + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, false)); + ProcessAckPacket(&frame); + // Third nack should retransmit the largest observed packet. + EXPECT_CALL(*send_algorithm_, SentPacket(_, packet_size, true)); + ProcessAckPacket(&frame); +} + TEST_F(QuicConnectionTest, LimitPacketsPerNack) { + EXPECT_CALL(*send_algorithm_, OnIncomingAck(12, _, _)).Times(1); int offset = 0; // Send packets 1 to 12 for (int i = 0; i < 12; ++i) { @@ -689,19 +730,20 @@ TEST_F(QuicConnectionTest, LimitPacketsPerNack) { // Nack three times. ProcessAckPacket(&nack); // The second call will trigger an ack. - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(1); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(1); ProcessAckPacket(&nack); // The third call should trigger retransmitting 10 packets. - EXPECT_CALL(*scheduler_, 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(*scheduler_, 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); QuicPacketSequenceNumber last_packet; SendStreamDataToPeer(1u, "foo", 0, false, &last_packet); // Packet 1 EXPECT_EQ(1u, last_packet); @@ -741,6 +783,7 @@ TEST_F(QuicConnectionTest, MultipleAcks) { } TEST_F(QuicConnectionTest, DontLatchUnackedPacket) { + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1); SendStreamDataToPeer(1, "foo", 0, false, NULL); // Packet 1; SendAckPacketToPeer(); // Packet 2 @@ -814,14 +857,89 @@ TEST_F(QuicConnectionTest, TestRetransmit) { EXPECT_EQ(default_retransmission_time, helper_->retransmission_alarm()); // Simulate the retransimission alarm firing clock_.AdvanceTime(kDefaultRetransmissionTime); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); - connection_.MaybeRetransmitPacket(1); + 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); } -// TODO(rch): Enable after we get non-blocking sockets. -TEST_F(QuicConnectionTest, DISABLED_TestQueued) { +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); + QuicByteCount second_packet_size; + EXPECT_CALL(*send_algorithm_, SentPacket(_,_,_)).WillOnce( + SaveArg<1>(&second_packet_size)); + connection_.SendStreamData(1, "second_packet", 12, false); + 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, _)); + } + connection_.OnRetransmissionTimeout(); +} + +TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) { + QuicPacketSequenceNumber original_sequence_number; + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, false)).WillOnce( + SaveArg<0>(&original_sequence_number)); + connection_.SendStreamData(1, "foo", 0, false); + EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission( + &connection_, original_sequence_number)); + EXPECT_EQ(0u, QuicConnectionPeer::GetRetransmissionCount( + &connection_, original_sequence_number)); + // 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)); + connection_.OnRetransmissionTimeout(); + EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission( + &connection_, original_sequence_number)); + EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission( + &connection_, rto_sequence_number)); + EXPECT_EQ(1u, QuicConnectionPeer::GetRetransmissionCount( + &connection_, rto_sequence_number)); + // Once by explicit nack. + 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)); + 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); + for (int i = 0; i < 3; i++) { + ProcessAckPacket(&ack); + } + EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission( + &connection_, rto_sequence_number)); + EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission( + &connection_, nack_sequence_number)); + EXPECT_EQ(2u, QuicConnectionPeer::GetRetransmissionCount( + &connection_, nack_sequence_number)); +} + +TEST_F(QuicConnectionTest, SetRTOAfterWritingToSocket) { + helper_->set_blocked(true); + connection_.SendStreamData(1, "foo", 0, false); + // Make sure that RTO is not started when the packet is queued. + EXPECT_EQ(0u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_)); + + // Test that RTO is started once we write to the socket. + helper_->set_blocked(false); + EXPECT_CALL(visitor_, OnCanWrite()); + connection_.OnCanWrite(); + EXPECT_EQ(1u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_)); +} + +TEST_F(QuicConnectionTest, TestQueued) { EXPECT_EQ(0u, connection_.NumQueuedPackets()); helper_->set_blocked(true); connection_.SendStreamData(1, "foo", 0, false); @@ -861,33 +979,32 @@ TEST_F(QuicConnectionTest, NoQuicCongestionFeedbackFrame) { TEST_F(QuicConnectionTest, WithQuicCongestionFeedbackFrame) { QuicCongestionFeedbackFrame info; info.type = kFixRate; - info.fix_rate.bitrate_in_bytes_per_second = 123; + info.fix_rate.bitrate = QuicBandwidth::FromBytesPerSecond(123); SetFeedback(&info); SendAckPacketToPeer(); EXPECT_EQ(kFixRate, last_feedback()->type); - EXPECT_EQ(info.fix_rate.bitrate_in_bytes_per_second, - last_feedback()->fix_rate.bitrate_in_bytes_per_second); + EXPECT_EQ(info.fix_rate.bitrate, last_feedback()->fix_rate.bitrate); } TEST_F(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) { SendAckPacketToPeer(); - EXPECT_CALL(*collector_, RecordIncomingPacket(_, _, _, _)); + EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)); ProcessPacket(1); } TEST_F(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) { SendAckPacketToPeer(); // Process an FEC packet, and revive the missing data packet - // but only contact the collector once. - EXPECT_CALL(*collector_, RecordIncomingPacket(_, _, _, _)); + // but only contact the receive_algorithm once. + EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)); ProcessFecPacket(2, 1, true); } TEST_F(QuicConnectionTest, InitialTimeout) { EXPECT_TRUE(connection_.connected()); EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)); QuicTime default_timeout = clock_.Now().Add( QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)); @@ -924,7 +1041,7 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) { // This time, we should time out. EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); EXPECT_EQ(default_timeout.Add(QuicTime::Delta::FromMilliseconds(5)), clock_.Now()); @@ -936,77 +1053,83 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) { TEST_F(QuicConnectionTest, SendScheduler) { // Test that if we send a packet without delay, it is not queued. QuicPacket* packet = ConstructDataPacket(1, 0); - EXPECT_CALL(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::Zero())); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); - connection_.SendPacket(1, packet, true, false, false); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)); + connection_.SendOrQueuePacket(1, packet, false); 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(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::FromMicroseconds(1))); - EXPECT_CALL(*scheduler_, SentPacket(1, _, _)).Times(0); - connection_.SendPacket(1, packet, true, false, false); + EXPECT_CALL(*send_algorithm_, SentPacket(1, _, _)).Times(0); + connection_.SendOrQueuePacket(1, packet, false); 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(*scheduler_, TimeUntilSend(true)).Times(0); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); - connection_.SendPacket(1, packet, true, true, false); + EXPECT_CALL(*send_algorithm_, TimeUntilSend(true)).Times(0); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)); + connection_.SendOrQueuePacket(1, packet, true); EXPECT_EQ(0u, connection_.NumQueuedPackets()); } -// TODO(rch): Enable after we get non-blocking sockets. -TEST_F(QuicConnectionTest, DISABLED_SendSchedulerEAGAIN) { +TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) { QuicPacket* packet = ConstructDataPacket(1, 0); helper_->set_blocked(true); - EXPECT_CALL(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::Zero())); - EXPECT_CALL(*scheduler_, SentPacket(1, _, _)).Times(0); - connection_.SendPacket(1, packet, true, false, false); + EXPECT_CALL(*send_algorithm_, SentPacket(1, _, _)).Times(0); + connection_.SendOrQueuePacket(1, packet, false); 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(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::FromMicroseconds(1))); - connection_.SendPacket(1, packet, true, false, false); + connection_.SendOrQueuePacket(1, packet, false); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Advance the clock to fire the alarm, and configure the scheduler // to permit the packet to be sent. - EXPECT_CALL(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::Zero())); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); + 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_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. - QuicPacket* packet = ConstructDataPacket(1, 0); - EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(true)).WillOnce(testing::Return( QuicTime::Delta::FromMicroseconds(1))); - connection_.SendPacket(1, packet, true, false, true); + 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(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(true)).WillOnce(testing::Return( QuicTime::Delta::Zero())); // Ensure the scheduler is notified this is a retransmit. - EXPECT_CALL(*scheduler_, SentPacket(1, _, true)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, true)); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); EXPECT_CALL(visitor_, OnCanWrite()); connection_.OnCanWrite(); @@ -1015,30 +1138,31 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) { TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) { QuicPacket* packet = ConstructDataPacket(1, 0); - EXPECT_CALL(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::FromMicroseconds(1))); - connection_.SendPacket(1, packet, true, false, false); + connection_.SendOrQueuePacket(1, packet, false); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Attempt to send another packet and make sure that it gets queued. packet = ConstructDataPacket(2, 0); - connection_.SendPacket(2, packet, true, false, false); + connection_.SendOrQueuePacket(2, packet, false); EXPECT_EQ(2u, connection_.NumQueuedPackets()); } TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { QuicPacket* packet = ConstructDataPacket(1, 0); - EXPECT_CALL(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::FromMicroseconds(10))); - connection_.SendPacket(1, packet, true, false, false); + connection_.SendOrQueuePacket(1, packet, false); 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(*scheduler_, TimeUntilSend(false)).WillRepeatedly(testing::Return( - QuicTime::Delta::Zero())); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(false)).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)); EXPECT_CALL(visitor_, OnCanWrite()); ProcessAckPacket(&frame); @@ -1049,15 +1173,15 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { QuicPacket* packet = ConstructDataPacket(1, 0); - EXPECT_CALL(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::FromMicroseconds(10))); - connection_.SendPacket(1, packet, true, false, false); + connection_.SendOrQueuePacket(1, packet, false); 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(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::FromMicroseconds(1))); ProcessAckPacket(&frame); @@ -1066,9 +1190,9 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { TEST_F(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) { QuicPacket* packet = ConstructDataPacket(1, 0); - EXPECT_CALL(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::FromMicroseconds(10))); - connection_.SendPacket(1, packet, true, false, false); + connection_.SendOrQueuePacket(1, packet, false); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // OnCanWrite should not send the packet (because of the delay) @@ -1085,7 +1209,7 @@ TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { ciphertext_size + QuicUtils::StreamFramePacketOverhead(1); // Queue the first packet. - EXPECT_CALL(*scheduler_, TimeUntilSend(false)).WillOnce(testing::Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(false)).WillOnce(testing::Return( QuicTime::Delta::FromMicroseconds(10))); EXPECT_EQ(1u, connection_.SendStreamData( 1, "EnoughDataToQueue", 0, false).bytes_consumed); @@ -1099,16 +1223,16 @@ TEST_F(QuicConnectionTest, LoopThroughSendingPackets) { ciphertext_size + QuicUtils::StreamFramePacketOverhead(1); // Queue the first packet. - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(17); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(17); EXPECT_EQ(17u, connection_.SendStreamData( 1, "EnoughDataToQueue", 0, false).bytes_consumed); } TEST_F(QuicConnectionTest, NoAckForClose) { ProcessPacket(1); - EXPECT_CALL(*scheduler_, OnIncomingAckFrame(_)); + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(0); EXPECT_CALL(visitor_, ConnectionClose(QUIC_CLIENT_GOING_AWAY, true)); - EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(0); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _)).Times(0); ProcessClosePacket(2, 0); } @@ -1118,8 +1242,8 @@ TEST_F(QuicConnectionTest, SendWhenDisconnected) { connection_.CloseConnection(QUIC_CLIENT_GOING_AWAY, false); EXPECT_FALSE(connection_.connected()); QuicPacket* packet = ConstructDataPacket(1, 0); - EXPECT_CALL(*scheduler_, SentPacket(1, _, _)).Times(0); - connection_.SendPacket(1, packet, true, false, false); + EXPECT_CALL(*send_algorithm_, SentPacket(1, _, _)).Times(0); + connection_.SendOrQueuePacket(1, packet, false); } TEST_F(QuicConnectionTest, PublicReset) { diff --git a/net/quic/quic_crypto_client_stream_test.cc b/net/quic/quic_crypto_client_stream_test.cc index 2ef645b..adbf139 100644 --- a/net/quic/quic_crypto_client_stream_test.cc +++ b/net/quic/quic_crypto_client_stream_test.cc @@ -84,8 +84,7 @@ void TestMockHelper::CheckClientHelloPacket( QuicEncrypter::Create(kNULL)); TestQuicVisitor quic_visitor; quic_framer.set_visitor(&quic_visitor); - ASSERT_TRUE(quic_framer.ProcessPacket(IPEndPoint(), IPEndPoint(), - packet)); + ASSERT_TRUE(quic_framer.ProcessPacket(packet)); EXPECT_EQ(kCryptoStreamId, quic_visitor.frame()->stream_id); EXPECT_FALSE(quic_visitor.frame()->fin); EXPECT_EQ(0u, quic_visitor.frame()->offset); diff --git a/net/quic/quic_data_writer.cc b/net/quic/quic_data_writer.cc index 3bbad4e..f90670f 100644 --- a/net/quic/quic_data_writer.cc +++ b/net/quic/quic_data_writer.cc @@ -99,6 +99,10 @@ bool QuicDataWriter::WriteBytes(const void* data, size_t data_len) { } void QuicDataWriter::WritePadding() { + DCHECK_LE(length_, capacity_); + if (length_ > capacity_) { + return; + } memset(buffer_ + length_, 0x00, capacity_ - length_); length_ = capacity_; } diff --git a/net/quic/quic_fec_group.cc b/net/quic/quic_fec_group.cc index 2a2695d..af976a3 100644 --- a/net/quic/quic_fec_group.cc +++ b/net/quic/quic_fec_group.cc @@ -26,10 +26,6 @@ QuicFecGroup::QuicFecGroup() QuicFecGroup::~QuicFecGroup() {} -size_t QuicFecGroup::GroupSize() const { - return max_protected_packet_ - min_protected_packet_ + 1; -} - bool QuicFecGroup::Update(const QuicPacketHeader& header, StringPiece decrypted_payload) { if (received_packets_.count(header.packet_sequence_number) != 0) { diff --git a/net/quic/quic_fec_group.h b/net/quic/quic_fec_group.h index 67cba64..14fdec8 100644 --- a/net/quic/quic_fec_group.h +++ b/net/quic/quic_fec_group.h @@ -21,12 +21,6 @@ class NET_EXPORT_PRIVATE QuicFecGroup { QuicFecGroup(); ~QuicFecGroup(); - QuicPacketSequenceNumber min_protected_packet() const { - return min_protected_packet_; - } - - size_t GroupSize() const; - // Updates the FEC group based on the delivery of a data packet. // Returns false if this packet has already been seen, true otherwise. bool Update(const QuicPacketHeader& header, @@ -61,6 +55,14 @@ class NET_EXPORT_PRIVATE QuicFecGroup { return base::StringPiece(parity_, parity_len_); } + QuicPacketSequenceNumber min_protected_packet() const { + return min_protected_packet_; + } + + size_t NumReceivedPackets() const { + return received_packets_.size(); + } + private: bool UpdateParity(base::StringPiece payload); // Returns the number of missing packets, or size_t max if the number diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index c9392a6..a794701 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -15,6 +15,7 @@ using base::StringPiece; using std::make_pair; using std::map; using std::numeric_limits; +using std::string; namespace net { @@ -61,74 +62,53 @@ bool CanTruncate(const QuicFrame& frame) { return false; } -QuicPacket* QuicFramer::ConstructFrameDataPacket( - const QuicPacketHeader& header, - const QuicFrames& frames) { - size_t num_consumed = 0; - QuicPacket* packet = - ConstructMaxFrameDataPacket(header, frames, &num_consumed); - DCHECK_EQ(frames.size(), num_consumed); - return packet; +size_t QuicFramer::GetSerializedFrameLength( + const QuicFrame& frame, size_t free_bytes, bool first_frame) { + if (frame.type == PADDING_FRAME) { + // PADDING implies end of packet. + return free_bytes; + } + size_t frame_len = kFrameTypeSize; + frame_len += ComputeFramePayloadLength(frame); + if (frame_len > free_bytes) { + // Only truncate the first frame in a packet, so if subsequent ones go + // over, stop including more frames. + if (!first_frame) { + return 0; + } + if (CanTruncate(frame)) { + // Truncate the frame so the packet will not exceed kMaxPacketSize. + // Note that we may not use every byte of the writer in this case. + DLOG(INFO) << "Truncating large frame"; + return free_bytes; + } + } + return frame_len; } -QuicPacket* QuicFramer::ConstructMaxFrameDataPacket( - const QuicPacketHeader& header, - const QuicFrames& frames, - size_t* num_consumed) { - DCHECK(!frames.empty()); - // Compute the length of the packet. We use "magic numbers" here because - // sizeof(member_) is not necessarily the same as sizeof(member_wire_format). +QuicPacket* QuicFramer::ConstructFrameDataPacket(const QuicPacketHeader& header, + const QuicFrames& frames) { const size_t max_plaintext_size = GetMaxPlaintextSize(kMaxPacketSize); - size_t len = kPacketHeaderSize; - bool truncating = false; + size_t packet_size = kPacketHeaderSize; for (size_t i = 0; i < frames.size(); ++i) { - if (frames[i].type == PADDING_FRAME) { - // PADDING implies end of packet so make sure we don't have - // more frames on the list. - DCHECK_EQ(i, frames.size() - 1); - len = max_plaintext_size; - *num_consumed = i + 1; - break; - } - size_t frame_len = 1; // Space for the 8 bit type. - frame_len += ComputeFramePayloadLength(frames[i]); - if (len + frame_len > max_plaintext_size) { - // Only truncate the first frame in a packet, so if subsequent ones go - // over, stop including more frames. - if (i > 0) { - break; - } - if (CanTruncate(frames[0])) { - // Truncate the frame so the packet will not exceed kMaxPacketSize. - // Note that we may not use every byte of the writer in this case. - len = max_plaintext_size; - *num_consumed = 1; - truncating = true; - DLOG(INFO) << "Truncating large frame"; - break; - } else { - return NULL; - } - } - len += frame_len; - *num_consumed = i + 1; - } - if (truncating) { - DCHECK_EQ(1u, *num_consumed); + DCHECK_LE(packet_size, max_plaintext_size); + const size_t frame_size = GetSerializedFrameLength( + frames[i], max_plaintext_size - packet_size, i == 0); + DCHECK(frame_size); + packet_size += frame_size; } + return ConstructFrameDataPacket(header, frames, packet_size); +} - QuicDataWriter writer(len); - +QuicPacket* QuicFramer::ConstructFrameDataPacket(const QuicPacketHeader& header, + const QuicFrames& frames, + size_t packet_size) { + QuicDataWriter writer(packet_size); if (!WritePacketHeader(header, &writer)) { return NULL; } - // frame count - if (*num_consumed > 256u) { - return NULL; - } - - for (size_t i = 0; i < *num_consumed; ++i) { + for (size_t i = 0; i < frames.size(); ++i) { const QuicFrame& frame = frames[i]; if (!writer.WriteUInt8(frame.type)) { return NULL; @@ -171,9 +151,11 @@ QuicPacket* QuicFramer::ConstructMaxFrameDataPacket( } } - DCHECK(truncating || len == writer.length()); // Save the length before writing, because take clears it. - len = writer.length(); + const size_t len = writer.length(); + // Less than or equal because truncated acks end up with max_plaintex_size + // length, even though they're typically slightly shorter. + DCHECK_LE(len, packet_size); QuicPacket* packet = QuicPacket::NewDataPacket(writer.take(), len, true); if (fec_builder_) { @@ -231,10 +213,7 @@ QuicEncryptedPacket* QuicFramer::ConstructPublicResetPacket( return new QuicEncryptedPacket(writer.take(), len, true); } -// TODO(satyamshekhar): Framer doesn't need addresses. Get rid of them. -bool QuicFramer::ProcessPacket(const IPEndPoint& self_address, - const IPEndPoint& peer_address, - const QuicEncryptedPacket& packet) { +bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) { DCHECK(!reader_.get()); reader_.reset(new QuicDataReader(packet.data(), packet.length())); @@ -250,7 +229,7 @@ bool QuicFramer::ProcessPacket(const IPEndPoint& self_address, if (public_header.flags & PACKET_PUBLIC_FLAGS_RST) { rv = ProcessPublicResetPacket(public_header); } else { - rv = ProcessDataPacket(public_header, self_address, peer_address, packet); + rv = ProcessDataPacket(public_header, packet); } reader_.reset(NULL); @@ -259,10 +238,8 @@ bool QuicFramer::ProcessPacket(const IPEndPoint& self_address, bool QuicFramer::ProcessDataPacket( const QuicPacketPublicHeader& public_header, - const IPEndPoint& self_address, - const IPEndPoint& peer_address, const QuicEncryptedPacket& packet) { - visitor_->OnPacket(self_address, peer_address); + visitor_->OnPacket(); QuicPacketHeader header(public_header); if (!ProcessPacketHeader(&header, packet)) { @@ -314,7 +291,6 @@ bool QuicFramer::ProcessPublicResetPacket( set_detailed_error("Unable to read rejected sequence number."); return false; } - visitor_->OnPublicResetPacket(packet); return true; } @@ -400,13 +376,6 @@ QuicPacketSequenceNumber QuicFramer::CalculatePacketSequenceNumberFromWire( next_epoch + packet_sequence_number)); } -/* static */ -bool QuicFramer::ReadGuidFromPacket(const QuicEncryptedPacket& packet, - QuicGuid* guid) { - QuicDataReader reader(packet.data(), packet.length()); - return reader.ReadUInt64(guid); -} - bool QuicFramer::ProcessPublicHeader(QuicPacketPublicHeader* public_header) { if (!reader_->ReadUInt64(&public_header->guid)) { set_detailed_error("Unable to read GUID."); @@ -428,6 +397,13 @@ bool QuicFramer::ProcessPublicHeader(QuicPacketPublicHeader* public_header) { return true; } +// static +bool QuicFramer::ReadGuidFromPacket(const QuicEncryptedPacket& packet, + QuicGuid* guid) { + QuicDataReader reader(packet.data(), packet.length()); + return reader.ReadUInt64(guid); +} + bool QuicFramer::ProcessPacketHeader( QuicPacketHeader* header, const QuicEncryptedPacket& packet) { @@ -682,11 +658,12 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( break; } case kFixRate: { - CongestionFeedbackMessageFixRate* fix_rate = &frame->fix_rate; - if (!reader_->ReadUInt32(&fix_rate->bitrate_in_bytes_per_second)) { + uint32 bitrate = 0; + if (!reader_->ReadUInt32(&bitrate)) { set_detailed_error("Unable to read bitrate."); return false; } + frame->fix_rate.bitrate = QuicBandwidth::FromBytesPerSecond(bitrate); break; } case kTCP: { @@ -696,10 +673,13 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( "Unable to read accumulated number of lost packets."); return false; } - if (!reader_->ReadUInt16(&tcp->receive_window)) { + uint16 receive_window = 0; + if (!reader_->ReadUInt16(&receive_window)) { set_detailed_error("Unable to read receive window."); return false; } + // Simple bit packing, don't send the 4 least significant bits. + tcp->receive_window = static_cast<QuicByteCount>(receive_window) << 4; break; } default: @@ -1034,17 +1014,20 @@ bool QuicFramer::AppendQuicCongestionFeedbackFramePayload( case kFixRate: { const CongestionFeedbackMessageFixRate& fix_rate = frame.fix_rate; - if (!writer->WriteUInt32(fix_rate.bitrate_in_bytes_per_second)) { + if (!writer->WriteUInt32(fix_rate.bitrate.ToBytesPerSecond())) { return false; } break; } case kTCP: { const CongestionFeedbackMessageTCP& tcp = frame.tcp; + DCHECK_LE(tcp.receive_window, 1u << 20); + // Simple bit packing, don't send the 4 least significant bits. + uint16 receive_window = static_cast<uint16>(tcp.receive_window >> 4); if (!writer->WriteUInt16(tcp.accumulated_number_of_lost_packets)) { return false; } - if (!writer->WriteUInt16(tcp.receive_window)) { + if (!writer->WriteUInt16(receive_window)) { return false; } break; diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index d507630..297ca05 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -11,7 +11,6 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/string_piece.h" -#include "net/base/ip_endpoint.h" #include "net/base/net_export.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" @@ -39,8 +38,7 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface { // Called when a new packet has been received, before it // has been validated or processed. - virtual void OnPacket(const IPEndPoint& self_address, - const IPEndPoint& peer_address) = 0; + virtual void OnPacket() = 0; // Called when a public reset packet has been parsed but has not yet // been validated. @@ -135,9 +133,7 @@ class NET_EXPORT_PRIVATE QuicFramer { // single, complete UDP packet (not a frame of a packet). This packet // might be null padded past the end of the payload, which will be correctly // ignored. - bool ProcessPacket(const IPEndPoint& self_address, - const IPEndPoint& peer_address, - const QuicEncryptedPacket& packet); + bool ProcessPacket(const QuicEncryptedPacket& packet); // Pass a data packet that was revived from FEC data into the framer // for parsing. @@ -146,17 +142,24 @@ class NET_EXPORT_PRIVATE QuicFramer { bool ProcessRevivedPacket(const QuicPacketHeader& header, base::StringPiece payload); + // Returns the number of bytes added to the packet for the specified frame, + // and 0 if the frame doesn't fit. Includes the header size for the first + // frame. + 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. + // TODO(ianswett): Used for testing only. QuicPacket* ConstructFrameDataPacket(const QuicPacketHeader& header, const QuicFrames& frames); - // 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. Sets - // num_consumed to the number of frames consumed constructing the packet. - QuicPacket* ConstructMaxFrameDataPacket(const QuicPacketHeader& header, - const QuicFrames& frames, - size_t* num_consumed); + // 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. @@ -185,8 +188,6 @@ class NET_EXPORT_PRIVATE QuicFramer { friend class test::QuicFramerPeer; bool ProcessDataPacket(const QuicPacketPublicHeader& public_header, - const IPEndPoint& self_address, - const IPEndPoint& peer_address, const QuicEncryptedPacket& packet); bool ProcessPublicResetPacket(const QuicPacketPublicHeader& public_header); diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index 3c0497d..2b5cb38 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -103,11 +103,7 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { error_count_++; } - virtual void OnPacket(const IPEndPoint& self_address, - const IPEndPoint& peer_address) { - self_address_ = self_address; - peer_address_ = peer_address; - } + virtual void OnPacket() {} virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) { public_reset_packet_.reset(new QuicPublicResetPacket(packet)); @@ -171,8 +167,6 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { int revived_packets_; bool accept_packet_; - IPEndPoint self_address_; - IPEndPoint peer_address_; scoped_ptr<QuicPacketHeader> header_; scoped_ptr<QuicPublicResetPacket> public_reset_packet_; vector<QuicStreamFrame*> stream_frames_; @@ -189,9 +183,7 @@ class QuicFramerTest : public ::testing::Test { QuicFramerTest() : encrypter_(new test::TestEncrypter()), decrypter_(new test::TestDecrypter()), - framer_(decrypter_, encrypter_), - self_address_(IPAddressNumber(), 1), - peer_address_(IPAddressNumber(), 2) { + framer_(decrypter_, encrypter_) { framer_.set_visitor(&visitor_); } @@ -244,8 +236,7 @@ class QuicFramerTest : public ::testing::Test { string expected_error, QuicErrorCode error_code) { QuicEncryptedPacket encrypted(AsChars(packet), len, false); - EXPECT_FALSE(framer_.ProcessPacket(self_address_, peer_address_, - encrypted)) << "len: " << len; + EXPECT_FALSE(framer_.ProcessPacket(encrypted)) << "len: " << len; EXPECT_EQ(expected_error, framer_.detailed_error()) << "len: " << len; EXPECT_EQ(error_code, framer_.error()) << "len: " << len; } @@ -274,8 +265,6 @@ class QuicFramerTest : public ::testing::Test { test::TestDecrypter* decrypter_; QuicFramer framer_; test::TestQuicVisitor visitor_; - IPEndPoint self_address_; - IPEndPoint peer_address_; }; TEST_F(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearEpochStart) { @@ -380,7 +369,7 @@ TEST_F(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearNextMax) { TEST_F(QuicFramerTest, EmptyPacket) { char packet[] = { 0x00 }; QuicEncryptedPacket encrypted(packet, 0, false); - EXPECT_FALSE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error()); } @@ -403,7 +392,7 @@ TEST_F(QuicFramerTest, LargePacket) { memset(packet + kPacketHeaderSize, 0, kMaxPacketSize - kPacketHeaderSize + 1); QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_FALSE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); ASSERT_TRUE(visitor_.header_.get()); // Make sure we've parsed the packet header, so we can send an error. @@ -430,7 +419,7 @@ TEST_F(QuicFramerTest, PacketHeader) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_FALSE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), @@ -567,12 +556,10 @@ TEST_F(QuicFramerTest, PaddingFrame) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + 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()); - ASSERT_EQ(peer_address_, visitor_.peer_address_); - ASSERT_EQ(self_address_, visitor_.self_address_); ASSERT_EQ(0u, visitor_.stream_frames_.size()); EXPECT_EQ(0u, visitor_.ack_frames_.size()); @@ -621,13 +608,11 @@ TEST_F(QuicFramerTest, StreamFrame) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + 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()); - ASSERT_EQ(peer_address_, visitor_.peer_address_); - ASSERT_EQ(self_address_, visitor_.self_address_); ASSERT_EQ(1u, visitor_.stream_frames_.size()); EXPECT_EQ(0u, visitor_.ack_frames_.size()); @@ -692,12 +677,11 @@ TEST_F(QuicFramerTest, RejectPacket) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + 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()); - ASSERT_EQ(peer_address_, visitor_.peer_address_); ASSERT_EQ(0u, visitor_.stream_frames_.size()); EXPECT_EQ(0u, visitor_.ack_frames_.size()); @@ -787,7 +771,7 @@ TEST_F(QuicFramerTest, StreamFrameInFecGroup) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -797,7 +781,6 @@ TEST_F(QuicFramerTest, StreamFrameInFecGroup) { EXPECT_EQ(string(AsChars(packet) + kStartOfFecProtectedData, arraysize(packet) - kStartOfFecProtectedData), visitor_.fec_protected_payload_); - ASSERT_EQ(peer_address_, visitor_.peer_address_); ASSERT_EQ(1u, visitor_.stream_frames_.size()); EXPECT_EQ(0u, visitor_.ack_frames_.size()); @@ -839,7 +822,7 @@ TEST_F(QuicFramerTest, AckFrame) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -899,7 +882,7 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameTCP) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -912,7 +895,7 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameTCP) { ASSERT_EQ(kTCP, frame.type); EXPECT_EQ(0x0201, frame.tcp.accumulated_number_of_lost_packets); - EXPECT_EQ(0x0403, frame.tcp.receive_window); + EXPECT_EQ(0x4030u, frame.tcp.receive_window); // Now test framing boundaries for (size_t i = 0; i < 6; ++i) { @@ -971,7 +954,7 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1052,7 +1035,7 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameFixRate) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1064,7 +1047,7 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameFixRate) { *visitor_.congestion_feedback_frames_[0]; ASSERT_EQ(kFixRate, frame.type); EXPECT_EQ(static_cast<uint32>(0x04030201), - frame.fix_rate.bitrate_in_bytes_per_second); + frame.fix_rate.bitrate.ToBytesPerSecond()); // Now test framing boundaries for (size_t i = 0; i < 6; ++i) { @@ -1104,7 +1087,7 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_FALSE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error()); } @@ -1144,12 +1127,11 @@ TEST_F(QuicFramerTest, RstStreamFrame) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + 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()); - ASSERT_EQ(peer_address_, visitor_.peer_address_); EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.rst_stream_frame_.stream_id); EXPECT_EQ(0x05060708, visitor_.rst_stream_frame_.error_code); @@ -1217,7 +1199,7 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1266,7 +1248,7 @@ TEST_F(QuicFramerTest, PublicResetPacket) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); ASSERT_TRUE(visitor_.public_reset_packet_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), @@ -1317,7 +1299,7 @@ TEST_F(QuicFramerTest, FecPacket) { }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1495,7 +1477,7 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) { QuicCongestionFeedbackFrame congestion_feedback_frame; congestion_feedback_frame.type = kTCP; congestion_feedback_frame.tcp.accumulated_number_of_lost_packets = 0x0201; - congestion_feedback_frame.tcp.receive_window = 0x0403; + congestion_feedback_frame.tcp.receive_window = 0x4030; QuicFrames frames; frames.push_back(QuicFrame(&congestion_feedback_frame)); @@ -1611,8 +1593,8 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) { QuicCongestionFeedbackFrame congestion_feedback_frame; congestion_feedback_frame.type = kFixRate; - congestion_feedback_frame.fix_rate.bitrate_in_bytes_per_second - = 0x04030201; + congestion_feedback_frame.fix_rate.bitrate + = QuicBandwidth::FromBytesPerSecond(0x04030201); QuicFrames frames; frames.push_back(QuicFrame(&congestion_feedback_frame)); @@ -1927,6 +1909,7 @@ TEST_F(QuicFramerTest, DISABLED_Truncation) { close_frame.error_code = static_cast<QuicErrorCode>(0x05060708); close_frame.error_details = "because I can"; ack_frame->received_info.largest_observed = 201; + ack_frame->sent_info.least_unacked = 0; for (uint64 i = 1; i < ack_frame->received_info.largest_observed; ++i) { ack_frame->received_info.missing_packets.insert(i); } @@ -1959,11 +1942,10 @@ TEST_F(QuicFramerTest, DISABLED_Truncation) { framer_.EncryptPacket(*raw_close_packet)); // Now make sure we can turn our ack packet back into an ack frame - ASSERT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, *ack_packet)); + ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); // And do the same for the close frame. - ASSERT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, - *close_packet)); + ASSERT_TRUE(framer_.ProcessPacket(*close_packet)); } TEST_F(QuicFramerTest, CleanTruncation) { @@ -2012,11 +1994,10 @@ TEST_F(QuicFramerTest, CleanTruncation) { framer_.EncryptPacket(*raw_close_packet)); // Now make sure we can turn our ack packet back into an ack frame - ASSERT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, *ack_packet)); + ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); // And do the same for the close frame. - ASSERT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, - *close_packet)); + ASSERT_TRUE(framer_.ProcessPacket(*close_packet)); // Test for clean truncation of the ack by comparing the length of the // original packets to the re-serialized packets. diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc index 8ca25c0..5cdf5cd 100644 --- a/net/quic/quic_http_stream_test.cc +++ b/net/quic/quic_http_stream_test.cc @@ -11,6 +11,8 @@ #include "net/base/upload_bytes_element_reader.h" #include "net/base/upload_data_stream.h" #include "net/http/http_response_headers.h" +#include "net/quic/congestion_control/receive_algorithm_interface.h" +#include "net/quic/congestion_control/send_algorithm_interface.h" #include "net/quic/quic_client_session.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_connection_helper.h" @@ -42,20 +44,19 @@ class TestQuicConnection : public QuicConnection { : QuicConnection(guid, address, helper) { } - void SetScheduler(QuicSendScheduler* scheduler) { - QuicConnectionPeer::SetScheduler(this, scheduler); + void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) { + QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm); } - void SetCollector(QuicReceiptMetricsCollector* collector) { - QuicConnectionPeer::SetCollector(this, collector); + void SetReceiveAlgorithm(ReceiveAlgorithmInterface* receive_algorithm) { + QuicConnectionPeer::SetReceiveAlgorithm(this, receive_algorithm); } }; -class TestCollector : public QuicReceiptMetricsCollector { +class TestReceiveAlgorithm : public ReceiveAlgorithmInterface { public: - explicit TestCollector(QuicCongestionFeedbackFrame* feedback) - : QuicReceiptMetricsCollector(&clock_, kFixRate), - feedback_(feedback) { + explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback) + : feedback_(feedback) { } bool GenerateCongestionFeedback( @@ -68,13 +69,13 @@ class TestCollector : public QuicReceiptMetricsCollector { } MOCK_METHOD4(RecordIncomingPacket, - void(size_t, QuicPacketSequenceNumber, QuicTime, bool)); + void(QuicByteCount, QuicPacketSequenceNumber, QuicTime, bool)); private: MockClock clock_; QuicCongestionFeedbackFrame* feedback_; - DISALLOW_COPY_AND_ASSIGN(TestCollector); + DISALLOW_COPY_AND_ASSIGN(TestReceiveAlgorithm); }; // Subclass of QuicHttpStream that closes itself when the first piece of data @@ -162,16 +163,16 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { net_log_.net_log()); socket->Connect(peer_addr_); runner_ = new TestTaskRunner(&clock_); - scheduler_ = new MockScheduler(); - collector_ = new TestCollector(NULL); - EXPECT_CALL(*scheduler_, TimeUntilSend(_)). + send_algorithm_ = new MockSendAlgorithm(); + receive_algorithm_ = new TestReceiveAlgorithm(NULL); + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_)). WillRepeatedly(testing::Return(QuicTime::Delta::Zero())); helper_ = new QuicConnectionHelper(runner_.get(), &clock_, &random_generator_, socket); connection_ = new TestQuicConnection(guid_, peer_addr_, helper_); connection_->set_visitor(&visitor_); - connection_->SetScheduler(scheduler_); - connection_->SetCollector(collector_); + connection_->SetSendAlgorithm(send_algorithm_); + connection_->SetReceiveAlgorithm(receive_algorithm_); session_.reset(new QuicClientSession(connection_, helper_, NULL, "www.google.com")); CryptoHandshakeMessage message; @@ -255,8 +256,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { BoundNetLog net_log_; bool use_closing_stream_; - MockScheduler* scheduler_; - TestCollector* collector_; + MockSendAlgorithm* send_algorithm_; + TestReceiveAlgorithm* receive_algorithm_; scoped_refptr<TestTaskRunner> runner_; scoped_array<MockWrite> mock_writes_; MockClock clock_; diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc index 39d9f7e..e80ea85 100644 --- a/net/quic/quic_packet_creator.cc +++ b/net/quic/quic_packet_creator.cc @@ -19,7 +19,8 @@ QuicPacketCreator::QuicPacketCreator(QuicGuid guid, QuicFramer* framer) : guid_(guid), framer_(framer), sequence_number_(0), - fec_group_number_(0) { + fec_group_number_(0), + packet_size_(kPacketHeaderSize) { framer_->set_fec_builder(this); } @@ -34,16 +35,13 @@ void QuicPacketCreator::OnBuiltFecProtectedPayload( } bool QuicPacketCreator::ShouldSendFec(bool force_close) const { - DCHECK(options_.max_packets_per_fec_group == 0 || - (fec_group_.get() != NULL && fec_group_->GroupSize() > 0)); - return options_.max_packets_per_fec_group > 0 && - ((force_close && fec_group_->GroupSize() > 0) || - fec_group_->GroupSize() >= options_.max_packets_per_fec_group); + return fec_group_.get() != NULL && + (force_close || + fec_group_->NumReceivedPackets() >= options_.max_packets_per_fec_group); } void QuicPacketCreator::MaybeStartFEC() { - if (options_.max_packets_per_fec_group > 0) { - DCHECK(fec_group_.get() == NULL); + if (options_.max_packets_per_fec_group > 0 && fec_group_.get() == NULL) { // Set the fec group number to the sequence number of the next packet. fec_group_number_ = sequence_number() + 1; fec_group_.reset(new QuicFecGroup()); @@ -54,65 +52,84 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, StringPiece data, QuicStreamOffset offset, bool fin, - QuicFrames* frames) { + QuicFrame* frame) { DCHECK_GT(options_.max_packet_length, QuicUtils::StreamFramePacketOverhead(1)); + const size_t free_bytes = BytesFree(); + DCHECK_GE(free_bytes, kFrameTypeSize + kMinStreamFrameLength); - size_t unconsumed_bytes = data.size(); + size_t bytes_consumed = 0; if (data.size() != 0) { - size_t max_frame_len = framer_->GetMaxPlaintextSize( - options_.max_packet_length - - QuicUtils::StreamFramePacketOverhead(1)); - DCHECK_GT(max_frame_len, 0u); - size_t frame_len = min<size_t>(max_frame_len, unconsumed_bytes); - - if (unconsumed_bytes > 0) { - bool set_fin = false; - if (unconsumed_bytes <= frame_len) { // last frame. - frame_len = min(unconsumed_bytes, frame_len); - set_fin = fin; - } - StringPiece data_frame(data.data() + data.size() - unconsumed_bytes, - frame_len); - frames->push_back(QuicFrame(new QuicStreamFrame( - id, set_fin, offset, data_frame))); - - unconsumed_bytes -= frame_len; - } - // If we haven't finished serializing all the data, don't set any final fin. - if (unconsumed_bytes > 0) { - fin = false; - } + size_t max_data_len = free_bytes - kFrameTypeSize - kMinStreamFrameLength; + bytes_consumed = min<size_t>(max_data_len, data.size()); + + bool set_fin = fin && bytes_consumed == data.size(); // Last frame. + StringPiece data_frame(data.data(), bytes_consumed); + *frame = QuicFrame(new QuicStreamFrame(id, set_fin, offset, data_frame)); + } else { + DCHECK(fin); + // Create a new packet for the fin, if necessary. + *frame = QuicFrame(new QuicStreamFrame(id, true, offset, "")); } - // Create a new packet for the fin, if necessary. - if (fin && data.size() == 0) { - QuicStreamFrame* frame = new QuicStreamFrame(id, true, offset, ""); - frames->push_back(QuicFrame(frame)); + return bytes_consumed; +} + +PacketPair QuicPacketCreator::SerializeAllFrames(const QuicFrames& frames) { + DCHECK_EQ(0u, queued_frames_.size()); + for (size_t i = 0; i < frames.size(); ++i) { + bool success = AddFrame(frames[i]); + DCHECK(success); } + return SerializePacket(NULL); +} - return data.size() - unconsumed_bytes; +bool QuicPacketCreator::HasPendingFrames() { + return !queued_frames_.empty(); } -PacketPair QuicPacketCreator::SerializeAllFrames(const QuicFrames& frames) { - size_t num_serialized; - PacketPair pair = SerializeFrames(frames, &num_serialized); - DCHECK_EQ(frames.size(), num_serialized); - return pair; +size_t QuicPacketCreator::BytesFree() { + 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_; + } +} + +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; } -PacketPair QuicPacketCreator::SerializeFrames(const QuicFrames& frames, - size_t* num_serialized) { +PacketPair QuicPacketCreator::SerializePacket( + QuicFrames* retransmittable_frames) { + DCHECK_EQ(false, queued_frames_.empty()); QuicPacketHeader header; FillPacketHeader(fec_group_number_, PACKET_PRIVATE_FLAGS_NONE, &header); - QuicPacket* packet = framer_->ConstructMaxFrameDataPacket( - header, frames, num_serialized); + QuicPacket* packet = 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); } QuicPacketCreator::PacketPair QuicPacketCreator::SerializeFec() { - DCHECK_LT(0u, fec_group_->GroupSize()); + DCHECK_LT(0u, fec_group_->NumReceivedPackets()); + DCHECK_EQ(0u, queued_frames_.size()); QuicPacketHeader header; FillPacketHeader(fec_group_number_, PACKET_PRIVATE_FLAGS_FEC, &header); @@ -129,15 +146,9 @@ QuicPacketCreator::PacketPair QuicPacketCreator::SerializeFec() { QuicPacketCreator::PacketPair QuicPacketCreator::CloseConnection( QuicConnectionCloseFrame* close_frame) { - - QuicPacketHeader header; - FillPacketHeader(0, PACKET_PRIVATE_FLAGS_NONE, &header); - QuicFrames frames; frames.push_back(QuicFrame(close_frame)); - QuicPacket* packet = framer_->ConstructFrameDataPacket(header, frames); - DCHECK(packet); - return make_pair(header.packet_sequence_number, packet); + return SerializeAllFrames(frames); } void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group, @@ -150,4 +161,9 @@ void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group, header->fec_group = fec_group; } +bool QuicPacketCreator::ShouldRetransmit(const QuicFrame& frame) { + return frame.type != ACK_FRAME && frame.type != CONGESTION_FEEDBACK_FRAME && + frame.type != PADDING_FRAME; +} + } // namespace net diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h index f9836f3..b5eb9b8 100644 --- a/net/quic/quic_packet_creator.h +++ b/net/quic/quic_packet_creator.h @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// Some helpers for quic packet creation. +// Accumulates frames for the next packet until more frames no longer fit or +// it's time to create a packet from them. Also provides packet creation of +// FEC packets based on previously created packets. #ifndef NET_QUIC_QUIC_PACKET_CREATOR_H_ #define NET_QUIC_QUIC_PACKET_CREATOR_H_ @@ -20,6 +22,8 @@ namespace net { class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { public: + typedef std::pair<QuicPacketSequenceNumber, QuicPacket*> PacketPair; + // Options for controlling how packets are created. struct Options { Options() @@ -28,7 +32,6 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { max_packets_per_fec_group(0) { } - // TODO(alyssar, rch) max frames/packet size_t max_packet_length; bool random_reorder; // Inefficient: rewrite if used at scale. // 0 indicates fec is disabled. @@ -43,32 +46,41 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { virtual void OnBuiltFecProtectedPayload(const QuicPacketHeader& header, base::StringPiece payload) OVERRIDE; - typedef std::pair<QuicPacketSequenceNumber, QuicPacket*> PacketPair; - // Checks if it's time to send an FEC packet. |force_close| forces this to // return true if an fec group is open. bool ShouldSendFec(bool force_close) const; - // Starts a new FEC group with the next serialized packet, if FEC is enabled. + // Starts a new FEC group with the next serialized packet, if FEC is enabled + // and there is not already an FEC group open. void MaybeStartFEC(); - // Converts a raw payload to a frame. Returns the number of bytes consumed - // from data. If data is empty and fin is true, the expected behavior is to - // consume the fin but return 0. + // 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 + // fin but return 0. size_t CreateStreamFrame(QuicStreamId id, base::StringPiece data, QuicStreamOffset offset, bool fin, - QuicFrames* frames); + QuicFrame* frame); // Serializes all frames into a single packet. All frames must fit into a // single packet. PacketPair SerializeAllFrames(const QuicFrames& frames); - // Serializes as many non-fec frames as can fit into a single packet. - // num_serialized is set to the number of frames serialized into the packet. - PacketPair SerializeFrames(const QuicFrames& frames, - size_t* num_serialized); + // 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(); + + // 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); + + // 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(); @@ -90,6 +102,8 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { } private: + static bool ShouldRetransmit(const QuicFrame& frame); + void FillPacketHeader(QuicFecGroupNumber fec_group, QuicPacketPrivateFlags flags, QuicPacketHeader* header); @@ -100,7 +114,8 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { QuicPacketSequenceNumber sequence_number_; QuicFecGroupNumber fec_group_number_; scoped_ptr<QuicFecGroup> fec_group_; - + size_t packet_size_; + QuicFrames queued_frames_; }; } // namespace net diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc index d4bdb28..1c202d0 100644 --- a/net/quic/quic_packet_creator_test.cc +++ b/net/quic/quic_packet_creator_test.cc @@ -28,7 +28,7 @@ class QuicPacketCreatorTest : public ::testing::Test { sequence_number_(0), guid_(2), data_("foo"), - utils_(guid_, &framer_) { + creator_(guid_, &framer_) { framer_.set_visitor(&framer_visitor_); } ~QuicPacketCreatorTest() { @@ -39,7 +39,17 @@ class QuicPacketCreatorTest : public ::testing::Test { void ProcessPacket(QuicPacket* packet) { scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet)); - framer_.ProcessPacket(IPEndPoint(), IPEndPoint(), *encrypted); + framer_.ProcessPacket(*encrypted); + } + + void CheckStreamFrame(const QuicFrame& frame, QuicStreamId stream_id, + const string& data, QuicStreamOffset offset, bool fin) { + EXPECT_EQ(STREAM_FRAME, frame.type); + ASSERT_TRUE(frame.stream_frame); + EXPECT_EQ(stream_id, frame.stream_frame->stream_id); + EXPECT_EQ(data, frame.stream_frame->data); + EXPECT_EQ(offset, frame.stream_frame->offset); + EXPECT_EQ(fin, frame.stream_frame->fin); } QuicFrames frames_; @@ -49,17 +59,17 @@ class QuicPacketCreatorTest : public ::testing::Test { QuicPacketSequenceNumber sequence_number_; QuicGuid guid_; string data_; - QuicPacketCreator utils_; + QuicPacketCreator creator_; }; TEST_F(QuicPacketCreatorTest, SerializeFrame) { frames_.push_back(QuicFrame(new QuicStreamFrame( 0u, false, 0u, StringPiece("")))); - PacketPair pair = utils_.SerializeAllFrames(frames_); + PacketPair pair = creator_.SerializeAllFrames(frames_); { InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket(_, _)); + EXPECT_CALL(framer_visitor_, OnPacket()); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); EXPECT_CALL(framer_visitor_, OnPacketComplete()); @@ -74,11 +84,11 @@ TEST_F(QuicPacketCreatorTest, SerializeFrames) { 0u, false, 0u, StringPiece("")))); frames_.push_back(QuicFrame(new QuicStreamFrame( 0u, true, 0u, StringPiece("")))); - PacketPair pair = utils_.SerializeAllFrames(frames_); + PacketPair pair = creator_.SerializeAllFrames(frames_); { InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket(_, _)); + EXPECT_CALL(framer_visitor_, OnPacket()); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnAckFrame(_)); EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); @@ -90,16 +100,17 @@ TEST_F(QuicPacketCreatorTest, SerializeFrames) { } TEST_F(QuicPacketCreatorTest, SerializeWithFEC) { - utils_.options()->max_packets_per_fec_group = 6; - utils_.MaybeStartFEC(); + creator_.options()->max_packets_per_fec_group = 6; + ASSERT_FALSE(creator_.ShouldSendFec(false)); + creator_.MaybeStartFEC(); frames_.push_back(QuicFrame(new QuicStreamFrame( 0u, false, 0u, StringPiece("")))); - PacketPair pair = utils_.SerializeAllFrames(frames_); + PacketPair pair = creator_.SerializeAllFrames(frames_); { InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket(_, _)); + EXPECT_CALL(framer_visitor_, OnPacket()); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_)); EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); @@ -108,15 +119,15 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) { ProcessPacket(pair.second); delete pair.second; - ASSERT_FALSE(utils_.ShouldSendFec(false)); - ASSERT_TRUE(utils_.ShouldSendFec(true)); + ASSERT_FALSE(creator_.ShouldSendFec(false)); + ASSERT_TRUE(creator_.ShouldSendFec(true)); - pair = utils_.SerializeFec(); + pair = creator_.SerializeFec(); ASSERT_EQ(2u, pair.first); { InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket(_, _)); + EXPECT_CALL(framer_visitor_, OnPacket()); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnFecData(_)); EXPECT_CALL(framer_visitor_, OnPacketComplete()); @@ -130,12 +141,12 @@ TEST_F(QuicPacketCreatorTest, CloseConnection) { frame.error_code = QUIC_NO_ERROR; frame.ack_frame = QuicAckFrame(0u, 0u); - PacketPair pair = utils_.CloseConnection(&frame); + PacketPair pair = creator_.CloseConnection(&frame); ASSERT_EQ(1u, pair.first); - ASSERT_EQ(1u, utils_.sequence_number()); + ASSERT_EQ(1u, creator_.sequence_number()); InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket(_, _)); + EXPECT_CALL(framer_visitor_, OnPacket()); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnAckFrame(_)); EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_)); @@ -145,6 +156,86 @@ TEST_F(QuicPacketCreatorTest, CloseConnection) { delete pair.second; } +TEST_F(QuicPacketCreatorTest, CreateStreamFrame) { + QuicFrame frame; + size_t consumed = creator_.CreateStreamFrame(1u, "test", 0u, false, &frame); + EXPECT_EQ(4u, consumed); + CheckStreamFrame(frame, 1u, "test", 0u, false); + delete frame.stream_frame; +} + +TEST_F(QuicPacketCreatorTest, CreateStreamFrameFin) { + QuicFrame frame; + size_t consumed = creator_.CreateStreamFrame(1u, "test", 10u, true, &frame); + EXPECT_EQ(4u, consumed); + CheckStreamFrame(frame, 1u, "test", 10u, true); + delete frame.stream_frame; +} + +TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) { + QuicFrame frame; + size_t consumed = creator_.CreateStreamFrame(1u, "", 0u, true, &frame); + EXPECT_EQ(0u, consumed); + CheckStreamFrame(frame, 1u, "", 0u, true); + delete frame.stream_frame; +} + +TEST_F(QuicPacketCreatorTest, CreateStreamFrameTooLarge) { + // A string larger than fits into a frame. + size_t ciphertext_size = NullEncrypter().GetCiphertextSize(1); + creator_.options()->max_packet_length = + ciphertext_size + QuicUtils::StreamFramePacketOverhead(1); + QuicFrame frame; + size_t consumed = creator_.CreateStreamFrame(1u, "test", 0u, true, &frame); + EXPECT_EQ(1u, consumed); + CheckStreamFrame(frame, 1u, "t", 0u, false); + delete frame.stream_frame; +} + +TEST_F(QuicPacketCreatorTest, AddFrameAndSerialize) { + const size_t max_plaintext_size = + framer_.GetMaxPlaintextSize(creator_.options()->max_packet_length); + EXPECT_FALSE(creator_.HasPendingFrames()); + EXPECT_EQ(max_plaintext_size - kPacketHeaderSize, creator_.BytesFree()); + + // Add a variety of frame types and then a padding frame. + QuicAckFrame ack_frame; + EXPECT_TRUE(creator_.AddFrame(QuicFrame(&ack_frame))); + EXPECT_TRUE(creator_.HasPendingFrames()); + + QuicCongestionFeedbackFrame congestion_feedback; + congestion_feedback.type = kFixRate; + EXPECT_TRUE(creator_.AddFrame(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_.HasPendingFrames()); + + QuicPaddingFrame padding_frame; + EXPECT_TRUE(creator_.AddFrame(QuicFrame(&padding_frame))); + EXPECT_TRUE(creator_.HasPendingFrames()); + EXPECT_EQ(0u, creator_.BytesFree()); + + EXPECT_FALSE(creator_.AddFrame(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); + + EXPECT_FALSE(creator_.HasPendingFrames()); + EXPECT_EQ(max_plaintext_size - kPacketHeaderSize, creator_.BytesFree()); + + delete frame.stream_frame; +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index 90a5dab..14ad48b 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -121,7 +121,7 @@ ostream& operator<<(ostream& os, } case kFixRate: { os << " bitrate_in_bytes_per_second: " - << congestion_frame.fix_rate.bitrate_in_bytes_per_second; + << congestion_frame.fix_rate.bitrate.ToBytesPerSecond(); break; } case kTCP: { @@ -131,10 +131,6 @@ ostream& operator<<(ostream& os, os << " receive_window: " << tcp.receive_window; break; } - default: { - DLOG(FATAL) << "Unsupported congestion info type: " - << congestion_frame.type; - } } return os; } @@ -145,6 +141,10 @@ ostream& operator<<(ostream& os, const QuicAckFrame& ack_frame) { return os; } +CongestionFeedbackMessageFixRate::CongestionFeedbackMessageFixRate() + : bitrate(QuicBandwidth::Zero()) { +} + CongestionFeedbackMessageInterArrival:: CongestionFeedbackMessageInterArrival() {} diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index 9d641ee..a88d884 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -20,6 +20,7 @@ #include "base/string_piece.h" #include "net/base/int128.h" #include "net/base/net_export.h" +#include "net/quic/quic_bandwidth.h" #include "net/quic/quic_time.h" namespace net { @@ -36,7 +37,8 @@ typedef QuicPacketSequenceNumber QuicFecGroupNumber; typedef uint64 QuicPublicResetNonceProof; // TODO(rch): Consider Quic specific names for these constants. -const size_t kMaxPacketSize = 1200; // Maximum size in bytes of a QUIC packet. +// Maximum size in bytes of a QUIC packet. +const QuicByteCount kMaxPacketSize = 1200; // Maximum number of open streams per connection. const size_t kDefaultMaxStreamsPerConnection = 100; @@ -53,6 +55,8 @@ const size_t kPrivateFlagsSize = 1; const size_t kFecGroupSize = 1; // Number of bytes reserved for the nonce proof in public reset packet. const size_t kPublicResetNonceSize = 8; +// Number of bytes reserved for the frame type preceding each frame. +const size_t kFrameTypeSize = 1; // Size in bytes of the data or fec packet header. const size_t kPacketHeaderSize = kQuicGuidSize + kPublicFlagsSize + @@ -318,7 +322,7 @@ enum CongestionFeedbackType { struct NET_EXPORT_PRIVATE CongestionFeedbackMessageTCP { uint16 accumulated_number_of_lost_packets; - uint16 receive_window; // Number of bytes >> 4. + QuicByteCount receive_window; }; struct NET_EXPORT_PRIVATE CongestionFeedbackMessageInterArrival { @@ -331,7 +335,8 @@ struct NET_EXPORT_PRIVATE CongestionFeedbackMessageInterArrival { }; struct NET_EXPORT_PRIVATE CongestionFeedbackMessageFixRate { - uint32 bitrate_in_bytes_per_second; + CongestionFeedbackMessageFixRate(); + QuicBandwidth bitrate; }; struct NET_EXPORT_PRIVATE QuicCongestionFeedbackFrame { @@ -418,7 +423,6 @@ struct NET_EXPORT_PRIVATE QuicFecData { // FEC protected packet. The last protected packet's sequence number will // be one less than the sequence number of the FEC packet. QuicFecGroupNumber fec_group; - QuicPacketSequenceNumber min_protected_packet_sequence_number; // The last protected packet's sequence number will be one // less than the sequence number of the FEC packet. base::StringPiece redundancy; diff --git a/net/quic/quic_protocol_test.cc b/net/quic/quic_protocol_test.cc new file mode 100644 index 0000000..a77582f --- /dev/null +++ b/net/quic/quic_protocol_test.cc @@ -0,0 +1,53 @@ +// 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_protocol.h" + +#include "base/stl_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { +namespace { + +TEST(QuicProtocolTest, ReceivedInfo_RecordReceived) { + ReceivedPacketInfo received_info; + received_info.RecordReceived(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()); +} + +TEST(QuicProtocolTest, ReceivedInfo_ClearMissingBefore) { + 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()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index f1872d2..1c41ffd 100644 --- a/net/quic/quic_session.cc +++ b/net/quic/quic_session.cc @@ -184,7 +184,7 @@ void QuicSession::OnCryptoHandshakeComplete(QuicErrorCode error) { } void QuicSession::ActivateStream(ReliableQuicStream* stream) { - LOG(INFO) << "num_streams: " << stream_map_.size() + DLOG(INFO) << "num_streams: " << stream_map_.size() << ". activating " << stream->id(); DCHECK(stream_map_.count(stream->id()) == 0); stream_map_[stream->id()] = stream; diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h index c5c8b265..32bffc1 100644 --- a/net/quic/quic_session.h +++ b/net/quic/quic_session.h @@ -104,7 +104,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { virtual QuicCryptoStream* GetCryptoStream() = 0; // Adds 'stream' to the active stream map. - void ActivateStream(ReliableQuicStream* stream); + virtual void ActivateStream(ReliableQuicStream* stream); // Returns the stream id for a new stream. QuicStreamId GetNextStreamId(); diff --git a/net/quic/quic_time.h b/net/quic/quic_time.h index db95241..e780ca5 100644 --- a/net/quic/quic_time.h +++ b/net/quic/quic_time.h @@ -67,7 +67,7 @@ class NET_EXPORT_PRIVATE QuicTime { explicit QuicTime(base::TimeTicks ticks); // Creates a new QuicTime with an internal value of 0. IsInitialized() - // will return true for these times. + // will return false for these times. static QuicTime Zero(); // Create a new QuicTime holding the time_ms. diff --git a/net/quic/quic_time_test.cc b/net/quic/quic_time_test.cc index 811204bd..fc7d7e3 100644 --- a/net/quic/quic_time_test.cc +++ b/net/quic/quic_time_test.cc @@ -7,7 +7,7 @@ #include "testing/gtest/include/gtest/gtest.h" namespace net { -namespace testing { +namespace test { class QuicTimeDeltaTest : public ::testing::Test { protected: @@ -112,5 +112,5 @@ TEST_F(QuicTimeTest, MockClock) { EXPECT_EQ(now, time); } -} // namespace testing +} // namespace test } // namespace net diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index 5ae9c35..0cfac44 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -13,10 +13,11 @@ namespace net { size_t QuicUtils::StreamFramePacketOverhead(int num_frames) { // TODO(jar): Use sizeof(some name). return kPacketHeaderSize + - (1 + // 8 bit type + (kFrameTypeSize + kMinStreamFrameLength) * num_frames; } +// static uint128 QuicUtils::FNV1a_128_Hash(const char* data, int len) { // The following two constants are defined as part of the hash algorithm. // 309485009821345068724781371 @@ -41,6 +42,7 @@ uint128 QuicUtils::FNV1a_128_Hash(const char* data, int len) { case x: \ return #x; +// static const char* QuicUtils::ErrorToString(QuicErrorCode error) { switch (error) { RETURN_STRING_LITERAL(QUIC_NO_ERROR); diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc new file mode 100644 index 0000000..86323ac --- /dev/null +++ b/net/quic/reliable_quic_stream_test.cc @@ -0,0 +1,91 @@ +// 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/reliable_quic_stream.h" + +#include "net/quic/quic_connection.h" +#include "net/quic/quic_utils.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" + +using base::StringPiece; +using testing::_; +using testing::InSequence; +using testing::Return; +using testing::StrEq; + +namespace net { +namespace test { +namespace { + +const char kData1[] = "FooAndBar"; +const char kData2[] = "EepAndBaz"; +const size_t kDataLen = 9; + +class QuicReliableTestStream : public ReliableQuicStream { + public: + QuicReliableTestStream(QuicStreamId id, QuicSession* session) + : ReliableQuicStream(id, session) { + } + virtual uint32 ProcessData(const char* data, uint32 data_len) { + return 0; + } + using ReliableQuicStream::WriteData; +}; + +class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> { + public: + ReliableQuicStreamTest() + : connection_(new MockConnection(1, IPEndPoint())), + session_(connection_, true), + stream_(1, &session_) { + } + + MockConnection* connection_; + MockSession session_; + QuicReliableTestStream stream_; +}; + +TEST_F(ReliableQuicStreamTest, WriteAllData) { + connection_->options()->max_packet_length = + 1 + QuicUtils::StreamFramePacketOverhead(1); + // TODO(rch): figure out how to get StrEq working here. + //EXPECT_CALL(session_, WriteData(_, StrEq(kData1), _, _)).WillOnce( + EXPECT_CALL(session_, WriteData(1, _, _, _)).WillOnce( + Return(QuicConsumedData(kDataLen, true))); + EXPECT_EQ(kDataLen, stream_.WriteData(kData1, false).bytes_consumed); +} + +TEST_F(ReliableQuicStreamTest, WriteData) { + connection_->options()->max_packet_length = + 1 + QuicUtils::StreamFramePacketOverhead(1); + //EXPECT_CALL(session_, WriteData(_, StrEq(kData1), _, _)).WillOnce( + EXPECT_CALL(session_, WriteData(_, _, _, _)).WillOnce( + Return(QuicConsumedData(kDataLen - 1, false))); + // The return will be kDataLen, because the last byte gets buffered. + EXPECT_EQ(kDataLen, stream_.WriteData(kData1, false).bytes_consumed); + + // Queue a bytes_consumed write. + EXPECT_EQ(kDataLen, stream_.WriteData(kData2, false).bytes_consumed); + + // Make sure we get the tail of the first write followed by the bytes_consumed + InSequence s; + //EXPECT_CALL(session_, WriteData(_, StrEq(&kData2[kDataLen - 1]), _, _)). + EXPECT_CALL(session_, WriteData(_, _, _, _)). + WillOnce(Return(QuicConsumedData(1, false))); + //EXPECT_CALL(session_, WriteData(_, StrEq(kData2), _, _)). + EXPECT_CALL(session_, WriteData(_, _, _, _)). + WillOnce(Return(QuicConsumedData(kDataLen - 2, false))); + stream_.OnCanWrite(); + + // And finally the end of the bytes_consumed + //EXPECT_CALL(session_, WriteData(_, StrEq(&kData2[kDataLen - 2]), _, _)). + EXPECT_CALL(session_, WriteData(_, _, _, _)). + WillOnce(Return(QuicConsumedData(2, true))); + stream_.OnCanWrite(); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/net/quic/test_tools/quic_connection_peer.cc b/net/quic/test_tools/quic_connection_peer.cc index 1411e5b..7befa32 100644 --- a/net/quic/test_tools/quic_connection_peer.cc +++ b/net/quic/test_tools/quic_connection_peer.cc @@ -4,9 +4,10 @@ #include "net/quic/test_tools/quic_connection_peer.h" +#include "base/stl_util.h" #include "net/quic/congestion_control/quic_congestion_manager.h" -#include "net/quic/congestion_control/quic_receipt_metrics_collector.h" -#include "net/quic/congestion_control/quic_send_scheduler.h" +#include "net/quic/congestion_control/receive_algorithm_interface.h" +#include "net/quic/congestion_control/send_algorithm_interface.h" #include "net/quic/quic_connection.h" namespace net { @@ -18,15 +19,17 @@ void QuicConnectionPeer::SendAck(QuicConnection* connection) { } // static -void QuicConnectionPeer::SetCollector(QuicConnection* connection, - QuicReceiptMetricsCollector* collector) { - connection->congestion_manager_.collector_.reset(collector); +void QuicConnectionPeer::SetReceiveAlgorithm( + QuicConnection* connection, + ReceiveAlgorithmInterface* receive_algorithm) { + connection->congestion_manager_.receive_algorithm_.reset(receive_algorithm); } // static -void QuicConnectionPeer::SetScheduler(QuicConnection* connection, - QuicSendScheduler* scheduler) { - connection->congestion_manager_.scheduler_.reset(scheduler); +void QuicConnectionPeer::SetSendAlgorithm( + QuicConnection* connection, + SendAlgorithmInterface* send_algorithm) { + connection->congestion_manager_.send_algorithm_.reset(send_algorithm); } // static @@ -50,5 +53,27 @@ bool QuicConnectionPeer::GetReceivedTruncatedAck(QuicConnection* connection) { return connection->received_truncated_ack_; } +// static +size_t QuicConnectionPeer::GetNumRetransmissionTimeouts( + QuicConnection* connection) { + return connection->retransmission_timeouts_.size(); +} + +// static +bool QuicConnectionPeer::IsSavedForRetransmission( + QuicConnection* connection, + QuicPacketSequenceNumber sequence_number) { + return ContainsKey(connection->retransmission_map_, sequence_number); +} + +// static +size_t QuicConnectionPeer::GetRetransmissionCount( + QuicConnection* connection, + QuicPacketSequenceNumber sequence_number) { + QuicConnection::RetransmissionMap::iterator it = + connection->retransmission_map_.find(sequence_number); + return it->second.number_retransmissions; +} + } // 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 46b20929..deb313e0 100644 --- a/net/quic/test_tools/quic_connection_peer.h +++ b/net/quic/test_tools/quic_connection_peer.h @@ -6,6 +6,7 @@ #define NET_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_ #include "base/basictypes.h" +#include "net/quic/quic_protocol.h" namespace net { @@ -13,8 +14,8 @@ struct QuicAckFrame; class QuicConnection; class QuicConnectionVisitorInterface; class QuicPacketCreator; -class QuicReceiptMetricsCollector; -class QuicSendScheduler; +class ReceiveAlgorithmInterface; +class SendAlgorithmInterface; namespace test { @@ -23,11 +24,11 @@ class QuicConnectionPeer { public: static void SendAck(QuicConnection* connection); - static void SetCollector(QuicConnection* connection, - QuicReceiptMetricsCollector* collector); + static void SetReceiveAlgorithm(QuicConnection* connection, + ReceiveAlgorithmInterface* receive_algorithm); - static void SetScheduler(QuicConnection* connection, - QuicSendScheduler* scheduler); + static void SetSendAlgorithm(QuicConnection* connection, + SendAlgorithmInterface* send_algorithm); static QuicAckFrame* GetOutgoingAck(QuicConnection* connection); @@ -38,6 +39,16 @@ class QuicConnectionPeer { static bool GetReceivedTruncatedAck(QuicConnection* connection); + static size_t GetNumRetransmissionTimeouts(QuicConnection* connection); + + static bool IsSavedForRetransmission( + QuicConnection* connection, + QuicPacketSequenceNumber sequence_number); + + static size_t GetRetransmissionCount( + 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 634218f..c53f797 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -50,6 +50,17 @@ void FramerVisitorCapturingAcks::OnCongestionFeedbackFrame( feedback_.reset(new QuicCongestionFeedbackFrame(frame)); } +FramerVisitorCapturingPublicReset::FramerVisitorCapturingPublicReset() { +} + +FramerVisitorCapturingPublicReset::~FramerVisitorCapturingPublicReset() { +} + +void FramerVisitorCapturingPublicReset::OnPublicResetPacket( + const QuicPublicResetPacket& public_reset) { + public_reset_packet_ = public_reset; +} + MockConnectionVisitor::MockConnectionVisitor() { } @@ -94,11 +105,10 @@ PacketSavingConnection::~PacketSavingConnection() { STLDeleteElements(&packets_); } -bool PacketSavingConnection::SendPacket(QuicPacketSequenceNumber number, - QuicPacket* packet, - bool should_retransmit, - bool force, - bool is_retransmission) { +bool PacketSavingConnection::SendOrQueuePacket( + QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + bool force) { packets_.push_back(packet); return true; } @@ -112,11 +122,10 @@ MockSession::MockSession(QuicConnection* connection, bool is_server) MockSession::~MockSession() { } -MockScheduler::MockScheduler() - : QuicSendScheduler(NULL, kFixRate) { +MockSendAlgorithm::MockSendAlgorithm() { } -MockScheduler::~MockScheduler() { +MockSendAlgorithm::~MockSendAlgorithm() { } namespace { diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index 0b2a67a..a269693 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -7,7 +7,7 @@ #ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ #define NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ -#include "net/quic/congestion_control/quic_send_scheduler.h" +#include "net/quic/congestion_control/send_algorithm_interface.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_framer.h" #include "net/quic/quic_session.h" @@ -49,8 +49,7 @@ class MockFramerVisitor : public QuicFramerVisitorInterface { ~MockFramerVisitor(); MOCK_METHOD1(OnError, void(QuicFramer* framer)); - MOCK_METHOD2(OnPacket, void(const IPEndPoint& self_address, - const IPEndPoint& peer_address)); + MOCK_METHOD0(OnPacket, void()); MOCK_METHOD1(OnPublicResetPacket, void(const QuicPublicResetPacket& header)); MOCK_METHOD0(OnRevivedPacket, void()); // The constructor set this up to return true by default. @@ -75,8 +74,7 @@ class NoOpFramerVisitor : public QuicFramerVisitorInterface { NoOpFramerVisitor() {} virtual void OnError(QuicFramer* framer) OVERRIDE {} - virtual void OnPacket(const IPEndPoint& self_address, - const IPEndPoint& peer_address) OVERRIDE {} + virtual void OnPacket() OVERRIDE {} virtual void OnPublicResetPacket( const QuicPublicResetPacket& packet) OVERRIDE {} virtual void OnRevivedPacket() OVERRIDE {} @@ -120,6 +118,22 @@ class FramerVisitorCapturingAcks : public NoOpFramerVisitor { 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_; +}; + class MockConnectionVisitor : public QuicConnectionVisitorInterface { public: MockConnectionVisitor(); @@ -190,11 +204,9 @@ class PacketSavingConnection : public MockConnection { PacketSavingConnection(QuicGuid guid, IPEndPoint address); virtual ~PacketSavingConnection(); - virtual bool SendPacket(QuicPacketSequenceNumber number, - QuicPacket* packet, - bool should_retransmit, - bool force, - bool is_retransmission) OVERRIDE; + virtual bool SendOrQueuePacket(QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + bool force) OVERRIDE; std::vector<QuicPacket*> packets_; @@ -226,17 +238,22 @@ class MockSession : public QuicSession { DISALLOW_COPY_AND_ASSIGN(MockSession); }; -class MockScheduler : public QuicSendScheduler { +class MockSendAlgorithm : public SendAlgorithmInterface { public: - MockScheduler(); - virtual ~MockScheduler(); - + MockSendAlgorithm(); + virtual ~MockSendAlgorithm(); + + MOCK_METHOD2(OnIncomingQuicCongestionFeedbackFrame, + void(const QuicCongestionFeedbackFrame&, 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(OnIncomingAckFrame, void(const QuicAckFrame&)); - MOCK_METHOD3(SentPacket, void(QuicPacketSequenceNumber, size_t, bool)); + MOCK_METHOD0(BandwidthEstimate, QuicBandwidth(void)); private: - DISALLOW_COPY_AND_ASSIGN(MockScheduler); + DISALLOW_COPY_AND_ASSIGN(MockSendAlgorithm); }; } // namespace test |