From a97b318dfde98e65f5fb150de6b0ca1066119432 Mon Sep 17 00:00:00 2001 From: "rtenneti@chromium.org" Date: Fri, 8 Aug 2014 08:10:18 +0000 Subject: [PATCH] Land Recent QUIC Changes. QUIC: don't keep around state for streams created by a request w/no body. Waiting for a stream frame w/ FIN that will never arrive, and the closed stream map grows without bound. Not flag protected. Merge internal change: 72338396 https://codereview.chromium.org/447093002/ Default socket receive buffer to 256K bytes and remove unnecessary CongestionFeedbackFrames from tests. Merge internal change: 72338299 https://codereview.chromium.org/447083002/ Include the socket receive buffer in the connection handshake. PRESENCE_OPTIONAL The goal is to eventually remove the TcpCongestionFeedbackFrame. Currently this sends the same value for every packet. All it contains is the tcp_receive_window (now called socket_receive_buffer) which is fixed at 256000. So it can be sent in the connection establishment and then never retransmitted. Because only one type of CongestionFeedbackFrame can be sent with an ack, this frees up space for a TimestampFrame which is the motivation for removing the TcpCongestionFeedbackFrame. Merge internal change: 72334240 https://codereview.chromium.org/446283002/ Improve QUIC's SendAlgorithmSimulator to allow setting the delayed ack timer and add a new test of a second BBR flow starting after a first flow has stabilized. Merge internal change: 72332690 https://codereview.chromium.org/449713002/ QUIC congestion option that forces ICWND to 10. Not flag protected. Merge internal change: 72323017 https://codereview.chromium.org/446253002/ Allow receiving a QUIC CongestionFeedbackFrame that is not kTCP. Currently the only congestion feedback frames that are sent are TCP, but I want to send timestamp packets in the next version, so the send algorithms should not assume that the incoming congestion feedback frame is kTCP. Merge internal change: 72322097 https://codereview.chromium.org/443313002/ QUIC - minor cleanup to match internal source tree. * Formatting cleanup and .get() != NULL changes. Merge internal change: 72204223 https://codereview.chromium.org/447973004/ Bring back InterArrivalReceiver and rename it to TimestampeReceiver. This is not in production. Merge internal change: 72170964 https://codereview.chromium.org/449693002/ Move Quic AppendTimestampFrame method out of AppendCongestionFrame. Also, remove obsolete TODO's and rename InterArrival to Timestamp. Not used in production. Merge internal change: 72148809 https://codereview.chromium.org/446063005/ R=rch@chromium.org Review URL: https://codereview.chromium.org/447093004 Cr-Commit-Position: refs/heads/master@{#288247} git-svn-id: svn://svn.chromium.org/chrome/trunk/src@288247 0039d316-1c4b-4281-b951-d872f2087c98 --- net/net.gypi | 3 + .../congestion_control/hybrid_slow_start_test.cc | 2 +- .../receive_algorithm_interface.cc | 6 +- .../congestion_control/send_algorithm_simulator.cc | 125 +++++++++++++-------- .../congestion_control/send_algorithm_simulator.h | 17 +++ net/quic/congestion_control/tcp_cubic_sender.cc | 28 +++-- .../congestion_control/tcp_cubic_sender_test.cc | 64 ++--------- net/quic/congestion_control/timestamp_receiver.cc | 43 +++++++ net/quic/congestion_control/timestamp_receiver.h | 37 ++++++ .../congestion_control/timestamp_receiver_test.cc | 55 +++++++++ net/quic/crypto/crypto_protocol.h | 6 +- net/quic/quic_config.cc | 24 +++- net/quic/quic_config.h | 12 ++ net/quic/quic_config_test.cc | 19 +++- net/quic/quic_connection.cc | 2 +- net/quic/quic_connection_logger.cc | 8 +- net/quic/quic_framer.cc | 121 ++++++++++---------- net/quic/quic_framer.h | 2 + net/quic/quic_framer_test.cc | 24 ++-- net/quic/quic_protocol.cc | 16 ++- net/quic/quic_protocol.h | 15 ++- net/quic/reliable_quic_stream.cc | 1 + net/quic/test_tools/mock_crypto_client_stream.cc | 2 +- net/quic/test_tools/quic_session_peer.cc | 6 + net/quic/test_tools/quic_session_peer.h | 2 + net/tools/quic/end_to_end_test.cc | 21 ++++ 26 files changed, 447 insertions(+), 214 deletions(-) create mode 100644 net/quic/congestion_control/timestamp_receiver.cc create mode 100644 net/quic/congestion_control/timestamp_receiver.h create mode 100644 net/quic/congestion_control/timestamp_receiver_test.cc diff --git a/net/net.gypi b/net/net.gypi index bb9f016adf6b..97159cb9d572 100644 --- a/net/net.gypi +++ b/net/net.gypi @@ -759,6 +759,8 @@ 'quic/congestion_control/tcp_receiver.h', 'quic/congestion_control/time_loss_algorithm.cc', 'quic/congestion_control/time_loss_algorithm.h', + 'quic/congestion_control/timestamp_receiver.cc', + 'quic/congestion_control/timestamp_receiver.h', 'quic/crypto/aead_base_decrypter.h', 'quic/crypto/aead_base_decrypter_nss.cc', 'quic/crypto/aead_base_decrypter_openssl.cc', @@ -1436,6 +1438,7 @@ 'quic/congestion_control/tcp_loss_algorithm_test.cc', 'quic/congestion_control/tcp_receiver_test.cc', 'quic/congestion_control/time_loss_algorithm_test.cc', + 'quic/congestion_control/timestamp_receiver_test.cc', 'quic/crypto/aes_128_gcm_12_decrypter_test.cc', 'quic/crypto/aes_128_gcm_12_encrypter_test.cc', 'quic/crypto/cert_compressor_test.cc', diff --git a/net/quic/congestion_control/hybrid_slow_start_test.cc b/net/quic/congestion_control/hybrid_slow_start_test.cc index 91c5d9f2d652..bea5840ab793 100644 --- a/net/quic/congestion_control/hybrid_slow_start_test.cc +++ b/net/quic/congestion_control/hybrid_slow_start_test.cc @@ -53,7 +53,7 @@ TEST_F(HybridSlowStartTest, Simple) { // TODO(ianswett): Add tests which more realistically invoke the methods, // simulating how actual acks arrive and packets are sent. TEST_F(HybridSlowStartTest, AckTrain) { - // At a typical RTT 60 ms, assuming that the inter arrival is 1 ms, + // At a typical RTT 60 ms, assuming that the inter arrival timestamp is 1 ms, // we expect to be able to send a burst of 30 packet before we trigger the // ack train detection. const int kMaxLoopCount = 5; diff --git a/net/quic/congestion_control/receive_algorithm_interface.cc b/net/quic/congestion_control/receive_algorithm_interface.cc index 8d1a39ce745f..4f608580c7f9 100644 --- a/net/quic/congestion_control/receive_algorithm_interface.cc +++ b/net/quic/congestion_control/receive_algorithm_interface.cc @@ -5,6 +5,7 @@ #include "net/quic/congestion_control/receive_algorithm_interface.h" #include "net/quic/congestion_control/tcp_receiver.h" +#include "net/quic/congestion_control/timestamp_receiver.h" namespace net { @@ -14,9 +15,8 @@ ReceiveAlgorithmInterface* ReceiveAlgorithmInterface::Create( switch (type) { case kTCP: return new TcpReceiver(); - case kInterArrival: - LOG(DFATAL) << "InterArrivalSendAlgorithm no longer supported."; - return NULL; + case kTimestamp: + return new TimestampReceiver(); } return NULL; } diff --git a/net/quic/congestion_control/send_algorithm_simulator.cc b/net/quic/congestion_control/send_algorithm_simulator.cc index 36aa32163d93..7af949ad2eb3 100644 --- a/net/quic/congestion_control/send_algorithm_simulator.cc +++ b/net/quic/congestion_control/send_algorithm_simulator.cc @@ -48,7 +48,8 @@ SendAlgorithmSimulator::SendAlgorithmSimulator( loss_correlation_(0), bandwidth_(bandwidth), rtt_(rtt), - buffer_size_(1000000) { + buffer_size_(1000000), + delayed_ack_timer_(QuicTime::Delta::FromMilliseconds(100)) { uint32 seed = base::RandInt(0, std::numeric_limits::max()); DVLOG(1) << "Seeding SendAlgorithmSimulator with " << seed; simple_random_.set_seed(seed); @@ -92,7 +93,8 @@ void SendAlgorithmSimulator::TransferBytes(QuicByteCount max_bytes, clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); SendDataNow(&pending_transfers_.front()); } else if (ack_event.time_delta < send_event.time_delta) { - DVLOG(1) << "Handling ack, advancing time:" + DVLOG(1) << "Handling ack of largest observed:" + << ack_event.transfer->sender->next_acked << ", advancing time:" << ack_event.time_delta.ToMicroseconds() << "us"; // Ack data all the data up to ack time and lose any missing sequence // numbers. @@ -165,57 +167,78 @@ QuicTime::Delta SendAlgorithmSimulator::FindNextAcked(Transfer* transfer) { lose_next_ack_ = reverse_loss_rate_ * kuint64max > simple_random_.RandUint64(); } - bool two_acks_remaining = lose_next_ack_; - sender->next_acked = sender->last_acked; - bool packets_lost = false; + + QuicPacketSequenceNumber next_acked = sender->last_acked; + QuicTime::Delta next_ack_delay = + FindNextAck(transfer, sender->last_acked, &next_acked); + if (lose_next_ack_) { + next_ack_delay = FindNextAck(transfer, next_acked, &next_acked); + } + sender->next_acked = next_acked; + return next_ack_delay; +} + +QuicTime::Delta SendAlgorithmSimulator::FindNextAck( + const Transfer* transfer, + QuicPacketSequenceNumber last_acked, + QuicPacketSequenceNumber* next_acked) const { + *next_acked = last_acked; + QuicTime::Delta ack_delay = QuicTime::Delta::Infinite(); // Remove any packets that are simulated as lost. for (list::const_iterator it = sent_packets_.begin(); it != sent_packets_.end(); ++it) { if (transfer != it->transfer) { continue; } - + // Skip over any packets less than or equal to last_acked. + if (it->sequence_number <= last_acked) { + continue; + } // Lost packets don't trigger an ack. if (it->ack_time == QuicTime::Zero()) { - packets_lost = true; continue; } - // Buffer dropped packets are skipped automatically, but still end up - // being lost and cause acks to be sent immediately. - if (sender->next_acked < it->sequence_number - 1) { - packets_lost = true; + DCHECK_LT(*next_acked, it->sequence_number); + // Consider a delayed ack for the current next_acked. + if (ack_delay < it->ack_time.Subtract(clock_->Now())) { + break; } - DCHECK_LT(sender->next_acked, it->sequence_number); - sender->next_acked = it->sequence_number; - if (packets_lost || (sender->next_acked - sender->last_acked) % 2 == 0) { - if (two_acks_remaining) { - two_acks_remaining = false; - } else { - break; - } + *next_acked = it->sequence_number; + ack_delay = it->ack_time.Subtract(clock_->Now()); + if (HasRecentLostPackets(transfer, *next_acked) || + (*next_acked - last_acked) >= 2) { + break; } - } - // If the connection has no packets to be acked, return Infinite. - if (sender->next_acked == sender->last_acked) { - return QuicTime::Delta::Infinite(); + ack_delay = ack_delay.Add(delayed_ack_timer_); } - QuicTime::Delta ack_time = QuicTime::Delta::Infinite(); - for (list::const_iterator it = sent_packets_.begin(); - it != sent_packets_.end(); ++it) { - if (transfer == it->transfer && sender->next_acked == it->sequence_number) { - ack_time = it->ack_time.Subtract(clock_->Now()); - } - } - // If only one packet is acked, simulate a delayed ack. - if (!ack_time.IsInfinite() && transfer->bytes_in_flight == kPacketSize) { - ack_time = ack_time.Add(QuicTime::Delta::FromMilliseconds(100)); - } DVLOG(1) << "FindNextAcked found next_acked_:" << transfer->sender->next_acked << " last_acked:" << transfer->sender->last_acked - << " ack_time(ms):" << ack_time.ToMilliseconds(); - return ack_time; + << " ack_delay(ms):" << ack_delay.ToMilliseconds(); + return ack_delay; +} + +bool SendAlgorithmSimulator::HasRecentLostPackets( + const Transfer* transfer, QuicPacketSequenceNumber next_acked) const { + QuicPacketSequenceNumber last_packet = transfer->sender->last_acked; + for (list::const_iterator it = sent_packets_.begin(); + it != sent_packets_.end() && it->sequence_number < next_acked; ++it) { + if (transfer != it->transfer) { + continue; + } + // Lost packets don't trigger an ack. + if (it->ack_time == QuicTime::Zero()) { + return true; + } + // Buffer dropped packets are skipped automatically, but still end up + // being lost and cause acks to be sent immediately. + if (it->sequence_number > last_packet + 1) { + return true; + } + last_packet = it->sequence_number; + } + return false; } void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) { @@ -223,6 +246,10 @@ void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) { DCHECK_LT(sender->last_acked, sender->next_acked); SendAlgorithmInterface::CongestionMap acked_packets; SendAlgorithmInterface::CongestionMap lost_packets; + DVLOG(1) << "Acking packets from:" << sender->last_acked + << " to " << sender->next_acked + << " bytes_in_flight:" << transfer->bytes_in_flight + << " Now():" << (clock_->Now().ToDebuggingValue() / 1000) << "ms"; // Some entries may be missing from the sent_packets_ array, if they were // dropped due to buffer overruns. SentPacket largest_observed(0, QuicTime::Zero(), QuicTime::Zero(), NULL); @@ -258,10 +285,12 @@ void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) { DVLOG(1) << "Updating RTT from send_time:" << largest_observed.send_time.ToDebuggingValue() << " to ack_time:" << largest_observed.ack_time.ToDebuggingValue(); - sender->rtt_stats->UpdateRtt( - largest_observed.ack_time.Subtract(largest_observed.send_time), - QuicTime::Delta::Zero(), - clock_->Now()); + QuicTime::Delta measured_rtt = + largest_observed.ack_time.Subtract(largest_observed.send_time); + DCHECK_GE(measured_rtt.ToMicroseconds(), rtt_.ToMicroseconds()); + sender->rtt_stats->UpdateRtt(measured_rtt, + QuicTime::Delta::Zero(), + clock_->Now()); sender->send_algorithm->OnCongestionEvent( true, transfer->bytes_in_flight, acked_packets, lost_packets); DCHECK_LE(kPacketSize * (acked_packets.size() + lost_packets.size()), @@ -295,7 +324,8 @@ void SendAlgorithmSimulator::SendDataNow(Transfer* transfer) { Sender* sender = transfer->sender; ++sender->last_sent; DVLOG(1) << "Sending packet:" << sender->last_sent - << " bytes_in_flight:" << transfer->bytes_in_flight; + << " bytes_in_flight:" << transfer->bytes_in_flight + << " Now():" << (clock_->Now().ToDebuggingValue() / 1000) << "ms"; sender->send_algorithm->OnPacketSent( clock_->Now(), transfer->bytes_in_flight, sender->last_sent, kPacketSize, HAS_RETRANSMITTABLE_DATA); @@ -314,17 +344,16 @@ void SendAlgorithmSimulator::SendDataNow(Transfer* transfer) { DVLOG(1) << "losing packet:" << sender->last_sent << " due to random loss."; - QuicTime ack_time = clock_->Now().Add(rtt_); // If the number of bytes in flight are less than the bdp, there's // no buffering delay. Bytes lost from the buffer are not counted. QuicByteCount bdp = bandwidth_.ToBytesPerPeriod(rtt_); - if ((sent_packets_.size() + 1) * kPacketSize > bdp) { - QuicByteCount qsize = (sent_packets_.size() + 1) * kPacketSize - bdp; - ack_time = ack_time.Add(bandwidth_.TransferTime(qsize)); - DVLOG(1) << "Increasing transfer time:" - << bandwidth_.TransferTime(qsize).ToMilliseconds() - << "ms due to qsize:" << qsize; + QuicTime ack_time = clock_->Now().Add(rtt_); + if (kPacketSize > bdp) { + ack_time = ack_time.Add(bandwidth_.TransferTime(kPacketSize - bdp)); } + QuicTime queue_ack_time = sent_packets_.empty() ? QuicTime::Zero() : + sent_packets_.back().ack_time.Add(bandwidth_.TransferTime(kPacketSize)); + ack_time = QuicTime::Max(ack_time, queue_ack_time); // If the packet is lost, give it an ack time of Zero. sent_packets_.push_back(SentPacket( sender->last_sent, clock_->Now(), diff --git a/net/quic/congestion_control/send_algorithm_simulator.h b/net/quic/congestion_control/send_algorithm_simulator.h index 4ecb83338fdb..fb6404926a6c 100644 --- a/net/quic/congestion_control/send_algorithm_simulator.h +++ b/net/quic/congestion_control/send_algorithm_simulator.h @@ -133,6 +133,10 @@ class SendAlgorithmSimulator { buffer_size_ = buffer_size_bytes; } + void set_delayed_ack_timer(QuicTime::Delta delayed_ack_timer) { + delayed_ack_timer_ = delayed_ack_timer; + } + // Advance the time by |delta| without sending anything. void AdvanceTime(QuicTime::Delta delta); @@ -173,6 +177,18 @@ class SendAlgorithmSimulator { // Sets the next acked. QuicTime::Delta FindNextAcked(Transfer* transfer); + // Sets the |next_acked| packet for the |transfer| starting at the specified + // |last_acked|. Returns QuicTime::Delta::Infinite and doesn't set + // |next_acked| if there is no ack after |last_acked|. + QuicTime::Delta FindNextAck(const Transfer* transfer, + QuicPacketSequenceNumber last_acked, + QuicPacketSequenceNumber* next_acked) const; + + // Returns true if any of the packets |transfer| is waiting for less than + // next_acked have been lost. + bool HasRecentLostPackets(const Transfer* transfer, + QuicPacketSequenceNumber next_acked) const; + // Process all the acks that should have arrived by the current time, and // lose any packets that are missing. Returns the number of bytes acked. void HandlePendingAck(Transfer* transfer); @@ -196,6 +212,7 @@ class SendAlgorithmSimulator { QuicBandwidth bandwidth_; QuicTime::Delta rtt_; size_t buffer_size_; // In bytes. + QuicTime::Delta delayed_ack_timer_; DISALLOW_COPY_AND_ASSIGN(SendAlgorithmSimulator); }; diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc index dd7e7aee5c31..d96770466788 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/net/quic/congestion_control/tcp_cubic_sender.cc @@ -8,6 +8,7 @@ #include "base/metrics/histogram.h" #include "net/quic/congestion_control/rtt_stats.h" +#include "net/quic/crypto/crypto_protocol.h" using std::max; using std::min; @@ -20,7 +21,6 @@ namespace { // fast retransmission. The cwnd after a timeout is still 1. const QuicTcpCongestionWindow kMinimumCongestionWindow = 2; const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS; -const QuicByteCount kDefaultReceiveWindow = 64000; const int64 kInitialCongestionWindow = 10; const int kMaxBurstLength = 3; }; // namespace @@ -37,7 +37,7 @@ TcpCubicSender::TcpCubicSender( stats_(stats), reno_(reno), congestion_window_count_(0), - receive_window_(kDefaultReceiveWindow), + receive_window_(kDefaultSocketReceiveBuffer), prr_out_(0), prr_delivered_(0), ack_count_since_loss_(0), @@ -58,17 +58,31 @@ TcpCubicSender::~TcpCubicSender() { } void TcpCubicSender::SetFromConfig(const QuicConfig& config, bool is_server) { - if (is_server && config.HasReceivedInitialCongestionWindow()) { - // Set the initial window size. - congestion_window_ = min(kMaxInitialWindow, - config.ReceivedInitialCongestionWindow()); + if (is_server) { + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW10)) { + // Initial window experiment. Ignore the initial congestion + // window suggested by the client and use the default ICWND of + // 10 instead. + congestion_window_ = kInitialCongestionWindow; + } else if (config.HasReceivedInitialCongestionWindow()) { + // Set the initial window size. + congestion_window_ = min(kMaxInitialWindow, + config.ReceivedInitialCongestionWindow()); + } + } + if (config.HasReceivedSocketReceiveBuffer()) { + // Set the initial socket receive buffer size in bytes. + receive_window_ = config.ReceivedSocketReceiveBuffer(); } } void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time) { - receive_window_ = feedback.tcp.receive_window; + if (feedback.type == kTCP) { + receive_window_ = feedback.tcp.receive_window; + } } void TcpCubicSender::OnCongestionEvent( diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc index ef2e1c0a2cf0..cc52da6c7d24 100644 --- a/net/quic/congestion_control/tcp_cubic_sender_test.cc +++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -9,6 +9,7 @@ #include "net/quic/congestion_control/rtt_stats.h" #include "net/quic/congestion_control/tcp_cubic_sender.h" #include "net/quic/congestion_control/tcp_receiver.h" +#include "net/quic/crypto/crypto_protocol.h" #include "net/quic/quic_utils.h" #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/quic_config_peer.h" @@ -130,16 +131,12 @@ class TcpCubicSenderTest : public ::testing::Test { }; TEST_F(TcpCubicSenderTest, SimpleSender) { - QuicCongestionFeedbackFrame feedback; // At startup make sure we are at the default. EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); // At startup make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA).IsZero()); - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, @@ -157,14 +154,10 @@ TEST_F(TcpCubicSenderTest, SimpleSender) { TEST_F(TcpCubicSenderTest, ApplicationLimitedSlowStart) { // Send exactly 10 packets and ensure the CWND ends at 14 packets. const int kNumberOfAcks = 5; - QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA).IsZero()); - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, @@ -183,14 +176,10 @@ TEST_F(TcpCubicSenderTest, ApplicationLimitedSlowStart) { TEST_F(TcpCubicSenderTest, ExponentialSlowStart) { const int kNumberOfAcks = 20; - QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA).IsZero()); - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, @@ -216,11 +205,6 @@ TEST_F(TcpCubicSenderTest, SlowStartAckTrain) { // Since we start at 10 packet first round will be 5 second round 10 etc // Hence we should pass 30 at 65 = 5 + 10 + 20 + 30 const int kNumberOfAcks = 65; - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. SendAvailableSendWindow(); @@ -262,12 +246,6 @@ TEST_F(TcpCubicSenderTest, SlowStartAckTrain) { } TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) { - // Make sure that we fall out of slow start when we encounter a packet loss. - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - const int kNumberOfAcks = 10; for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. @@ -319,12 +297,6 @@ TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) { TEST_F(TcpCubicSenderTest, SlowStartPacketLossPRR) { // Test based on the first example in RFC6937. - // Make sure that we fall out of slow start when we encounter a packet loss. - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example. const int kNumberOfAcks = 5; for (int i = 0; i < kNumberOfAcks; ++i) { @@ -375,12 +347,6 @@ TEST_F(TcpCubicSenderTest, SlowStartBurstPacketLossPRR) { // Test based on the second example in RFC6937, though we also implement // forward acknowledgements, so the first two incoming acks will trigger // PRR immediately. - // Make sure that we fall out of slow start when we encounter a packet loss. - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example. const int kNumberOfAcks = 5; for (int i = 0; i < kNumberOfAcks; ++i) { @@ -506,11 +472,6 @@ TEST_F(TcpCubicSenderTest, SlowStartMaxSendWindow) { sender_.reset( new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP)); - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. SendAvailableSendWindow(); @@ -527,11 +488,6 @@ TEST_F(TcpCubicSenderTest, TcpRenoMaxCongestionWindow) { sender_.reset( new TcpCubicSenderPeer(&clock_, true, kMaxCongestionWindowTCP)); - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - SendAvailableSendWindow(); AckNPackets(2); // Make sure we fall out of slow start. @@ -556,11 +512,6 @@ TEST_F(TcpCubicSenderTest, TcpCubicMaxCongestionWindow) { sender_.reset( new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP)); - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - SendAvailableSendWindow(); AckNPackets(2); // Make sure we fall out of slow start. @@ -612,14 +563,17 @@ TEST_F(TcpCubicSenderTest, ConfigureMaxInitialWindow) { sender_->SetFromConfig(config, true); EXPECT_EQ(2 * congestion_window, sender_->congestion_window()); + + // Verify that kCOPT: kIW10 forces the congestion window to the + // default of 10 regardless of ReceivedInitialWindow. + QuicTagVector options; + options.push_back(kIW10); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, true); + EXPECT_EQ(congestion_window, sender_->congestion_window()); } TEST_F(TcpCubicSenderTest, CongestionAvoidanceAtEndOfRecovery) { - // Make sure that we fall out of slow start when we encounter a packet loss. - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); // Ack 10 packets in 5 acks to raise the CWND to 20. const int kNumberOfAcks = 5; for (int i = 0; i < kNumberOfAcks; ++i) { diff --git a/net/quic/congestion_control/timestamp_receiver.cc b/net/quic/congestion_control/timestamp_receiver.cc new file mode 100644 index 000000000000..ad4407c1ad23 --- /dev/null +++ b/net/quic/congestion_control/timestamp_receiver.cc @@ -0,0 +1,43 @@ +// Copyright 2014 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/timestamp_receiver.h" + +#include + +#include "base/basictypes.h" + +namespace net { + +TimestampReceiver::TimestampReceiver() { +} + +TimestampReceiver::~TimestampReceiver() { +} + +bool TimestampReceiver::GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* feedback) { + if (received_packet_times_.size() <= 1) { + // Don't waste resources by sending a feedback frame for only one packet. + return false; + } + feedback->type = kTimestamp; + + // Copy our current receive set to our feedback message, we will not resend + // this data if it is lost. + feedback->timestamp.received_packet_times = received_packet_times_; + + // Prepare for the next set of arriving packets by clearing our current set. + received_packet_times_.clear(); + return true; +} + +void TimestampReceiver::RecordIncomingPacket( + QuicByteCount /*bytes*/, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp) { + received_packet_times_.insert(std::make_pair(sequence_number, timestamp)); +} + +} // namespace net diff --git a/net/quic/congestion_control/timestamp_receiver.h b/net/quic/congestion_control/timestamp_receiver.h new file mode 100644 index 000000000000..3eb2a7c461e6 --- /dev/null +++ b/net/quic/congestion_control/timestamp_receiver.h @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CONGESTION_CONTROL_TIMESTAMP_RECEIVER_H_ +#define NET_QUIC_CONGESTION_CONTROL_TIMESTAMP_RECEIVER_H_ + +#include "base/basictypes.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 TimestampReceiver : public ReceiveAlgorithmInterface { + public: + TimestampReceiver(); + virtual ~TimestampReceiver(); + + virtual bool GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* feedback) OVERRIDE; + + virtual void RecordIncomingPacket(QuicByteCount bytes, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp) OVERRIDE; + + private: + // The set of received packets since the last feedback was sent, along with + // their arrival times. + TimeMap received_packet_times_; + + DISALLOW_COPY_AND_ASSIGN(TimestampReceiver); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_TIMESTAMP_RECEIVER_H_ diff --git a/net/quic/congestion_control/timestamp_receiver_test.cc b/net/quic/congestion_control/timestamp_receiver_test.cc new file mode 100644 index 000000000000..2c477ea887b5 --- /dev/null +++ b/net/quic/congestion_control/timestamp_receiver_test.cc @@ -0,0 +1,55 @@ +// Copyright 2014 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 "net/quic/congestion_control/timestamp_receiver.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class TimestampReceiverTest : public ::testing::Test { + protected: + TimestampReceiver receiver_; + MockClock clock_; +}; + +TEST_F(TimestampReceiverTest, SimpleReceiver) { + QuicTime start = clock_.ApproximateNow(); + QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10); + clock_.AdvanceTime(received_delta); + QuicTime receive_timestamp = clock_.ApproximateNow(); + receiver_.RecordIncomingPacket(1, 1, receive_timestamp); + + QuicCongestionFeedbackFrame feedback; + // Do not generate a congestion feedback frame because it has only one packet. + ASSERT_FALSE(receiver_.GenerateCongestionFeedback(&feedback)); + + clock_.AdvanceTime(received_delta); + receive_timestamp = clock_.ApproximateNow(); + // Packet not received; but rather revived by FEC. + receiver_.RecordIncomingPacket(1, 2, receive_timestamp); + clock_.AdvanceTime(received_delta); + receive_timestamp = clock_.ApproximateNow(); + receiver_.RecordIncomingPacket(1, 3, receive_timestamp); + + ASSERT_TRUE(receiver_.GenerateCongestionFeedback(&feedback)); + + EXPECT_EQ(kTimestamp, feedback.type); + EXPECT_EQ(3u, feedback.timestamp.received_packet_times.size()); + TimeMap::iterator it = feedback.timestamp.received_packet_times.begin(); + EXPECT_EQ(1u, it->first); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), it->second.Subtract(start)); + it = feedback.timestamp.received_packet_times.begin(); + ++it; + EXPECT_EQ(2u, it->first); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), it->second.Subtract(start)); + ++it; + EXPECT_EQ(3u, it->first); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(30), it->second.Subtract(start)); +} + +} // namespace test +} // namespace net diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index 577913789dc7..878b8eefb2b8 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -44,14 +44,18 @@ const QuicTag kNULL = TAG('N', 'U', 'L', 'N'); // null algorithm const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12 const QuicTag kCC12 = TAG('C', 'C', '1', '2'); // ChaCha20 + Poly1305 +// Socket receive buffer +const QuicTag kSRBF = TAG('S', 'R', 'B', 'F'); // Socket receive buffer + // Congestion control feedback types const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic const QuicTag kPACE = TAG('P', 'A', 'C', 'E'); // Paced TCP cubic -const QuicTag kINAR = TAG('I', 'N', 'A', 'R'); // Inter arrival +const QuicTag kTSTP = TAG('T', 'S', 'T', 'P'); // Timestamp // Congestion control options const QuicTag kTBBR = TAG('T', 'B', 'B', 'R'); // Reduced Buffer Bloat TCP const QuicTag kRENO = TAG('R', 'E', 'N', 'O'); // Reno Congestion Control +const QuicTag kIW10 = TAG('I', 'W', '1', '0'); // Force ICWND to 10 // Loss detection algorithm types const QuicTag kNACK = TAG('N', 'A', 'C', 'K'); // TCP style nack counting diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc index 90579d3a03f3..c68eac980be6 100644 --- a/net/quic/quic_config.cc +++ b/net/quic/quic_config.cc @@ -443,7 +443,8 @@ QuicConfig::QuicConfig() initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL), // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring // QUIC_VERSION_19. - initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL) { + initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL), + socket_receive_buffer_(kSRBF, PRESENCE_OPTIONAL) { } QuicConfig::~QuicConfig() {} @@ -617,6 +618,22 @@ uint32 QuicConfig::ReceivedInitialSessionFlowControlWindowBytes() const { return initial_session_flow_control_window_bytes_.GetReceivedValue(); } +void QuicConfig::SetSocketReceiveBufferToSend(uint32 tcp_receive_window) { + socket_receive_buffer_.SetSendValue(tcp_receive_window); +} + +uint32 QuicConfig::GetSocketReceiveBufferToSend() const { + return socket_receive_buffer_.GetSendValue(); +} + +bool QuicConfig::HasReceivedSocketReceiveBuffer() const { + return socket_receive_buffer_.HasReceivedValue(); +} + +uint32 QuicConfig::ReceivedSocketReceiveBuffer() const { + return socket_receive_buffer_.GetReceivedValue(); +} + bool QuicConfig::negotiated() { // TODO(ianswett): Add the negotiated parameters once and iterate over all // of them in negotiated, ToHandshakeMessage, ProcessClientHello, and @@ -668,6 +685,7 @@ void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { initial_flow_control_window_bytes_.ToHandshakeMessage(out); initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out); initial_session_flow_control_window_bytes_.ToHandshakeMessage(out); + socket_receive_buffer_.ToHandshakeMessage(out); connection_options_.ToHandshakeMessage(out); } @@ -715,6 +733,10 @@ QuicErrorCode QuicConfig::ProcessPeerHello( peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { + error = socket_receive_buffer_.ProcessPeerHello( + peer_hello, hello_type, error_details); + } + if (error == QUIC_NO_ERROR) { error = loss_detection_.ProcessPeerHello( peer_hello, hello_type, error_details); } diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h index 376b2b05896f..3c5f5cdc16e7 100644 --- a/net/quic/quic_config.h +++ b/net/quic/quic_config.h @@ -340,6 +340,15 @@ class NET_EXPORT_PRIVATE QuicConfig { uint32 ReceivedInitialSessionFlowControlWindowBytes() const; + // Sets socket receive buffer to transmit to the peer. + void SetSocketReceiveBufferToSend(uint32 window_bytes); + + uint32 GetSocketReceiveBufferToSend() const; + + bool HasReceivedSocketReceiveBuffer() const; + + uint32 ReceivedSocketReceiveBuffer() const; + bool negotiated(); // SetDefaults sets the members to sensible, default values. @@ -389,6 +398,9 @@ class NET_EXPORT_PRIVATE QuicConfig { QuicFixedUint32 initial_stream_flow_control_window_bytes_; // Initial session flow control receive window in bytes. QuicFixedUint32 initial_session_flow_control_window_bytes_; + + // Socket receive buffer in bytes. + QuicFixedUint32 socket_receive_buffer_; }; } // namespace net diff --git a/net/quic/quic_config_test.cc b/net/quic/quic_config_test.cc index 5241fed7e6f2..86f435f83b0f 100644 --- a/net/quic/quic_config_test.cc +++ b/net/quic/quic_config_test.cc @@ -42,6 +42,7 @@ TEST_F(QuicConfigTest, ToHandshakeMessage) { config_.set_idle_connection_state_lifetime(QuicTime::Delta::FromSeconds(5), QuicTime::Delta::FromSeconds(2)); config_.set_max_streams_per_connection(4, 2); + config_.SetSocketReceiveBufferToSend(kDefaultSocketReceiveBuffer); CryptoHandshakeMessage msg; config_.ToHandshakeMessage(&msg); @@ -66,6 +67,10 @@ TEST_F(QuicConfigTest, ToHandshakeMessage) { EXPECT_EQ(QUIC_NO_ERROR, error); EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value); + error = msg.GetUint32(kSRBF, &value); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_EQ(kDefaultSocketReceiveBuffer, value); + const QuicTag* out; size_t out_len; error = msg.GetTaglist(kCGST, &out, &out_len); @@ -91,7 +96,7 @@ TEST_F(QuicConfigTest, ToHandshakeMessageWithPacing) { TEST_F(QuicConfigTest, ProcessClientHello) { QuicConfig client_config; QuicTagVector cgst; - cgst.push_back(kINAR); + cgst.push_back(kTSTP); cgst.push_back(kQBIC); client_config.set_congestion_feedback(cgst, kQBIC); client_config.set_idle_connection_state_lifetime( @@ -107,6 +112,7 @@ TEST_F(QuicConfigTest, ProcessClientHello) { 2 * kInitialStreamFlowControlWindowForTest); client_config.SetInitialSessionFlowControlWindowToSend( 2 * kInitialSessionFlowControlWindowForTest); + client_config.SetSocketReceiveBufferToSend(kDefaultSocketReceiveBuffer); QuicTagVector copt; copt.push_back(kTBBR); copt.push_back(kFHDR); @@ -137,6 +143,8 @@ TEST_F(QuicConfigTest, ProcessClientHello) { 2 * kInitialStreamFlowControlWindowForTest); EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(), 2 * kInitialSessionFlowControlWindowForTest); + EXPECT_EQ(config_.ReceivedSocketReceiveBuffer(), + kDefaultSocketReceiveBuffer); } TEST_F(QuicConfigTest, ProcessServerHello) { @@ -159,6 +167,7 @@ TEST_F(QuicConfigTest, ProcessServerHello) { 2 * kInitialStreamFlowControlWindowForTest); server_config.SetInitialSessionFlowControlWindowToSend( 2 * kInitialSessionFlowControlWindowForTest); + server_config.SetSocketReceiveBufferToSend(kDefaultSocketReceiveBuffer); CryptoHandshakeMessage msg; server_config.ToHandshakeMessage(&msg); string error_details; @@ -183,6 +192,8 @@ TEST_F(QuicConfigTest, ProcessServerHello) { 2 * kInitialStreamFlowControlWindowForTest); EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(), 2 * kInitialSessionFlowControlWindowForTest); + EXPECT_EQ(config_.ReceivedSocketReceiveBuffer(), + kDefaultSocketReceiveBuffer); } TEST_F(QuicConfigTest, MissingOptionalValuesInCHLO) { @@ -261,7 +272,7 @@ TEST_F(QuicConfigTest, MultipleNegotiatedValuesInVectorTag) { QuicConfig server_config; QuicTagVector cgst; cgst.push_back(kQBIC); - cgst.push_back(kINAR); + cgst.push_back(kTSTP); server_config.set_congestion_feedback(cgst, kQBIC); CryptoHandshakeMessage msg; @@ -276,8 +287,8 @@ TEST_F(QuicConfigTest, NoOverLapInCGST) { QuicConfig server_config; server_config.SetDefaults(); QuicTagVector cgst; - cgst.push_back(kINAR); - server_config.set_congestion_feedback(cgst, kINAR); + cgst.push_back(kTSTP); + server_config.set_congestion_feedback(cgst, kTSTP); CryptoHandshakeMessage msg; string error_details; diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 9594fdd6982b..405ba4225541 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -1695,7 +1695,7 @@ void QuicConnection::MaybeProcessUndecryptablePackets() { // new keys installed and hence any undecryptable packets will // never be able to be decrypted. if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) { - if (debug_visitor_ != NULL) { + if (debug_visitor_.get() != NULL) { for (size_t i = 0; i < undecryptable_packets_.size(); ++i) { debug_visitor_->OnUndecryptablePacket(); } diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc index 8f2163bc6a66..6f7757e12d4e 100644 --- a/net/quic/quic_connection_logger.cc +++ b/net/quic/quic_connection_logger.cc @@ -122,13 +122,13 @@ base::Value* NetLogQuicCongestionFeedbackFrameCallback( NetLog::LogLevel /* log_level */) { base::DictionaryValue* dict = new base::DictionaryValue(); switch (frame->type) { - case kInterArrival: { - dict->SetString("type", "InterArrival"); + case kTimestamp: { + dict->SetString("type", "Timestamp"); base::ListValue* received = new base::ListValue(); dict->Set("received_packets", received); for (TimeMap::const_iterator it = - frame->inter_arrival.received_packet_times.begin(); - it != frame->inter_arrival.received_packet_times.end(); ++it) { + frame->timestamp.received_packet_times.begin(); + it != frame->timestamp.received_packet_times.end(); ++it) { string value = base::Uint64ToString(it->first) + "@" + base::Uint64ToString(it->second.ToDebuggingValue()); received->AppendString(value); diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 2ef5c7fbdf1d..027392c2cbdb 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -983,7 +983,8 @@ QuicFramer::AckFrameInfo QuicFramer::GetAckFrameInfo( last_missing = *iter; } // Include the last nack range. - ack_info.nack_ranges[last_missing - cur_range_length] = cur_range_length; + ack_info.nack_ranges[last_missing - cur_range_length] = + cur_range_length; // Include the range to the largest observed. ack_info.max_delta = max(ack_info.max_delta, frame.largest_observed - last_missing); @@ -1417,9 +1418,8 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( static_cast(feedback_type); switch (frame->type) { - case kInterArrival: { - CongestionFeedbackMessageInterArrival* inter_arrival = - &frame->inter_arrival; + case kTimestamp: { + CongestionFeedbackMessageTimestamp* timestamp = &frame->timestamp; uint8 num_received_packets; if (!reader_->ReadBytes(&num_received_packets, 1)) { set_detailed_error("Unable to read num received packets."); @@ -1442,7 +1442,7 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( QuicTime time_received = creation_time_.Add( QuicTime::Delta::FromMicroseconds(time_received_us)); - inter_arrival->received_packet_times.insert( + timestamp->received_packet_times.insert( make_pair(smallest_received, time_received)); for (uint8 i = 0; i < num_received_packets - 1; ++i) { @@ -1460,7 +1460,7 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( return false; } QuicPacketSequenceNumber packet = smallest_received + sequence_delta; - inter_arrival->received_packet_times.insert( + timestamp->received_packet_times.insert( make_pair(packet, time_received.Add( QuicTime::Delta::FromMicroseconds(time_delta_us)))); } @@ -1469,7 +1469,6 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( } case kTCP: { CongestionFeedbackMessageTCP* tcp = &frame->tcp; - // TODO(ianswett): Remove receive window, since it's constant. uint16 receive_window = 0; if (!reader_->ReadUInt16(&receive_window)) { set_detailed_error("Unable to read receive window."); @@ -1788,16 +1787,16 @@ size_t QuicFramer::ComputeFrameLength( len += 1; // Congestion feedback type. switch (congestion_feedback.type) { - case kInterArrival: { - const CongestionFeedbackMessageInterArrival& inter_arrival = - congestion_feedback.inter_arrival; + case kTimestamp: { + const CongestionFeedbackMessageTimestamp& timestamp = + congestion_feedback.timestamp; len += 1; // Number received packets. - if (inter_arrival.received_packet_times.size() > 0) { + if (!timestamp.received_packet_times.empty()) { len += PACKET_6BYTE_SEQUENCE_NUMBER; // Smallest received. len += 8; // Time. // 2 bytes per sequence number delta plus 4 bytes per delta time. len += PACKET_6BYTE_SEQUENCE_NUMBER * - (inter_arrival.received_packet_times.size() - 1); + (timestamp.received_packet_times.size() - 1); } break; } @@ -2094,55 +2093,8 @@ bool QuicFramer::AppendCongestionFeedbackFrame( } switch (frame.type) { - case kInterArrival: { - const CongestionFeedbackMessageInterArrival& inter_arrival = - frame.inter_arrival; - DCHECK_GE(numeric_limits::max(), - inter_arrival.received_packet_times.size()); - if (inter_arrival.received_packet_times.size() > - numeric_limits::max()) { - return false; - } - // TODO(ianswett): Make num_received_packets a varint. - uint8 num_received_packets = - inter_arrival.received_packet_times.size(); - if (!writer->WriteBytes(&num_received_packets, 1)) { - return false; - } - if (num_received_packets > 0) { - TimeMap::const_iterator it = - inter_arrival.received_packet_times.begin(); - - QuicPacketSequenceNumber lowest_sequence = it->first; - if (!AppendPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, - lowest_sequence, writer)) { - return false; - } - - QuicTime lowest_time = it->second; - if (!writer->WriteUInt64( - lowest_time.Subtract(creation_time_).ToMicroseconds())) { - return false; - } - - for (++it; it != inter_arrival.received_packet_times.end(); ++it) { - QuicPacketSequenceNumber sequence_delta = it->first - lowest_sequence; - DCHECK_GE(numeric_limits::max(), sequence_delta); - if (sequence_delta > numeric_limits::max()) { - return false; - } - if (!writer->WriteUInt16(static_cast(sequence_delta))) { - return false; - } - - int32 time_delta_us = - it->second.Subtract(lowest_time).ToMicroseconds(); - if (!writer->WriteBytes(&time_delta_us, sizeof(time_delta_us))) { - return false; - } - } - } - break; + case kTimestamp: { + return AppendTimestampFrame(frame, writer); } case kTCP: { const CongestionFeedbackMessageTCP& tcp = frame.tcp; @@ -2161,6 +2113,53 @@ bool QuicFramer::AppendCongestionFeedbackFrame( return true; } +bool QuicFramer::AppendTimestampFrame( + const QuicCongestionFeedbackFrame& frame, + QuicDataWriter* writer) { + const CongestionFeedbackMessageTimestamp& timestamp = frame.timestamp; + DCHECK_GE(numeric_limits::max(), + timestamp.received_packet_times.size()); + if (timestamp.received_packet_times.size() > numeric_limits::max()) { + return false; + } + uint8 num_received_packets = timestamp.received_packet_times.size(); + if (!writer->WriteBytes(&num_received_packets, 1)) { + return false; + } + if (num_received_packets > 0) { + TimeMap::const_iterator it = timestamp.received_packet_times.begin(); + + QuicPacketSequenceNumber lowest_sequence = it->first; + if (!AppendPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + lowest_sequence, writer)) { + return false; + } + + QuicTime lowest_time = it->second; + if (!writer->WriteUInt64( + lowest_time.Subtract(creation_time_).ToMicroseconds())) { + return false; + } + + for (++it; it != timestamp.received_packet_times.end(); ++it) { + QuicPacketSequenceNumber sequence_delta = it->first - lowest_sequence; + DCHECK_GE(numeric_limits::max(), sequence_delta); + if (sequence_delta > numeric_limits::max()) { + return false; + } + if (!writer->WriteUInt16(static_cast(sequence_delta))) { + return false; + } + + int32 time_delta_us = it->second.Subtract(lowest_time).ToMicroseconds(); + if (!writer->WriteBytes(&time_delta_us, sizeof(time_delta_us))) { + return false; + } + } + } + return true; +} + bool QuicFramer::AppendStopWaitingFrame( const QuicPacketHeader& header, const QuicStopWaitingFrame& frame, diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index 1349f9360c6c..41ed0b86e568 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -463,6 +463,8 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicDataWriter* builder); bool AppendCongestionFeedbackFrame(const QuicCongestionFeedbackFrame& frame, QuicDataWriter* builder); + bool AppendTimestampFrame(const QuicCongestionFeedbackFrame& frame, + QuicDataWriter* builder); bool AppendStopWaitingFrame(const QuicPacketHeader& header, const QuicStopWaitingFrame& frame, QuicDataWriter* builder); diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index f8d1c5be27ec..9b9401b19b09 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -2045,7 +2045,7 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) { } } -TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) { +TEST_P(QuicFramerTest, CongestionFeedbackFrameTimestamp) { unsigned char packet[] = { // public flags (8 byte connection_id) 0x3C, @@ -2060,7 +2060,7 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) { // frame type (congestion feedback frame) 0x20, - // congestion feedback type (inter arrival) + // congestion feedback type (timestamp) 0x01, // num received packets 0x03, @@ -2091,10 +2091,10 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) { ASSERT_EQ(1u, visitor_.congestion_feedback_frames_.size()); const QuicCongestionFeedbackFrame& frame = *visitor_.congestion_feedback_frames_[0]; - ASSERT_EQ(kInterArrival, frame.type); - ASSERT_EQ(3u, frame.inter_arrival.received_packet_times.size()); + ASSERT_EQ(kTimestamp, frame.type); + ASSERT_EQ(3u, frame.timestamp.received_packet_times.size()); TimeMap::const_iterator iter = - frame.inter_arrival.received_packet_times.begin(); + frame.timestamp.received_packet_times.begin(); EXPECT_EQ(GG_UINT64_C(0x0123456789ABA), iter->first); EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59687), iter->second.Subtract(start_).ToMicroseconds()); @@ -3429,7 +3429,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { +TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTimestamp) { QuicPacketHeader header; header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -3440,16 +3440,16 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { header.fec_group = 0; QuicCongestionFeedbackFrame frame; - frame.type = kInterArrival; - frame.inter_arrival.received_packet_times.insert( + frame.type = kTimestamp; + frame.timestamp.received_packet_times.insert( make_pair(GG_UINT64_C(0x0123456789ABA), start_.Add(QuicTime::Delta::FromMicroseconds( GG_UINT64_C(0x07E1D2C3B4A59687))))); - frame.inter_arrival.received_packet_times.insert( + frame.timestamp.received_packet_times.insert( make_pair(GG_UINT64_C(0x0123456789ABB), start_.Add(QuicTime::Delta::FromMicroseconds( GG_UINT64_C(0x07E1D2C3B4A59688))))); - frame.inter_arrival.received_packet_times.insert( + frame.timestamp.received_packet_times.insert( make_pair(GG_UINT64_C(0x0123456789ABD), start_.Add(QuicTime::Delta::FromMicroseconds( GG_UINT64_C(0x07E1D2C3B4A59689))))); @@ -3470,7 +3470,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { // frame type (congestion feedback frame) 0x20, - // congestion feedback type (inter arrival) + // congestion feedback type (timestamp) 0x01, // num received packets 0x03, @@ -3556,7 +3556,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) { QuicCongestionFeedbackFrame congestion_feedback_frame; congestion_feedback_frame.type = - static_cast(kInterArrival + 1); + static_cast(kTimestamp + 1); QuicFrames frames; frames.push_back(QuicFrame(&congestion_feedback_frame)); diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index a1975022bb09..c587c56f8dd2 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -271,11 +271,10 @@ CongestionFeedbackMessageTCP::CongestionFeedbackMessageTCP() : receive_window(0) { } -CongestionFeedbackMessageInterArrival::CongestionFeedbackMessageInterArrival() { +CongestionFeedbackMessageTimestamp::CongestionFeedbackMessageTimestamp() { } -CongestionFeedbackMessageInterArrival:: - ~CongestionFeedbackMessageInterArrival() {} +CongestionFeedbackMessageTimestamp::~CongestionFeedbackMessageTimestamp() {} QuicCongestionFeedbackFrame::QuicCongestionFeedbackFrame() : type(kTCP) {} @@ -502,13 +501,12 @@ ostream& operator<<(ostream& os, const QuicCongestionFeedbackFrame& congestion_frame) { os << "type: " << congestion_frame.type; switch (congestion_frame.type) { - case kInterArrival: { - const CongestionFeedbackMessageInterArrival& inter_arrival = - congestion_frame.inter_arrival; + case kTimestamp: { + const CongestionFeedbackMessageTimestamp& timestamp = + congestion_frame.timestamp; os << " received packets: [ "; - for (TimeMap::const_iterator it = - inter_arrival.received_packet_times.begin(); - it != inter_arrival.received_packet_times.end(); ++it) { + for (TimeMap::const_iterator it = timestamp.received_packet_times.begin(); + it != timestamp.received_packet_times.end(); ++it) { os << it->first << "@" << it->second.ToDebuggingValue() << " "; } os << "]"; diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index ec94e4c7e57c..d95df71baf49 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -69,6 +69,9 @@ const uint32 kDefaultFlowControlSendWindow = 16 * 1024; // 16 KB // algorithms. const size_t kMaxTcpCongestionWindow = 200; +// Size of the socket receive buffer in bytes. +const QuicByteCount kDefaultSocketReceiveBuffer = 256000; + // Don't allow a client to suggest an RTT longer than 15 seconds. const uint32 kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond; @@ -700,7 +703,7 @@ void NET_EXPORT_PRIVATE InsertMissingPacketsBetween( // compatibility. enum CongestionFeedbackType { kTCP, // Used to mimic TCP. - kInterArrival, // Use additional inter arrival information. + kTimestamp, // Use additional inter arrival timestamp information. }; // Defines for all types of congestion control algorithms that can be used in @@ -724,9 +727,9 @@ struct NET_EXPORT_PRIVATE CongestionFeedbackMessageTCP { QuicByteCount receive_window; }; -struct NET_EXPORT_PRIVATE CongestionFeedbackMessageInterArrival { - CongestionFeedbackMessageInterArrival(); - ~CongestionFeedbackMessageInterArrival(); +struct NET_EXPORT_PRIVATE CongestionFeedbackMessageTimestamp { + CongestionFeedbackMessageTimestamp(); + ~CongestionFeedbackMessageTimestamp(); // The set of received packets since the last feedback was sent, along with // their arrival times. @@ -741,10 +744,10 @@ struct NET_EXPORT_PRIVATE QuicCongestionFeedbackFrame { std::ostream& os, const QuicCongestionFeedbackFrame& c); CongestionFeedbackType type; - // This should really be a union, but since the inter arrival struct + // This should really be a union, but since the timestamp struct // is non-trivial, C++ prohibits it. CongestionFeedbackMessageTCP tcp; - CongestionFeedbackMessageInterArrival inter_arrival; + CongestionFeedbackMessageTimestamp timestamp; }; struct NET_EXPORT_PRIVATE QuicRstStreamFrame { diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc index 212b69868c7a..dbb5d440c219 100644 --- a/net/quic/reliable_quic_stream.cc +++ b/net/quic/reliable_quic_stream.cc @@ -234,6 +234,7 @@ void ReliableQuicStream::OnConnectionClosed(QuicErrorCode error, void ReliableQuicStream::OnFinRead() { DCHECK(sequencer_.IsClosed()); + fin_received_ = true; CloseReadSide(); } diff --git a/net/quic/test_tools/mock_crypto_client_stream.cc b/net/quic/test_tools/mock_crypto_client_stream.cc index a4b3ab0289d6..4d4057a4b019 100644 --- a/net/quic/test_tools/mock_crypto_client_stream.cc +++ b/net/quic/test_tools/mock_crypto_client_stream.cc @@ -81,7 +81,7 @@ void MockCryptoClientStream::SendOnCryptoHandshakeEvent( void MockCryptoClientStream::SetConfigNegotiated() { ASSERT_FALSE(session()->config()->negotiated()); QuicTagVector cgst; - cgst.push_back(kINAR); + cgst.push_back(kTSTP); cgst.push_back(kQBIC); session()->config()->set_congestion_feedback(cgst, kQBIC); session()->config()->set_idle_connection_state_lifetime( diff --git a/net/quic/test_tools/quic_session_peer.cc b/net/quic/test_tools/quic_session_peer.cc index f8024fe0ae2c..14897b26fcbd 100644 --- a/net/quic/test_tools/quic_session_peer.cc +++ b/net/quic/test_tools/quic_session_peer.cc @@ -50,5 +50,11 @@ QuicDataStream* QuicSessionPeer::GetIncomingDataStream( return session->GetIncomingDataStream(stream_id); } +// static +map& +QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(QuicSession* session) { + return session->locally_closed_streams_highest_offset_; +} + } // namespace test } // namespace net diff --git a/net/quic/test_tools/quic_session_peer.h b/net/quic/test_tools/quic_session_peer.h index 02146caf1adb..e69f6ba9c64a 100644 --- a/net/quic/test_tools/quic_session_peer.h +++ b/net/quic/test_tools/quic_session_peer.h @@ -28,6 +28,8 @@ class QuicSessionPeer { static QuicWriteBlockedList* GetWriteBlockedStreams(QuicSession* session); static QuicDataStream* GetIncomingDataStream(QuicSession* session, QuicStreamId stream_id); + static std::map& + GetLocallyClosedStreamsHighestOffset(QuicSession* session); private: DISALLOW_COPY_AND_ASSIGN(QuicSessionPeer); diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc index 19985a80faab..1243e34dde23 100644 --- a/net/tools/quic/end_to_end_test.cc +++ b/net/tools/quic/end_to_end_test.cc @@ -1366,6 +1366,27 @@ TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) { server_thread_->Resume(); } +TEST_P(EndToEndTest, RequestWithNoBodyWillNeverSendStreamFrameWithFIN) { + // Regression test for b/16010251. + // A stream created on receipt of a simple request with no body will never get + // a stream frame with a FIN. Verify that we don't keep track of the stream in + // the locally closed streams map: it will never be removed if so. + ASSERT_TRUE(Initialize()); + + // Send a simple headers only request, and receive response. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); + + // Now verify that the server is not waiting for a final FIN or RST. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + QuicSession* session = dispatcher->session_map().begin()->second; + EXPECT_EQ(0u, QuicSessionPeer::GetLocallyClosedStreamsHighestOffset( + session).size()); + server_thread_->Resume(); +} + } // namespace } // namespace test } // namespace tools -- 2.11.4.GIT